Sign inSign up

thelazyengineer/utb

By thelazyengineer

Updated 1 day ago

Docker network toolbox: DNS, HTTP, TCP, TLS, proxy, PostgreSQL & Java 17/21 diagnostics on UBI 10

Image
Networking
Developer tools
Monitoring & observability
1

655

thelazyengineer/utb repository overview

UTB — Universal Admin Toolbox

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.

Try it now — no workspace or host mounts

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 endpointhttp --json https://example.com
Reach an explicit service porttcp postgres.internal 5432 --timeout 3
Resolve a service namedns service.internal
Diagnose a proxy or corporate CAproxy, certs, keystore
Check database authentication and TLSpsql -h postgres.internal
Discover the installed commandstools or help

Diagnose Docker networks and Kubernetes pods

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.

HTTP/TCP checks and CI reports

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

Images and release

TagPurpose
thelazyengineer/utb:1.2.0Versioned release, 2026-09-19
thelazyengineer/utb:latestCurrent release
thelazyengineer/utb:ubi10Current release based on UBI 10
thelazyengineer/utb:1.1.0Previous release, retained for rollback
thelazyengineer/utb:1.0.0Legacy 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.

Quick Setup & Sample Files

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.

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.

Method 2: Manual Setup — Compose and .env

Create 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.

Step 1: Create docker-compose.yaml
services:
  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.

Step 2: Create .env

Save 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.

Run the toolbox after either setup method

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.

Guide: corporate proxies and configuration

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.

Guide: corporate CA trust and Java

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

Guide: PostgreSQL and keystores

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.

Commands, exit status, and reports

CommandPurpose
utb allAggregate 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 fileKeystore listing, certificate dates, and leaf SANs
utb psql -h hostDatabase TCP, authentication, SQL, and TLS
utb hostMounted Docker host proxy and CA configuration
utb portsCurrent-namespace listeners, forwarding, available Docker mappings
utb app containerContainer state, JVM, logs, sockets, and DB correlation
utb http URLHTTP 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 versionRelease identification
utb infoConfiguration summary with proxy credentials hidden
utb init [directory]Create missing workspace files
utb shellInteractive 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.

Guide: Docker host and application troubleshooting

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:

  • Exit 137: indicates SIGKILL; it is not proof of OOM. OOMKilled: true records Docker's OOM evidence. Compare host logs and memory measurements.
  • Memory pressure: JVM heap is only part of container memory. Account for thread stacks, metaspace, direct buffers, native libraries, and other processes.
  • CLOSE_WAIT: the peer closed its side and the application has not finished closing. Correlate exact database ports and trends before assigning a cause.
  • Connection pool timeouts: inspect pool occupancy, long queries, blocking transactions, network reachability, and JVM pauses before increasing limits.
  • Missing JVM/database data: may reflect missing tools or permissions; it is unavailable evidence rather than proof the service is healthy.
  • TLS differences: proxied HTTPS and direct TLS are different paths. Reuse the application's actual trust policy when comparing results.

Logs, JVM arguments, and database query text may contain sensitive information. Review reports before sharing them, even though known credentials are redacted.

Upgrading from 1.1.0

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.

Upgrading from 1.0.0

  1. Back up your workspace configuration and review the changes below.
  2. Pull 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.
  3. Remove direct read-only mounts onto the container's /etc/pki/ca-trust when imports are needed. Use /host-ca-trust:ro or the existing /host:ro mount.
  4. Replace shell expressions in .env with literal values; remove unused sample database or keystore settings. Review nonempty STACK_* overrides.
  5. Expect failed JSON checks to exit nonzero and accept booleans for success.
  6. Recreate only the toolbox service when ready:
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.

Offline guides

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.

Tag summary

Content type

Image

Digest

sha256:8b2512dab

Size

339.3 MB

Last updated

1 day ago

docker pull thelazyengineer/utb