Accueil
Emplacements
Blog
Produits
Proxies résidentiels Proxies résidentiels illimités Proxies résidentiels statiques Proxies de centre de données statiques Proxies statiques partagés
Ressources
Tarifs Emplacements Blog Centre d'aide FAQ
S'inscrire

Securing Proxy Credentials in GitHub Actions and Docker Environments

Proxy credentials should never be treated as a generic environment variable problem. In GitHub Actions and Docker environments, they cross multiple trust boundaries—repository workflows, self-hosted runners, Docker daemons, build containers, and logs—so the right solution is to separate where credentials are stored, where they are injected, and where they are allowed to persist.

That separation is what prevents the most common failure pattern: teams configure a proxy once, then unknowingly leak credentials into workflow YAML, container layers, shell history, debug logs, or host-level configs that outlive the build. A secure setup keeps proxy secrets in GitHub Secrets or another controlled secret store, applies proxy config separately for runners and Docker, avoids command-line exposure where possible, and validates each layer independently.

Step 1: Map the trust boundaries

Before changing any workflow, define which component actually needs proxy access. GitHub's runner proxy documentation makes clear that self-hosted runners can use proxy environment variables such as http_proxy, https_proxy, and no_proxy, and that Docker may need separate proxy configuration if your workflows use Docker container actions or service containers.

That means there are at least four distinct security surfaces:

  • The GitHub Actions workflow itself.

  • The self-hosted runner process.

  • The Docker daemon on the host.

  • The Docker build or runtime container environment.

If you do not split those surfaces, you get fragile behavior. A runner may be able to reach GitHub while docker build fails to fetch dependencies, or a container action may fail even though the workflow environment looks correctly configured.

For operational clarity, use this boundary model:

  • Secrets layer: stores proxy host, port, username, password, full proxy URLs, or decryption passphrases.

  • Runner layer: lets the self-hosted runner communicate with GitHub and required external services through the proxy.

  • Docker daemon layer: allows image pulls, service containers, and Docker-based actions to use the proxy.

  • Build layer: passes proxy values only into the build step that actually needs them, without hardcoding credentials into source files.

This first step is where most of the security value comes from. When you know which component needs the proxy, you can stop copying credentials into places that do not need them.

Step 2: Store proxy credentials the right way

GitHub's secrets documentation is explicit: secrets can be created at the repository, environment, or organization level, and used in workflows through the secrets context. That makes GitHub Secrets the correct default place for proxy credentials in Actions, not workflow YAML, committed .env files, or Dockerfiles.

A practical secret layout usually looks like this:

  • PROXY_HOST

  • PROXY_PORT

  • PROXY_USERNAME

  • PROXY_PASSWORD

  • NO_PROXY

  • Or one complete PROXY_URL if your tooling expects a full URI.

GitHub also warns against passing secrets on the command line whenever possible, because command-line processes may be visible to other users or captured in audit systems; environment variables, standard input, or other supported mechanisms are safer. That warning matters especially for proxy credentials, because they are often embedded in URLs and easy to leak by accident.

For most workflows, inject the secret values only at the job or step level where they are needed:

name: build-behind-proxy

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      HTTP_PROXY: http://${{ secrets.PROXY_USERNAME }}:${{ secrets.PROXY_PASSWORD }}@${{ secrets.PROXY_HOST }}:${{ secrets.PROXY_PORT }}
      HTTPS_PROXY: http://${{ secrets.PROXY_USERNAME }}:${{ secrets.PROXY_PASSWORD }}@${{ secrets.PROXY_HOST }}:${{ secrets.PROXY_PORT }}
      NO_PROXY: ${{ secrets.NO_PROXY }}
    steps:
      - uses: actions/checkout@v4

      - name: Verify proxy env is present
        run: |
          test -n "$HTTP_PROXY"
          test -n "$HTTPS_PROXY"

      - name: Build image
        run: |
          docker build \
            --build-arg HTTP_PROXY="$HTTP_PROXY" \
            --build-arg HTTPS_PROXY="$HTTPS_PROXY" \
            --build-arg NO_PROXY="$NO_PROXY" \
            -t my-image .

This pattern keeps the secret in GitHub's secret boundary until runtime and reduces the number of files where the credential ever exists in cleartext.

For larger secret payloads, GitHub documents two sanctioned workarounds: encrypt a file with gpg and store only the passphrase as a secret, or store a Base64-encoded small binary blob as a secret and decode it during the workflow. Those patterns are useful for proxy certificates, PAC files, bundled credential payloads, or enterprise proxy config artifacts that do not fit cleanly into a single simple secret.

