Tag: security

  • 10 Lines to Better Docker Compose Secrets

    10 Lines to Better Docker Compose Secrets

    This is a prac­ti­cal pat­tern I use when con­tainer­ized apps expect envi­ron­ment vari­ables but I want the secu­ri­ty ben­e­fits of file-​mounted secrets. Drop the shell script below next to your Docker Compose files and you can do the same.

    Quick overview

    Secrets like pass­words and API keys belong out­side your repos­i­to­ry and appli­ca­tion image lay­ers. Docker Compose can mount such secrets in your con­tain­ers as files under /run/secrets, which keeps them out of images and ver­sion con­trol. But many apps still expect con­fig­u­ra­tion via envi­ron­ment vari­ables. Rather than chang­ing app code, I use a tiny wrap­per script that:

    • reads every file in /run/secrets
    • exports each file’s con­tents as an envi­ron­ment variable
    • then execs the orig­i­nal command

    It’s small, pre­dictable, portable, and keeps secrets from mix­ing with your ver­sioned .env envi­ron­ment files and out of your Compose files.

    How it works

    • Location: Docker Compose mounts secrets into the con­tain­er at /run/secrets/<NAME>.
    • Mapping rule: The wrap­per uses those file names as envi­ron­ment vari­able names; the file con­tents become the val­ues. Secret names in your Compose file must be valid shell iden­ti­fiers (they become both the file names in /run/secrets and the export­ed vari­able names).
    • Execution: After export­ing vari­ables, the script uses exec "$@" so that the wrapped process replaces the shell and inher­its the export­ed environment.
    • Security mod­el: Secrets remain files you can per­mis­sion appro­pri­ate­ly on the host; they’re not baked into images or stored in your Compose YAML as plain text.

    The script

    Let’s call it with-secrets.sh:

    #!/bin/sh
    set -eu
    
    for secret_file in /run/secrets/*; do
      [ -e "$secret_file" ] || continue
      if [ -f "$secret_file" ]; then
        name=$(basename "$secret_file")
        export "$name=$(cat "$secret_file")"
      fi
    done
    
    exec "$@"

    Notes about the script

    • set -eu fails fast on unset vari­ables or errors.
    • Since it exports each secret using the file name as the vari­able name, san­i­tize the file name if you need dif­fer­ent envi­ron­ment vari­able names.
    • The final exec hands con­trol to your app with­out leav­ing an extra shell process.

    Example Compose snippet

    services:
      app:
        image: your-app:latest
        secrets:
          - DB_PASS
          - API_KEY
        volumes:
          - ./with-secrets.sh:/with-secrets.sh:ro
        command: ["/with-secrets.sh", "your-original-command", "--with-args"]
    
    secrets:
      DB_PASS:
        file: ./secrets/db_password.txt
      API_KEY:
        file: ./secrets/api_key.txt

    Behavior: DB_PASS and API_KEY above appear as files (/run/secrets/DB_PASS, /run/secrets/API_KEY); the mount­ed with-secrets.sh wrap­per script exports them as DB_PASS and API_KEY envi­ron­ment vari­ables for your-original-command --with-args.

    Decision points and alternatives

    • Prefer native *_FILE sup­port if your app sup­ports it (e.g., PostgreSQL’s PGPASSFILE). That avoids the wrap­per entirely.
    • For multi-​host or high-​compliance deploy­ments, use an exter­nal secrets man­ag­er (e.g., Hashicorp Vault, cloud KMS, SOPS) rather than Compose secrets.
    • Build-​time secrets are a sep­a­rate con­cern; use BuildKit or ded­i­cat­ed build secret mech­a­nisms to avoid leak­ing cre­den­tials into your image layers.

    Risks and mitigations

    • Risk: Accidentally log­ging or dump­ing envi­ron­ment vari­ables
      Mitigation: Never print envi­ron­ment vari­ables in logs and restrict debug output
    • Risk: Secret file names that are not valid shell iden­ti­fiers
      Mitigation: Normalize or map file names to safe envi­ron­ment vari­able names before exporting
    • Risk: Secrets checked into git or oth­er ver­sion con­trol
      Mitigation: Keep secret files out of repos, add strict .gitignore rules, and inject secrets via CI/​CD or run­time provisioning

    Final notes

    This pat­tern is inten­tion­al­ly prag­mat­ic: it pre­serves the secu­ri­ty advan­tage of file-​mounted secrets while let­ting unmod­i­fied apps keep using envi­ron­ment vari­ables. It’s not a sil­ver bul­let for every environment–use it where Compose secrets are appro­pri­ate and pair it with stronger secret stores for production-​grade, multi-​host deployments.

  • A brief note on Log4perl

    A brief note on Log4perl

    The Java world had an… inter­est­ing week­end when secu­ri­ty researchers revealed on December 9 a vul­ner­a­bil­i­ty in the pop­u­lar Apache Log4j 2 soft­ware library for record­ing and debug­ging events. Systems as diverse as Amazon Web Services, Apple iCloud, and the Minecraft video game could be exploit­ed to run arbi­trary code on a serv­er mere­ly by send­ing a specially-​crafted string of text. Information tech­nol­o­gy pro­fes­sion­als have been scram­bling ever since the ini­tial dis­clo­sure to patch, upgrade, recon­fig­ure, or oth­er­wise pro­tect affect­ed servers. It’s bad, and past unpatched vul­ner­a­bil­i­ties like this have been respon­si­ble for the expo­sure of mil­lions of people’s sen­si­tive data.

    Many Perl appli­ca­tions use the similarly-​named and ‑designed Log::Log4perl library, and the good news is that as far as I can tell the lat­ter doesn’t suf­fer from the type of vul­ner­a­bil­i­ty described above. This doesn’t mean poorly-​written or ‑con­fig­ured Perl-​based sys­tems are immune to all exploits, just this par­tic­u­lar one. You should be safe to con­tin­ue using Log4perl unless some­one has delib­er­ate­ly con­fig­ured it oth­er­wise, and in fact, my work uses it extensively.

    You might be sur­prised to read me sug­gest­ing a log­ging frame­work after writ­ing mul­ti­ple arti­cles espous­ing the Perl step debug­ger as an alter­na­tive. Log4perl devel­op­er Mike Schilli’s 2002 intro­duc­tion to the pack­age for Perl.com came down on the oppo­site side of the argu­ment. It can seem like one of those pro­gram­mer reli­gious issues like tabs vs. spaces, vim vs. Emacs, or Linux vs. Windows. (For the record, the cor­rect answers are spaces, BBEdit, and macOS. 😉)

    But in this case, you can and should have the best of both worlds—logging at dif­fer­ent lev­els to appro­pri­ate des­ti­na­tions while still drop­ping into the inter­ac­tive debug­ger when you need to do some­thing trick­i­er like exam­ine pro­gram state or tweak a data struc­ture on the fly. I use both tech­niques and only empha­size the advo­ca­cy of step debug­ging because it’s under­stood less.