Docker network toolbox: DNS, HTTP, TCP, TLS, proxy, PostgreSQL & Java 17/21 diagnostics on UBI 10
655
UTB is a Linux administration and diagnostics image based on Red Hat UBI 10. It includes Java 17 and 21, DNS/network tools, TLS and Java keystore checks, PostgreSQL diagnostics, and Docker/Java/Tomcat application inspection.
docker run --rm thelazyengineer/utb:1.2.0 http https://example.com
docker run --rm thelazyengineer/utb:1.2.0 dns example.com
docker run --rm thelazyengineer/utb:1.2.0 tools
UTB is for Docker network troubleshooting and enterprise application diagnostics: DNS, HTTP timing/status, TCP reachability, TLS/CA trust, corporate proxies, PostgreSQL, and Java 17/21. Use the same image for a disposable check, a CI JSON report, or a persistent administration workspace. A Docker socket is needed only for Docker API inspection, not ordinary network checks.
| You need to… | Start with… |
|---|---|
| Check an HTTP health endpoint | http --json https://example.com |
| Reach an explicit service port | tcp postgres.internal 5432 --timeout 3 |
| Resolve a service name | dns service.internal |
| Diagnose a proxy or corporate CA | proxy, certs, keystore |
| Check database authentication and TLS | psql -h postgres.internal |
| Discover the installed commands | tools or help |
Use an existing Compose network to reach its service names, or share a running container's network to check its localhost endpoint:
# Replace myproject_default, postgres, and my-app with your existing resources.
docker run --rm --network myproject_default \
thelazyengineer/utb:1.2.0 tcp postgres 5432
docker run --rm --network container:my-app \
thelazyengineer/utb:1.2.0 http http://127.0.0.1:8080/health
On an existing Kubernetes pod running on an amd64 node, from your workstation:
kubectl debug -it pod/my-app --image=thelazyengineer/utb:1.2.0 \
--container=utb-debug -- /usr/local/bin/utb shell
The debug container shares the pod network; permissions and admission policy
still apply. Its record stays until the pod is removed. UTB does not bundle
kubectl; the command above runs on your workstation. The image is amd64 only;
Kubernetes examples have not been validated on a live cluster.
mkdir -p reports
docker run --rm --user "$(id -u):$(id -g)" \
--cap-drop ALL --security-opt no-new-privileges \
-v "$(pwd)/reports:/workspace:z" \
thelazyengineer/utb:1.2.0 http https://example.com \
--timeout 10 --expect-status 200 --json --report /workspace/http.json
HTTP uses GET, verifies TLS, respects proxies/NO_PROXY, and succeeds on 2xx by
default. --follow enables bounded redirects; --expect-status 200,204 selects
accepted codes. HTTPS redirects cannot downgrade to HTTP; downloads are capped
at 10 MiB. DNS, connect, TLS, first-byte, and total timings are cumulative
milliseconds; proxies and connection reuse affect their interpretation. Reports
show the URL origin and omit its path, query, userinfo, fragment, body, and headers.
Use --ca-file /workspace/roots.pem to select an HTTP trust bundle.
TCP checks connect without sending application data. Specify up to 16 ports:
tcp host 443 8443 --timeout 3. The timeout applies to each port, including DNS
and address attempts. An open port establishes reachability, not service health.
These explicit checks are separate from the configured utb all audit.
Failures return nonzero even in JSON mode; reports use mode 0600. CI should
collect the report on failure too. More examples cover restricted non-root runs,
packet capture, corporate trust, and service networks in the bundled recipe guide:
docker run --rm thelazyengineer/utb:1.2.0 cat /opt/utb/docs/RECIPES.md
| Tag | Purpose |
|---|---|
thelazyengineer/utb:1.2.0 | Versioned release, 2026-09-19 |
thelazyengineer/utb:latest | Current release |
thelazyengineer/utb:ubi10 | Current release based on UBI 10 |
thelazyengineer/utb:1.1.0 | Previous release, retained for rollback |
thelazyengineer/utb:1.0.0 | Legacy release |
Published and tested platform: Linux amd64. Pin a version or digest for repeatable deployments. Rolling tags can change.
Version 1.2.0 adds HTTP timing/status checks, targeted TCP checks, tool discovery, and practical Docker/Kubernetes/CI recipes. Validation includes offline regressions, lint/syntax checks, isolated image/network smoke tests, both Compose configurations, and disposable PostgreSQL tests. Both JDKs are included: this is a full administration image, not a minimal network-only image.
Choose Method 1 to generate the workspace automatically, or Method 2 to
create both docker-compose.yaml and .env manually. Both methods use the
published image; no source checkout or Dockerfile is required.
init (Recommended)Create a workspace directory, then run the image's init command:
mkdir -p utb-workspace
cd utb-workspace
docker pull thelazyengineer/utb:1.2.0
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd):/workspace:z" thelazyengineer/utb:1.2.0 init
This creates docker-compose.yaml, .env, .env.example, host-scripts/, and
a workspace README.md in the mounted directory. New .env files have mode
0600; --user makes the generated files belong to your host user.
Edit the generated .env for your proxy, database, or keystore. Leave optional
settings blank when unused. init already creates .env; no copy step is
needed. The generated Compose file follows latest; set its image: to
thelazyengineer/utb:1.2.0 if you want to pin this release.
utb init [directory] defaults to /workspace. Inside a running toolbox,
utb init /workspace performs the same initialization. Re-running it creates
only missing files and preserves existing configuration, helpers, symlinks,
and modes. To obtain updated samples, initialize an empty directory and merge
the changes you want.
.envCreate a workspace directory and place both files below in the same directory. The environment file is part of this setup method, not a separate method.
docker-compose.yamlservices:
utb:
image: thelazyengineer/utb:1.2.0
container_name: utb-toolbox
stdin_open: true
tty: true
network_mode: host
env_file:
- .env
environment:
# STACK_* overrides inherited host-shell proxy settings.
HTTP_PROXY: ${STACK_HTTP_PROXY:-${HTTP_PROXY:-${http_proxy:-}}}
HTTPS_PROXY: ${STACK_HTTPS_PROXY:-${HTTPS_PROXY:-${https_proxy:-}}}
NO_PROXY: ${STACK_NO_PROXY:-${NO_PROXY:-${no_proxy:-}}}
http_proxy: ${STACK_HTTP_PROXY:-${HTTP_PROXY:-${http_proxy:-}}}
https_proxy: ${STACK_HTTPS_PROXY:-${HTTPS_PROXY:-${https_proxy:-}}}
no_proxy: ${STACK_NO_PROXY:-${NO_PROXY:-${no_proxy:-}}}
volumes:
- .:/workspace:z
# Host inspection and RHEL CA discovery; omit for remote-only checks.
- /:/host:ro
# Docker API access grants host-level control even with :ro.
- /var/run/docker.sock:/var/run/docker.sock:ro
# Optional extra CA certificates; create ./certs before enabling.
# - ./certs:/custom-certs:ro
Other toolbox settings are loaded from .env by env_file; they do not need
duplicate entries under environment. This manual example does not require
host-scripts/. Method 1 also supplies those optional convenience helpers.
.envSave this alongside docker-compose.yaml, then fill in only the settings you need:
# Corporate proxy: leave blank for direct connections.
# Example URL: http://proxy.corp.example:8080
HTTP_PROXY=
HTTPS_PROXY=
NO_PROXY=localhost,127.0.0.1,::1
# Nonempty STACK_* values override inherited proxy settings.
STACK_HTTP_PROXY=
STACK_HTTPS_PROXY=
STACK_NO_PROXY=
PROXY_TEST_URL=https://registry-1.docker.io/v2/
# Optional database: leave POSTGRES_HOST blank to skip aggregate DB checks.
POSTGRES_HOST=
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=
POSTGRES_DB=postgres
PGSSLMODE=prefer
# For authenticated TLS, select verify-full and the appropriate CA file.
PGSSLROOTCERT=
# DNS and active JDK (17 or 21).
DNS_TARGETS="registry-1.docker.io access.redhat.com google.com"
JAVA_VERSION=17
# Optional Java keystore: use a path visible inside the container.
TLS_KEYSTORE_FILE=
TLS_KEYSTORE_PASSWORD=
TLS_KEY_PASSWORD=
TLS_KEYSTORE_TYPE=
FQDN=
VERIFY_TLS_HOST=
UTB_CA_BUNDLE=
Keep .env private and validate the two files together:
chmod 600 .env
docker compose config --quiet
The stack uses Linux host networking, the host filesystem, and the Docker socket
for host administration. A socket bind marked :ro still permits mutating Docker
API calls. Omit the host/socket mounts for remote-only diagnostics. Keep the
container's /etc/pki/ca-trust writable so custom CA imports can work.
For a one-off check:
docker compose run --rm utb utb help
docker compose run --rm utb utb all
For a persistent toolbox:
docker compose up -d
docker compose exec utb utb info
docker compose exec utb utb all --json --report /workspace/audit-report.json
docker compose exec utb utb shell
The utb command initializes settings for fresh docker exec sessions too.
Reports under /workspace persist in the mounted workspace.
Edit .env generated by init. A minimal example is:
HTTP_PROXY=http://proxy.corp.example:8080
HTTPS_PROXY=http://proxy.corp.example:8080
NO_PROXY=localhost,127.0.0.1,::1,.corp.internal
PROXY_TEST_URL=https://registry-1.docker.io/v2/
JAVA_VERSION=17
POSTGRES_HOST=
TLS_KEYSTORE_FILE=
Keep .env private (chmod 600 .env). Supply actual passwords through the
environment or private configuration rather than command arguments. Percent-
encode reserved characters inside proxy URL credentials. Credentials are hidden
in normal diagnostics but remain visible to sufficiently privileged operators.
Startup loads an explicit ENV_FILE, otherwise /workspace/.env, otherwise a
local .env. It treats dotenv values literally and loads only supported toolbox
settings: shell commands, variable interpolation, and multiline expressions are
not executed. Existing container environment variables win, including explicitly
empty values. Docker Compose separately performs its own interpolation; single-
quote values containing a literal $ in Compose dotenv files.
Proxy variables are mirrored between uppercase and lowercase. Nonempty
STACK_HTTP_PROXY, STACK_HTTPS_PROXY, and STACK_NO_PROXY override inherited
proxy values, useful when the host shell already sets corporate proxies.
Proxy credentials are not written into curl, wget, dnf, or shell profiles.
docker compose run --rm utb utb proxy --timeout 5 --json
docker compose run --rm utb utb dns --timeout 2 internal-db.corp.example
utb proxy deliberately tests the selected proxy even when NO_PROXY would
bypass the target; the report separately states the bypass decision. Registry
HTTP 401 is reachable, while proxy authentication errors (407), server errors
(5xx), and TLS verification errors fail the check. If no HTTP(S) proxy is
configured, the command checks direct connectivity.
Set DNS_TARGETS to space- or comma-separated hosts for environment-specific
DNS checks. utb all also tests default public TLS endpoints; use individual
commands with explicit targets when public access is intentionally restricted.
Mount extra PEM/DER certificates under /custom-certs:
docker run --rm -v "$(pwd)/certs:/custom-certs:ro" \
thelazyengineer/utb:1.2.0 certs app.corp.example:443
For a RHEL-family host trust store:
docker run --rm -v /etc/pki/ca-trust:/host-ca-trust:ro \
thelazyengineer/utb:1.2.0 certs app.corp.example:443
The host-aware Compose stack also discovers roots through /host/etc/pki/ca-trust.
Startup imports certificate blocks into the writable container trust store;
source mounts stay untouched and private keys are excluded. Both included JDKs
use the system Java trust store. Certificate imports are serialized and only
changed UTB-managed anchors cause trust regeneration.
Do not mount /etc/pki/ca-trust inside the container read-only when you need
imports. With a non-root user or read-only trust store, native tools can use an
already extracted host bundle, but new system/Java roots cannot be installed.
UTB_CA_BUNDLE or SSL_CERT_FILE selects an explicit native-tool PEM bundle;
this does not replace Java's system trust. Explicit per-tool trust settings and
database-specific PGSSLROOTCERT are preserved.
Select either included JDK:
docker run --rm -e JAVA_VERSION=17 thelazyengineer/utb:1.2.0 java -version
docker run --rm -e JAVA_VERSION=21 thelazyengineer/utb:1.2.0 java -version
Configure the optional database in the workspace .env:
POSTGRES_HOST=db.corp.example
POSTGRES_PORT=5432
POSTGRES_USER=observer
POSTGRES_PASSWORD='replace-with-your-password'
POSTGRES_DB=app
PGSSLMODE=verify-full
PGSSLROOTCERT=/workspace/certs/database-root.pem
docker compose run --rm utb utb psql --timeout 5 --json
The check reports TCP reachability, authentication, bounded diagnostic queries,
and active TLS status. Use a suitably scoped monitoring account. Leave
POSTGRES_HOST blank to skip the aggregate database check; psql -h specifies
the host, while psql --help displays help.
For a Java keystore, set TLS_KEYSTORE_FILE, TLS_KEYSTORE_PASSWORD, and
optionally TLS_KEYSTORE_TYPE=JKS or PKCS12 in .env:
docker compose run --rm utb utb keystore \
-f /workspace/certs/app.p12 --fqdn app.corp.example --json
The check validates listing/password integrity, certificate dates, and leaf
subject alternative names. It explicitly marks private-key decryption and
system-CA chain trust as NOT_TESTED. Successful listing alone does not prove
that an application can use the key or serve a trusted certificate chain.
| Command | Purpose |
|---|---|
utb all | Aggregate DNS, proxy, TLS, ports, and configured optional checks |
utb dns [hosts...] | Resolver settings, forward/reverse lookup, latency |
utb proxy [url] | Proxy TCP, HTTP(S), TLS trust, or direct connectivity |
utb certs [host:port...] | Remote TLS trust, hostname, and certificate validity |
utb keystore -f file | Keystore listing, certificate dates, and leaf SANs |
utb psql -h host | Database TCP, authentication, SQL, and TLS |
utb host | Mounted Docker host proxy and CA configuration |
utb ports | Current-namespace listeners, forwarding, available Docker mappings |
utb app container | Container state, JVM, logs, sockets, and DB correlation |
utb http URL | HTTP status, TLS validation, and request timings; JSON/report support |
utb tcp host port [port ...] | Bounded DNS/TCP reachability for explicit ports |
utb tools [--json] | Installed utility inventory grouped by task |
utb version | Release identification |
utb info | Configuration summary with proxy credentials hidden |
utb init [directory] | Create missing workspace files |
utb shell | Interactive shell |
docker run IMAGE dns ... and docker run IMAGE utb dns ... are both supported.
Use utb <command> --help for complete options. Included tools also run directly,
including curl, wget, dig, openssl, psql, ip/ss, tcpdump, mtr, ncat, jq, Python,
keytool, jcmd, jstat, jstack, and jmap. For raw utilities that share a diagnostic
name, use their full path: utb /usr/bin/psql or utb /usr/bin/host.
Checks exit 0 on success, 1 on failure, and 2 on invalid arguments. JSON
checks also exit nonzero on failure; enable set -o pipefail when piping them
through jq. utb app is an evidence collector: exit 0 means its report was
collected, not that the application is healthy. An inaccessible target fails.
docker compose run --rm -T utb utb all --json --check-timeout 60 \
--report /workspace/audit-report.json
docker compose run --rm utb utb all --report /workspace/audit-report.txt
Aggregate JSON includes per-check status, duration, exit code, and details.
Unconfigured database/keystore checks and unavailable host mounts are skipped.
Reports are written atomically with mode 0600. Without host networking, socket
listeners describe the toolbox's network namespace, not the host's listeners.
Disabled IP forwarding is an observation, not automatically a failed audit.
The generated host helpers support noninteractive pipelines and an explicit image:
UTB_IMAGE=thelazyengineer/utb:1.2.0 ./host-scripts/run-utb.sh utb all --json
UTB_IMAGE=thelazyengineer/utb:1.2.0 ./host-scripts/diagnose-container.sh my-app --json
./host-scripts/check-docker-host.sh
Inspect a proposed daemon proxy change before explicitly applying it:
./host-scripts/setup-docker-proxy.sh --dry-run .env
sudo ./host-scripts/setup-docker-proxy.sh .env
The helper escapes systemd settings, hides credentials, backs up existing
configuration, and writes privately and atomically. It reloads systemd after
writing. --restart additionally restarts Docker; --clear explicitly clears
proxy configuration. These options are administrative actions, not audit steps.
Use utb app <container> --tail 200 --json to gather evidence. Interpret it with
application logs and metrics:
OOMKilled: true
records Docker's OOM evidence. Compare host logs and memory measurements.Logs, JVM arguments, and database query text may contain sensitive information. Review reports before sharing them, even though known credentials are redacted.
Pull thelazyengineer/utb:1.2.0, change your Compose image: to that version,
and recreate only the toolbox with docker compose up -d --no-build utb.
Confirm with docker compose exec utb utb version. Existing commands and
workspace files remain compatible. init continues to preserve existing files;
extract fresh samples into an empty directory when you want updated helpers.
The 1.1.0 tag remains available for rollback.
thelazyengineer/utb:1.2.0 and change the existing Compose image: to that
version. For a source-build Compose file, use --no-build when starting the
published image./etc/pki/ca-trust when
imports are needed. Use /host-ca-trust:ro or the existing /host:ro mount..env with literal values; remove unused sample
database or keystore settings. Review nonempty STACK_* overrides.success.docker compose pull utb
docker compose up -d --no-build --force-recreate utb
docker compose exec utb utb info
utb init preserves existing helpers and Compose files. To obtain updated
samples, initialize an empty directory and merge the desired changes:
mkdir -p utb-1.2.0-samples
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd)/utb-1.2.0-samples:/workspace:z" thelazyengineer/utb:1.2.0 init
Keep your private .env and customized files. The 1.0.0 image remains available
for rollback but lacks the fixes introduced in 1.1.0. If a credential from an earlier copied
test suite was ever used operationally, rotate it; removing it from a file does
not revoke it.
The image includes the user guide, recipes, troubleshooting guide, and release notes:
docker run --rm thelazyengineer/utb:1.2.0 cat /opt/utb/docs/USER_GUIDE.md
docker run --rm thelazyengineer/utb:1.2.0 cat /opt/utb/docs/TROUBLESHOOTING.md
docker run --rm thelazyengineer/utb:1.2.0 cat /opt/utb/docs/RELEASES.md
docker run --rm thelazyengineer/utb:1.2.0 cat /opt/utb/docs/RECIPES.md
The image defaults to a root shell for administration. Choose mounts, network access, user privileges, and credentials to match the diagnostic task. This toolbox collects evidence and does not replace application-specific monitoring.
Content type
Image
Digest
sha256:8b2512dab…
Size
339.3 MB
Last updated
1 day ago
docker pull thelazyengineer/utb