How to Configure Windows Server 2019 Container Security
Securing Windows containers on Windows Server 2019 involves hardening the container runtime, securing container images, isolating workloads, controlling network access, and monitoring for anomalous behavior. The default Docker installation is functional but not hardened for production use. This guide covers the key security controls available for Windows containers on Windows Server 2019 and the specific configurations required to implement them.
Choosing the Right Isolation Mode
The first and most fundamental security decision is choosing between process isolation and Hyper-V isolation. Process-isolated containers share the host kernel — a kernel exploit in a container can potentially compromise the host. Hyper-V containers run with a dedicated kernel in a lightweight VM, providing strong isolation. For security-sensitive workloads, always use Hyper-V isolation:
docker run --isolation=hyperv --name secure_app mcr.microsoft.com/windows/servercore:ltsc2019 cmd
Enforce Hyper-V isolation as the default in the Docker daemon configuration:
$config = @{ "exec-opts" = @("isolation=hyperv") } | ConvertTo-Json
Set-Content "C:ProgramDatadockerconfigdaemon.json" $config
Restart-Service docker
Running Containers as Non-Administrator
By default, Windows containers run processes as ContainerAdministrator, which has elevated privileges inside the container. Use ContainerUser for non-privileged containers where possible:
docker run --user ContainerUser --isolation=hyperv mcr.microsoft.com/windows/servercore:ltsc2019 whoami
In a Dockerfile, switch to ContainerUser for the application process:
FROM mcr.microsoft.com/windows/servercore:ltsc2019
# Create a dedicated service account
RUN net user appservice /add /passwordreq:yes
RUN net localgroup "IIS_IUSRS" appservice /add
# Copy application
COPY ./app C:/app
# Drop to non-admin user
USER appservice
WORKDIR C:/app
ENTRYPOINT ["C:/app/MyApp.exe"]
Securing the Docker Daemon
The Docker daemon socket (named pipe on Windows) provides root-equivalent access. Restrict access to the Docker pipe to the Administrators group only and enable TLS for remote Docker API access:
$acl = Get-Acl "\.pipedocker_engine"
$acl | Format-List
Enable TLS for the Docker daemon to secure remote management. Generate TLS certificates and configure the daemon:
@"
{
"tlsverify": true,
"tlscacert": "C:/docker-tls/ca.pem",
"tlscert": "C:/docker-tls/server-cert.pem",
"tlskey": "C:/docker-tls/server-key.pem",
"hosts": ["tcp://0.0.0.0:2376", "npipe://"],
"exec-opts": ["isolation=hyperv"]
}
"@ | Set-Content "C:ProgramDatadockerconfigdaemon.json"
Restart-Service docker
Implementing Read-Only Container Filesystems
Running containers with a read-only root filesystem prevents attackers from writing malicious files or modifying application binaries. Mount only specific paths as writable:
docker run `
--read-only `
--isolation=hyperv `
--tmpfs C:/Temp `
-v AppLogs:C:/Logs `
--name readonly_app `
contoso/myapp:v1
Network Security for Containers
Isolate container networks to restrict lateral movement. Create separate networks for frontend and backend tiers:
docker network create --driver nat --subnet 172.21.0.0/24 frontend-net
docker network create --driver nat --subnet 172.22.0.0/24 backend-net
Apply Windows Firewall rules to restrict outbound traffic from the container network. Block all outbound connections from the container subnet except required services:
New-NetFirewallRule `
-DisplayName "Container Outbound - Block Default" `
-Direction Outbound `
-RemoteAddress "172.21.0.0/24" `
-Action Block `
-Priority 1000
New-NetFirewallRule `
-DisplayName "Container Outbound - Allow HTTPS" `
-Direction Outbound `
-RemoteAddress "172.21.0.0/24" `
-RemotePort 443 `
-Protocol TCP `
-Action Allow `
-Priority 900
Image Security Scanning
Scan container images for known vulnerabilities before deploying them to production. Use Trivy, the open-source vulnerability scanner, to scan Windows images:
Invoke-WebRequest -Uri "https://github.com/aquasecurity/trivy/releases/download/v0.48.0/trivy_0.48.0_windows-64bit.zip" -OutFile "C:trivy.zip"
Expand-Archive "C:trivy.zip" -DestinationPath "C:trivy"
C:trivytrivy.exe image --severity HIGH,CRITICAL mcr.microsoft.com/windows/servercore:ltsc2019
C:trivytrivy.exe image --format json --output C:scan-results.json contoso/myapp:v1
Group Managed Service Accounts for Containers
Group Managed Service Accounts (gMSA) allow Windows containers to authenticate to Active Directory resources like SQL Server and file shares without storing credentials in environment variables. Configure gMSA for containers:
Install-Module -Name CredentialSpec -Force
New-GMSACredentialSpec `
-Name "WebApp_gMSA" `
-AccountName "svc_webapp$" `
-Domain contoso.local `
-Path "C:ProgramDatadockercredentialspecswebapp_credspec.json"
Run a container with the gMSA credential spec:
docker run `
--security-opt "credentialspec=file://webapp_credspec.json" `
--hostname webapp01 `
--isolation=hyperv `
-d contoso/webapp:v1
Security Auditing and Logging
Enable Docker audit logging to record all Docker daemon activity. Configure comprehensive logging in the daemon:
@"
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
},
"debug": false,
"log-level": "warn",
"exec-opts": ["isolation=hyperv"],
"no-new-privileges": true
}
"@ | Set-Content "C:ProgramDatadockerconfigdaemon.json"
Monitor Docker events for security-relevant activities:
docker events `
--filter "type=container" `
--filter "event=exec_start" `
--filter "event=exec_create" `
--format "{{json .}}"
Container security on Windows Server 2019 is a multi-layered discipline. Using Hyper-V isolation for strong boundaries, gMSA for AD integration, non-admin users for least privilege, read-only filesystems where possible, and continuous image scanning creates a defense-in-depth posture that significantly reduces your container attack surface.