Netcheck performs network connectivity checks and exposes metrics. Security should focus on container isolation, network egress restrictions, metrics endpoint protection, and credential handling for any authenticated targets.
| Aspect | Risk Level | Mitigation |
|---|---|---|
| Metrics endpoint exposure | Medium | Bind to localhost, use reverse proxy with TLS |
| Container escape | Low | Run as non-root, drop capabilities, read-only filesystem |
| Network egress | Low | Limit outbound to specific targets |
| Credential handling | Medium | Use secrets management, avoid hardcoded credentials |
| Log data | Low | Encrypt logs containing network topology info |
services:
netcheck:
image: ghcr.io/m1/netcheck:latest
# Run as non-root user
user: "1000:1000"
# Read-only root filesystem
read_only: true
# Temporary writable directories
tmpfs:
- /tmp:noexec,nosuid,size=64m
# Drop all Linux capabilities
cap_drop:
- ALL
# Prevent privilege escalation
security_opt:
- no-new-privileges:true
# Optional: AppArmor profile
security_opt:
- apparmor:docker-default
# Optional: Seccomp profile
security_opt:
- seccomp:unconfined
# Resource limits
deploy:
resources:
limits:
cpus: '0.5'
memory: 64M
reservations:
cpus: '0.1'
memory: 16M
# Network restrictions
networks:
- monitoring
| Setting | Purpose | Impact |
|---|---|---|
user: "1000:1000" |
Run as non-root user | Prevents root access inside container |
read_only: true |
Read-only root filesystem | Prevents malicious writes |
tmpfs |
Writable temp in memory | Allows necessary writes without disk access |
cap_drop: ALL |
Drop all capabilities | Removes privileged operations |
no-new-privileges |
Prevent privilege escalation | Blocks setuid/setgid exploits |
apparmor |
Mandatory access control | Additional kernel-level restrictions |
deploy.resources |
CPU/memory limits | Prevents resource exhaustion |
Never expose the metrics endpoint directly to the internet:
ports:
- "127.0.0.1:8080:8080" # Localhost only
Configure Nginx or Apache as a reverse proxy:
server {
listen 443 ssl http2;
server_name netcheck.example.com;
# TLS configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
# Rate limiting
limit_req_zone $binary_remote_addr zone=netcheck_limit:10m rate=10r/s;
limit_req zone=netcheck_limit burst=20 nodelay;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_read_timeout 30s;
proxy_connect_timeout 5s;
}
# Protect metrics endpoint
location /metrics {
# Basic authentication
auth_basic "Netcheck Metrics";
auth_basic_user_file /etc/nginx/.htpasswd;
# IP whitelist (optional)
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
proxy_pass http://127.0.0.1:8080/metrics;
}
}
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH
sudo ufw allow ssh
# Allow HTTPS (if using reverse proxy)
sudo ufw allow 443/tcp
# Enable firewall
sudo ufw --force enable
# Check status
sudo ufw status verbose
# Default zone
sudo firewall-cmd --set-default-zone=public
# Allow services
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=https
# Block direct access to netcheck port
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port port="8080" protocol="tcp" reject'
# Reload
sudo firewall-cmd --reload
Limit outbound connections to approved targets only:
# Docker Compose with egress restrictions
services:
netcheck:
# ... other settings ...
# Restrict outbound traffic (requires Docker 20.10+)
sysctls:
- net.ipv4.conf.all.route_localnet=0
# Use network policies (Kubernetes)
# Or Docker network with limited routing
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: netcheck-egress
namespace: monitoring
spec:
podSelector:
matchLabels:
app: netcheck
policyTypes:
- Egress
egress:
# Allow DNS resolution
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
# Allow specific external targets
- to:
- ipBlock:
cidr: 1.1.1.0/24 # Cloudflare DNS
- ipBlock:
cidr: 8.8.8.0/24 # Google DNS
ports:
- protocol: TCP
port: 443
# Allow Prometheus scraping
- to:
- podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090
# Create password file
sudo htpasswd -c /etc/nginx/.htpasswd admin
sudo htpasswd /etc/nginx/.htpasswd prometheus
Use a sidecar proxy for token auth:
services:
netcheck:
image: ghcr.io/m1/netcheck:latest
ports:
- "127.0.0.1:8080:8080"
auth-proxy:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx-auth.conf:/etc/nginx/nginx.conf:ro
- ./htpasswd:/etc/nginx/.htpasswd:ro
depends_on:
- netcheck
Configure Prometheus with basic auth:
scrape_configs:
- job_name: 'netcheck'
scheme: http
basic_auth:
username: prometheus
password: secure-password
static_configs:
- targets: ['netcheck:8080']
services:
netcheck:
image: ghcr.io/m1/netcheck:latest
secrets:
- db_password
secrets:
db_password:
external: true
services:
netcheck:
image: ghcr.io/m1/netcheck:latest
env_file:
- ./secrets.env
secrets.env (restricted permissions):
chmod 600 secrets.env
apiVersion: v1
kind: Secret
metadata:
name: netcheck-secrets
type: Opaque
stringData:
db-password: "secure-password-here"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
Encrypt logs containing network topology:
# Encrypt log files
gpg --symmetric --cipher-algo AES256 /var/log/netcheck.log
# Or use encrypted storage
docker run --mount type=volume,source=encrypted-logs,target=/logs ...
Monitor access to metrics endpoint:
# Nginx access log format
log_format netcheck_audit '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_user_agent" rt=$request_time';
access_log /var/log/nginx/netcheck-access.log netcheck_audit;
# Add to Docker Compose or apply on host
sysctls:
- net.ipv4.conf.all.rp_filter=1
- net.ipv4.conf.default.rp_filter=1
- net.ipv4.conf.all.accept_redirects=0
- net.ipv4.conf.default.accept_redirects=0
- net.ipv4.conf.all.send_redirects=0
- net.ipv4.conf.default.send_redirects=0
sudo tee -a /etc/sysctl.d/99-netcheck-hardening.conf << 'EOF'
# Network hardening
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.tcp_syncookies = 1
EOF
sudo sysctl -p /etc/sysctl.d/99-netcheck-hardening.conf
# Check for updates
docker compose pull
# Update with zero downtime
docker compose up -d --force-recreate
# Clean up old images
docker image prune -f
# Watchtower for automatic updates (use with caution)
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 86400 --cleanup netcheck
Security Note: Auto-updaters introduce supply chain risk. Prefer manual updates with image digest pinning:
image: ghcr.io/m1/netcheck:latest@sha256:<digest>
# Backup Docker Compose and configuration
tar -czvf netcheck-config-$(date +%Y%m%d).tar.gz \
/opt/netcheck/docker/docker-compose.yml \
/etc/nginx/conf.d/netcheck.conf \
/etc/nginx/.htpasswd
# Encrypt with GPG
gpg --symmetric --cipher-algo AES256 netcheck-config-$(date +%Y%m%d).tar.gz
Any questions?
Feel free to contact us. Find all contact information on our contact page.