Introduction

Properly configuring Docker is essential for both security and functionality, especially in enterprise and production environments. This comprehensive guide covers critical configuration scenarios, security hardening techniques, and best practices for running Docker safely in production.

We’ll explore proxy configuration, registry trust management, security scanning, resource limits, and the principle of least privilege—all essential for production-ready Docker deployments.

Part 1: Configuring Docker for Corporate Networks

Understanding the Proxy Challenge

In corporate environments, direct internet access is often restricted. All traffic must go through an HTTP/HTTPS proxy server. Docker needs to be configured to use this proxy for:

  • Pulling images from Docker Hub
  • Building images that download packages
  • Containers that need internet access

Configuring the Docker Daemon for Proxy

The Docker daemon needs proxy configuration to pull images and communicate with registries.

On Linux (systemd):

  1. Create a systemd drop-in directory:
sudo mkdir -p /etc/systemd/system/docker.service.d
  1. Create the proxy configuration file:
sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
  1. Add the proxy settings:
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:8080"
Environment="HTTPS_PROXY=https://proxy.example.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp.example.com,10.0.0.0/8"

Understanding NO_PROXY:

  • localhost, 127.0.0.1: Local addresses
  • docker-registry.example.com: Your private registry
  • .corp.example.com: All subdomains of corp.example.com
  • 10.0.0.0/8: Internal network CIDR
  1. Reload and restart Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
  1. Verify the configuration:
sudo systemctl show --property=Environment docker

On Docker Desktop (Windows/Mac):

  1. Open Docker Desktop
  2. Go to SettingsResourcesProxies
  3. Enable Manual proxy configuration
  4. Enter your proxy details:
    • Web Server (HTTP): http://proxy.example.com:8080
    • Secure Web Server (HTTPS): https://proxy.example.com:8080
    • Bypass: localhost,127.0.0.1,*.example.com
  5. Click Apply & Restart

Configuring Proxy for Container Runtime

Containers themselves may also need proxy configuration to access the internet.

Method 1: Environment Variables in docker run

docker run -d \
  -e HTTP_PROXY="http://proxy.example.com:8080" \
  -e HTTPS_PROXY="https://proxy.example.com:8080" \
  -e NO_PROXY="localhost,127.0.0.1" \
  my-app

Method 2: In Dockerfile

FROM ubuntu:20.04

# Set proxy for build time
ENV HTTP_PROXY="http://proxy.example.com:8080"
ENV HTTPS_PROXY="https://proxy.example.com:8080"
ENV NO_PROXY="localhost,127.0.0.1"

# Install packages (will use proxy)
RUN apt-get update && apt-get install -y curl

# Unset proxy for runtime (optional)
ENV HTTP_PROXY=""
ENV HTTPS_PROXY=""

Method 3: Docker Compose

version: '3.8'

services:
  web:
    image: my-app
    environment:
      - HTTP_PROXY=http://proxy.example.com:8080
      - HTTPS_PROXY=https://proxy.example.com:8080
      - NO_PROXY=localhost,127.0.0.1

Method 4: Global Container Proxy (daemon.json)

sudo nano /etc/docker/daemon.json
{
  "proxies": {
    "default": {
      "httpProxy": "http://proxy.example.com:8080",
      "httpsProxy": "https://proxy.example.com:8080",
      "noProxy": "localhost,127.0.0.1,docker-registry.example.com"
    }
  }
}
sudo systemctl restart docker

Part 2: Configuring Access to Private/Insecure Registries

Understanding the Security Model

By default, Docker requires all registries to use TLS (HTTPS). This is a critical security feature. However, for local development or air-gapped networks, you may need to configure Docker to trust an insecure (HTTP) registry.

⚠️ Warning: Only use insecure registries in isolated, trusted networks. Never in production!

Configuring Insecure Registries

On Linux:

  1. Edit daemon.json:
sudo nano /etc/docker/daemon.json
  1. Add the insecure registry:
{
  "insecure-registries": [
    "myregistry.local:5000",
    "192.168.1.100:5000"
  ]
}
  1. Restart Docker:
sudo systemctl restart docker
  1. Verify:
docker info | grep -A 5 "Insecure Registries"

On Docker Desktop:

  1. Go to SettingsDocker Engine
  2. Edit the JSON configuration:
{
  "insecure-registries": [
    "myregistry.local:5000"
  ]
}
  1. Click Apply & Restart

Testing the Insecure Registry

# Pull an image
docker pull alpine

# Tag for your registry
docker tag alpine myregistry.local:5000/alpine:latest

# Push (will work now)
docker push myregistry.local:5000/alpine:latest

# Pull back
docker pull myregistry.local:5000/alpine:latest

Part 3: Docker Security Best Practices

1. Run Containers as Non-Root User

Why: If a container is compromised, the attacker has root access to the container and potentially the host.

Bad:

FROM node:16
WORKDIR /app
COPY . .
CMD ["node", "server.js"]
# Runs as root!

Good:

FROM node:16

# Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser

WORKDIR /app
COPY --chown=appuser:appuser . .

# Switch to non-root user
USER appuser

CMD ["node", "server.js"]

Verify:

docker run my-app whoami
# Should output: appuser (not root)

2. Use Read-Only Filesystems

Prevent containers from modifying their filesystem:

docker run --read-only \
  --tmpfs /tmp \
  --tmpfs /var/run \
  my-app

In Docker Compose:

services:
  web:
    image: my-app
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

3. Limit Container Resources

Prevent containers from consuming all host resources:

docker run -d \
  --memory="512m" \
  --memory-swap="512m" \
  --cpus="1.0" \
  --pids-limit=100 \
  my-app

In Docker Compose:

services:
  web:
    image: my-app
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M

4. Drop Unnecessary Capabilities

Linux capabilities allow fine-grained control over privileges:

docker run -d \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  my-web-server

Common capabilities:

  • NET_BIND_SERVICE: Bind to ports < 1024
  • CHOWN: Change file ownership
  • DAC_OVERRIDE: Bypass file permission checks
  • SETUID/SETGID: Change user/group IDs

5. Use Security Scanning

Scan images for vulnerabilities:

# Using Docker Scout (built-in)
docker scout cves my-app:latest

# Using Trivy
docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image my-app:latest

# Using Clair
docker run -d --name clair-db postgres
docker run -d --name clair --link clair-db:postgres \
  quay.io/coreos/clair:latest

6. Enable Docker Content Trust

Verify image signatures before pulling:

# Enable content trust
export DOCKER_CONTENT_TRUST=1

# Now only signed images can be pulled
docker pull alpine:latest
# Will fail if not signed

# Sign and push your own images
docker trust sign myregistry.com/my-app:v1.0
docker push myregistry.com/my-app:v1.0

7. Use Secrets Management

Never hardcode secrets in images!

Bad:

ENV DATABASE_PASSWORD=supersecret123

Good - Docker Secrets (Swarm):

# Create secret
echo "supersecret123" | docker secret create db_password -

# Use in service
docker service create \
  --name my-app \
  --secret db_password \
  my-app:latest

Good - Environment Files:

# .env file (add to .gitignore!)
DATABASE_PASSWORD=supersecret123

# Use with docker run
docker run --env-file .env my-app

# Use with docker-compose
docker-compose --env-file .env up

8. Network Segmentation

Isolate containers with custom networks:

# Create isolated networks
docker network create frontend
docker network create backend

# Frontend can't access backend directly
docker run -d --name web --network frontend nginx
docker run -d --name api --network backend my-api
docker run -d --name db --network backend postgres

# API connects both (acts as gateway)
docker network connect frontend api

9. Enable AppArmor/SELinux

AppArmor (Ubuntu/Debian):

# Check if enabled
sudo aa-status

# Run container with profile
docker run --security-opt apparmor=docker-default my-app

SELinux (RHEL/CentOS):

# Check if enabled
getenforce

# Run container with SELinux
docker run --security-opt label=type:container_runtime_t my-app

10. Regular Updates

# Update base images regularly
docker pull ubuntu:20.04
docker build --no-cache -t my-app .

# Remove old images
docker image prune -a

# Update Docker itself
sudo apt-get update && sudo apt-get upgrade docker-ce

Part 4: Docker Daemon Security Configuration

Secure daemon.json Configuration

{
  "icc": false,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "live-restore": true,
  "userland-proxy": false,
  "no-new-privileges": true,
  "seccomp-profile": "/etc/docker/seccomp-profile.json",
  "userns-remap": "default"
}

Key settings:

  • icc: false: Disable inter-container communication by default
  • log-opts: Prevent log files from filling disk
  • live-restore: Keep containers running during daemon downtime
  • no-new-privileges: Prevent privilege escalation
  • userns-remap: Use user namespaces for isolation

Enable TLS for Docker Daemon

Secure the Docker daemon socket:

# Generate certificates
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

# Configure daemon
sudo nano /etc/docker/daemon.json
{
  "tls": true,
  "tlscacert": "/etc/docker/ca.pem",
  "tlscert": "/etc/docker/server-cert.pem",
  "tlskey": "/etc/docker/server-key.pem",
  "tlsverify": true
}

Part 5: Monitoring and Auditing

Enable Audit Logging

# Install auditd
sudo apt-get install auditd

# Add Docker rules
sudo nano /etc/audit/rules.d/docker.rules
-w /usr/bin/docker -k docker
-w /var/lib/docker -k docker
-w /etc/docker -k docker
-w /usr/lib/systemd/system/docker.service -k docker
-w /etc/systemd/system/docker.service -k docker
sudo systemctl restart auditd

Monitor Container Activity

# Real-time stats
docker stats

# Inspect container processes
docker top my-container

# View container events
docker events --filter 'type=container'

# Export logs
docker logs my-container > container.log

Conclusion

Proper Docker configuration and security are critical for production deployments. This guide covered:

  • Proxy configuration for corporate networks
  • Insecure registry setup (with warnings)
  • Comprehensive security best practices
  • Resource limits and capability management
  • Secrets management
  • Network segmentation
  • Daemon security configuration
  • Monitoring and auditing

Key Security Takeaways:

  • Always run containers as non-root users
  • Use read-only filesystems where possible
  • Limit resources to prevent DoS
  • Drop unnecessary capabilities
  • Scan images for vulnerabilities
  • Never hardcode secrets
  • Segment networks appropriately
  • Keep Docker and images updated
  • Enable audit logging
  • Use TLS for daemon communication

By following these practices, you’ll create a secure, production-ready Docker environment that protects your applications and infrastructure.