The Swarm Visualizer extends on the dockersamples/docker-swarm-visualizer concept demo'd at the 2015 DockerCon EU keynote.
Its goals are:
- minimize and sanitize internal Swarm data being exposed to the browser
- allow for more control of the data being displayed
- implement authentication via OpenID Connect
docker service create visualizer \
-e CLUSTER_NAME="Dev Cluster" \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
--constraint node.role==manager
jtgasper3/swarm-visualizerSee deployment/ for a fuller example.
service:
viz:
image: jtgasper3/swarm-visualizer:latest
deploy:
placement:
constraints:
- node.role==manager
environment:
CLUSTER_NAME: Dev Cluster
ports:
- 8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sockThe examples above mount the Docker socket directly for simplicity. The socket is root-equivalent on the host and grants full control of the swarm — see Security Considerations for a hardened, read-only setup before exposing this app.
General Environment Variables:
CLUSTER_NAME: title to display on the main pageCONTEXT_ROOT: the context root of the web app; useful when working with reverse-proxies (default:/)LISTENER_PORT: port to listen on (default:8080)HIDE_ALL_CONFIGS: hides all configs values (default:false)HIDE_ALL_ENVS: hides all environment variables values (default:false)HIDE_ALL_MOUNTS: hides all mounts values (default:false)HIDE_ALL_SECRETS: hides all secrets values (default:false)HIDE_LABELS: comma list of values that hides labels values fromall,container,network,node,service(default:(nothing))SENSITIVE_DATA_PATHS: comma delimited path of values to remove from the exported data. See Data Sanitization below
OIDC Environment Variables:
ENABLE_AUTHN:trueenable OIDC authentication support (default:false)OIDC_CLIENT_ID: standard OAuth client idOIDC_CLIENT_SECRET_FILE: path to file containing a standard OAuth client secretOIDC_REDIRECT_URL: this app's callback url; should end in/callbackand will be registered in the identity provider. For example,https://myswarm.example.internal/visualizer/callbackOIDC_SCOPES: comma separated list of scopes. For example,openid,profile,emailOIDC_WELL_KNOWN_URL: location to look up the identity provider's public signing key, token and authorization endpoints. For example,https://auth.example.com/.well-known/openid-configurationOIDC_AUTH_URL: authorization endpoint URL; overrides the value fromOIDC_WELL_KNOWN_URLif setOIDC_TOKEN_URL: token endpoint URL; overrides the value fromOIDC_WELL_KNOWN_URLif setOIDC_USERNAME_CLAIM: JWT claim to use as the display username (default:preferred_username)OIDC_SESSION_MAX_AGE: lifetime of the session cookie in seconds (default:3600)
Other Environment Variables:
DOCKER_API_VERSION: adjust the Docker api version if the server needs it. (default:(negotiated))TRUSTED_PROXIES: comma-separated list of trusted reverse-proxy IP addresses or CIDR ranges. When set, theX-Real-IPandX-Forwarded-Forheaders are trusted for rate limiting purposes when the direct connection originates from a listed address. Plain IPs are accepted alongside CIDR notation (e.g.10.0.0.0/8,192.168.1.5). Only set this if the application port is not directly reachable by untrusted clients, otherwise clients can spoof their IP to bypass rate limits.
When running behind a reverse proxy (such as Traefik or nginx), set TRUSTED_PROXIES to the proxy's IP or subnet and CONTEXT_ROOT to the path prefix if the app is not served from /. For example:
environment:
CONTEXT_ROOT: /visualizer/
TRUSTED_PROXIES: 10.0.0.0/8The Docker API can expose potentially sensitive information. There are several methods to sanitize data from the payload that can be tailored to your needs:
- Using the environment variables of
HIDE_ALL_CONFIGS,HIDE_ALL_ENVS,HIDE_ALL_MOUNTS, andHIDE_ALL_SECRETSwith the value oftruewill cause the application to strip the respective values from the output sent to the browser. - The environment variable
HIDE_LABELScan be used to strip the output of various labels using a comma separated list ofcontainer,network,node,service. The value ofallcan also be used instead of specified all of the values. - To manage things on a service by service level, use labels on the desired service (with
io.github.jtgasper3.visualizer.hide-labels) and environment variables (io.github.jtgasper3.visualizer.hide-envs) to specify a comma separated list of label or environment variables to remove from the service's specific labels or environment variable values from the output. The value is changes to "(sanitized)".
For very granular control over uses that we didn't consider, use the environment variable of SENSITIVE_DATA_PATHS and a comma separated list of paths to remove. Examine the JSON output and find and specify the path to remove. Use * for arrays, and use single quotes to delimit values of property names that have embedded periods (i.e. services.*.Spec.TaskTemplate.ContainerSpec.Labels.'desktop.docker.io/mounts/0/Source').
Securing a deployment is the operator's responsibility. The two items below have the largest impact and are not handled by the application itself.
This app reads cluster state from the Docker Engine API. Mounting /var/run/docker.sock directly (as the Usage examples do for brevity) hands the container full, root-equivalent control of the host and the entire swarm. The app only ever performs read operations (listing nodes, services, tasks, and networks), so it does not need that level of access.
For anything beyond local development, place a read-only socket proxy between the app and the Docker socket and grant it only the endpoints this app uses. The app honors the standard DOCKER_HOST variable, so point it at the proxy and drop the socket mount entirely:
services:
dockerproxy:
image: ghcr.io/tecnativa/docker-socket-proxy:latest
environment:
# Grant only the read endpoints this app needs; everything else stays denied,
# and the proxy rejects all write (POST/PUT/DELETE) calls by default.
NODES: 1
SERVICES: 1
TASKS: 1
NETWORKS: 1
deploy:
placement:
constraints:
- node.role == manager # swarm reads must run on a manager
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- viz
viz:
image: jtgasper3/swarm-visualizer:latest
environment:
CLUSTER_NAME: Dev Cluster
DOCKER_HOST: tcp://dockerproxy:2375
ports:
- 8080:8080
networks:
- viz
# No docker.sock mount and no manager constraint needed on the app itself —
# only the proxy talks to the socket and must run on a manager.
networks:
viz:
driver: overlayWith this setup the application can no longer issue write commands to the daemon, so a compromise of the app cannot be escalated into control of the cluster.
Setting ENABLE_AUTHN=true enables authentication only: it verifies that a request carries a valid, unexpired ID token issued by your configured identity provider for this client (the token's signature, aud, and iss are checked). It does not perform authorization — there is no per-user, group, or role check. Any identity your IdP will issue such a token to can view the dashboard.
To control who may access the app, restrict it at the boundaries:
- At the identity provider — assign an application role, or limit the app registration / enterprise application to specific users or groups, so the IdP only issues tokens to intended users.
- At a reverse proxy — enforce an allow-list or forward-auth policy in front of the app.
Combine access control with the Data Sanitization options above to limit what authenticated users can see (environment variables, mount sources, configs, and labels are exposed unless hidden).
If not already enabled:
docker swarm initdocker compose up --watchThe compose file mounts the static assets so they can be modified on the fly.
docker stack deploy -c test/docker-stack-test.yml test_1
docker stack deploy -c test/docker-stack-test.yml test_2
docker service create --name httpd httpd:2.4
Stop dummy services:
docker service rm httpd
docker stack rm test_1
docker stack rm test_2Remove Swarm mode:
docker swarm leave --force