One more best practice: avoid lumping everything into one structured mega-secret unless you have a very good reason. Current GitHub Actions secret-handling guidance recommends minimizing scope and keeping sensitive values individually manageable so they can be audited, rotated, and revoked more safely.

Step 3: Secure the runner and Docker separately

If you are using a self-hosted runner, the runner itself should be configured to use the proxy through environment variables or a .env file in the runner application directory. GitHub's runner docs state that these variables are read when the runner starts, which means you must set them before starting the runner and restart the runner when the proxy configuration changes.

A minimal self-hosted runner .env file looks like this:

https_proxy=http://proxy.local:8080
http_proxy=http://proxy.local:8080
no_proxy=example.com,myserver.local:443

That .env method is supported only on self-hosted runners, not GitHub-hosted runners. On Linux and macOS, GitHub also documents persistent configuration through /etc/environment, while Windows can use machine-level environment variables or netsh for WinHTTP-based apps.

If the runner is hosted in Azure, GitHub explicitly recommends adding 168.63.129.16 and 169.254.169.254 to no_proxy so the runner can still reach required Azure metadata and management services. This is not a small edge case; missing those addresses can cause confusing runner instability in otherwise correct proxy setups.

Now separate Docker from the runner. GitHub's proxy documentation states that Docker may also need its own proxy configuration when workflows use Docker container actions or service containers. In other words, a correctly configured runner does not automatically mean a correctly configured Docker daemon.

A standard Linux daemon configuration often looks like this:

# /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:3128"
Environment="HTTPS_PROXY=https://proxy.example.com:3129"
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp"

Then apply it with:

sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl show --property=Environment docker

This matters because Docker pulls, Docker-based GitHub Actions, and service containers may all depend on the daemon's proxy path rather than the workflow's shell environment.

For docker build, prefer build-time injection rather than hardcoding proxy credentials in the Dockerfile. CloudBees' proxy guidance shows that docker build --build-arg http_proxy=... and related build-time variables are a workable pattern, while permanent ENV lines in the Dockerfile create unnecessary persistence risk. If you bake proxy credentials directly into image layers, you increase the chance they remain in build metadata, cached layers, or derived images.

Step 4: Prevent leaks, verify behavior, and rotate

The final step is not configuration. It is validation. A secure setup is one where you can prove that the runner has proxy access, Docker has proxy access, the build has only the minimum required exposure, and the logs do not reveal credentials.

Use this validation sequence:

  1. Confirm the runner reads http_proxy, https_proxy, and no_proxy before startup or from its .env file.

  2. Confirm Docker daemon settings separately with systemctl show --property=Environment docker or the equivalent on your host.

  3. Confirm docker build only receives proxy values when that build step actually needs them.

  4. Inspect workflow logs to verify that secrets are redacted and no transformed or generated sensitive values are accidentally exposed.

GitHub explicitly warns that any sensitive information that is not a registered GitHub secret should be masked with ::add-mask::VALUE so it is treated as secret data in logs. This is especially important when a workflow derives a new proxy URL or decodes a certificate bundle from a stored secret, because generated values are not automatically protected unless you register or mask them.

There are also several operational practices that materially improve security:

  • Use minimally scoped credentials instead of one proxy account shared across every repo and environment.

  • Separate development, staging, and production proxy secrets so one leak does not affect every environment.

  • Rotate proxy credentials regularly and remove unused ones during scheduled reviews.

  • Audit secret usage and workflow access so only the required repositories or environments can read a given secret.

  • Consider external secrets managers for larger or more regulated environments, especially when you need lifecycle management, audit logging, and automated rotation.

The most common failure cases are predictable:

  • Runner works, Docker fails. Usually the runner proxy is configured, but Docker daemon proxy settings are missing.

  • Docker pulls work, builds fail. Usually the daemon has proxy access, but the build step never received --build-arg values.

  • Secrets stay hidden, but a derived proxy URL leaks. Usually the generated string was never masked with ::add-mask::VALUE.

  • A self-hosted runner in Azure behaves inconsistently. Usually no_proxy does not include the Azure metadata and management IPs GitHub calls out.

CTA

If you are also evaluating the proxy provider side—not just the CI/CD secret-handling side—proxy001.com is a useful benchmark to review because its public site advertises 100M+ residential IPs in 200+ locations, 99.9% uptime, sub-0.3 second speed, and SOCKS5/HTTP(S) support. That makes it a relevant external comparison point when you need to assess whether a proxy service is operationally compatible with GitHub Actions, Docker builds, and self-hosted runner environments in addition to being stored securely.

Démarrez votre service de proxy mondial
sécurisé et stable
Démarrez en quelques minutes et libéréz tout le potentiel des proxies.
Commencer