Production-ready Docker image for Duo Security Authentication Proxy with RADIUS support. Built for linux/amd64 and linux/arm64 on Ubuntu 24.04.
docker run -d \
--name duoauthproxy \
-p 1812:1812/udp \
-e RADIUS_HOST=10.10.10.2 \
-e RADIUS_SECRET=radiussecret \
-e DUO_IKEY=DIXXXXXXXXXXXXXXXXXX \
-e DUO_SKEY=YourSecretKeyHere \
-e DUO_API_HOST=api-XXXXXXXX.duosecurity.com \
-e RADIUS_CLIENT_IP_1=192.168.1.10 \
-e RADIUS_CLIENT_SECRET_1=clientsecret \
ghcr.io/ict-solutions-dev/duoauthproxy:edge-duo6.6.0services:
duoauthproxy:
image: ghcr.io/ict-solutions-dev/duoauthproxy:1.2.0-duo6.6.0
container_name: duoauthproxy
restart: unless-stopped
ports:
- "1812:1812/udp"
environment:
RADIUS_HOST: 10.10.10.2
RADIUS_SECRET_FILE: /run/secrets/radius_secret
DUO_IKEY_FILE: /run/secrets/duo_ikey
DUO_SKEY_FILE: /run/secrets/duo_skey
DUO_API_HOST: api-XXXXXXXX.duosecurity.com
RADIUS_CLIENT_IP_1: 192.168.1.10
RADIUS_CLIENT_SECRET_FILE: /run/secrets/radius_client_secret
RADIUS_FAILMODE: safe
secrets:
- radius_secret
- duo_ikey
- duo_skey
- radius_client_secret
volumes:
- duoauthproxy-logs:/opt/duoauthproxy/log
secrets:
radius_secret:
file: ./secrets/radius_secret.txt
duo_ikey:
file: ./secrets/duo_ikey.txt
duo_skey:
file: ./secrets/duo_skey.txt
radius_client_secret:
file: ./secrets/radius_client_secret.txt
volumes:
duoauthproxy-logs:| Variable | Description |
|---|---|
RADIUS_HOST |
Primary RADIUS server hostname or IP |
RADIUS_SECRET |
RADIUS server shared secret |
DUO_IKEY |
Duo integration key |
DUO_SKEY |
Duo secret key |
DUO_API_HOST |
Duo API hostname (e.g. api-XXXXXXXX.duosecurity.com) |
RADIUS_CLIENT_IP_1 |
First RADIUS client IP address |
RADIUS_CLIENT_SECRET_1 |
First RADIUS client shared secret |
| Variable | Default | Description |
|---|---|---|
RADIUS_PORT |
1812 |
Upstream RADIUS server port |
RADIUS_SERVER_PORT |
1812 |
Local RADIUS listener port |
RADIUS_CLIENT_TYPE |
radius_client |
Auth client type — radius_client, ad_client, or duo_only_client |
RADIUS_FAILMODE |
safe |
Behavior when Duo is unreachable — safe (allow) or secure (deny) |
RADIUS_PASS_THROUGH_ALL |
false |
Forward all RADIUS attributes to the client |
RADIUS_PASS_THROUGH_ATTRS |
— | Comma-separated attribute names to forward (e.g. Framed-IP-Address,Reply-Message) |
Configure additional upstream RADIUS servers using sequentially numbered variables:
| Variable | Description |
|---|---|
RADIUS_HOST_2 … RADIUS_HOST_6 |
Additional RADIUS server hostnames/IPs |
RADIUS_PORT_2 … RADIUS_PORT_6 |
Ports for additional servers (default: 1812) |
Note: Hosts must be defined sequentially — you cannot define
RADIUS_HOST_4withoutRADIUS_HOST_2andRADIUS_HOST_3.
| Variable | Description |
|---|---|
RADIUS_CLIENT_IP_2 … RADIUS_CLIENT_IP_6 |
Additional client IPs |
RADIUS_CLIENT_SECRET_2 … RADIUS_CLIENT_SECRET_6 |
Corresponding client secrets |
Every environment variable that contains sensitive data supports the _FILE suffix pattern for use with Docker secrets or mounted files:
# Instead of passing the secret directly:
-e RADIUS_SECRET=mysecret
# Mount a file and reference it:
-e RADIUS_SECRET_FILE=/run/secrets/radius_secretIf both VAR and VAR_FILE are set, the container will exit with an error.
| Port | Protocol | Purpose |
|---|---|---|
1812–1818 |
UDP | RADIUS authentication |
18120 |
UDP | Duo Authentication Proxy |
636 |
TCP | LDAPS |
389 |
TCP | LDAP |
| Path | Purpose |
|---|---|
/opt/duoauthproxy/conf/ |
Configuration directory (generated at startup) |
/opt/duoauthproxy/log/ |
Application logs |
The entrypoint script (assets/01-init.sh) performs the following on container startup:
- Secret file resolution — reads
*_FILEenvironment variables from mounted files - Input validation — checks required variables, sequential host ordering, valid client types and failmodes
- Config generation — writes
/opt/duoauthproxy/conf/authproxy.cfgfrom environment variables - Connectivity test — runs
authproxy_connectivity_toolto verify Duo API reachability - Startup — launches the authentication proxy daemon via
exec
Secrets are redacted in the startup log output.
This project uses a dual version scheme — the image tag contains both the project version and the upstream Duo Authentication Proxy version:
ghcr.io/ict-solutions-dev/duoauthproxy:1.2.0-duo6.6.0
^^^^^ ^^^^^
project Duo upstream
| Change | Bump | Example |
|---|---|---|
| Breaking change (renamed env vars, new entrypoint) | Major | v1.x.x → v2.0.0 |
| New feature (RadSec support, new env vars) | Minor | v1.1.0 → v1.2.0 |
| Bugfix, Duo version bump, base image update | Patch | v1.2.0 → v1.2.1 |
Images are published to GitHub Container Registry.
| Tag Pattern | Source | Example |
|---|---|---|
edge-duo{VERSION} |
develop branch |
edge-duo6.6.0 |
{RELEASE}-duo{VERSION} |
Git tag (v*) |
1.2.0-duo6.6.0 |
# Development (latest from develop branch)
docker pull ghcr.io/ict-solutions-dev/duoauthproxy:edge-duo6.6.0
# Production release
docker pull ghcr.io/ict-solutions-dev/duoauthproxy:1.2.0-duo6.6.0Only the latest Duo version is actively built. Older images remain available in GHCR but are no longer rebuilt.
The GitHub Actions workflow runs on push to develop, version tags (v*), and manual dispatch. The Duo version is defined as DUO_VERSION env in the workflow file.
The pipeline:
- Lints the Dockerfile with hadolint
- Builds multi-arch images (
linux/amd64,linux/arm64) using Docker Buildx - Pushes to GitHub Container Registry
- Scans for vulnerabilities with Trivy (results uploaded to Security tab)
- Generates SBOM in SPDX format via Anchore
- Attests build provenance with GitHub Attestations
- Container runs as non-root user
duo(UID/GID35505) - Trivy vulnerability scanning on every build
- SBOM generation for supply chain transparency
- Build provenance attestation via Sigstore
- Docker secrets support to avoid plaintext credentials
- Dependabot enabled for Docker base image and GitHub Actions updates
This repository enforces Conventional Commits for PR titles via prlint. Valid prefixes: feat, fix, chore, docs, perf, refactor, style, test, lang.
See repository for license details.