How to Forward HTTP Basic Auth Credentials via Nginx Reverse Proxy to Jenkins Backend


2 views

When implementing Nginx as a reverse proxy for Jenkins with HTTP Basic Authentication, developers often encounter issues passing the authenticated username to the backend service. The current approach of forwarding $http_authorization directly creates complications since Jenkins expects the username in a different format.

Here's the corrected Nginx configuration that properly forwards authentication details:

location / {
  auth_basic "Restricted";
  auth_basic_user_file /usr/share/nginx/.htpasswd;
  sendfile off;

  proxy_pass         http://192.168.178.102:8080;
  proxy_redirect     default;
  proxy_set_header   Host             $http_host;
  proxy_set_header   X-Real-IP        $remote_addr;
  proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  proxy_set_header   Authorization    $http_authorization;
  proxy_set_header   X-Forwarded-User $remote_user;
  
  # Additional proxy settings
  proxy_max_temp_file_size 0;
  client_max_body_size     10m;
  proxy_connect_timeout    90;
  proxy_buffer_size        4k;
}

The critical changes include:

  • Using $remote_user instead of $http_authorization for X-Forwarded-User
  • Properly forwarding the Authorization header
  • Maintaining all essential proxy headers for complete request tracking

For Jenkins to properly interpret these headers, you may need to configure the security realm. In config.xml:

<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
  <disableSignup>true</disableSignup>
  <enableCaptcha>false</enableCaptcha>
</securityRealm>
<useSecurity>true</useSecurity>

If authentication still fails:

  1. Verify Nginx can read the .htpasswd file: sudo nginx -t
  2. Check header presence in Jenkins logs
  3. Test with curl: curl -I -u user:pass http://your-proxy

For Jenkins WebSocket connections, add these headers:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto $scheme;

When using Nginx as a reverse proxy for Jenkins with HTTP Basic Authentication, one common requirement is to pass the authenticated username to the backend Jenkins instance. While the authentication works at the Nginx level, Jenkins often needs to know which user is making the request for proper authorization and auditing.

The existing Nginx configuration handles basic authentication and proxy setup correctly:

location / {
  auth_basic "Restricted";
  auth_basic_user_file /usr/share/nginx/.htpasswd;
  sendfile off;

  proxy_pass         http://192.168.178.102:8080;
  proxy_redirect     default;
  proxy_set_header   Host             $http_host;
  proxy_set_header   X-Real-IP        $remote_addr;
  proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  proxy_set_header   X-Forwarded-User $http_authorization; 
  proxy_max_temp_file_size 0;

  client_max_body_size       10m;
  client_body_buffer_size    128k;

  proxy_connect_timeout      90;
  proxy_send_timeout         90;
  proxy_read_timeout         90;             
  proxy_buffer_size          4k;
  proxy_buffers              4 32k;
  proxy_busy_buffers_size    64k;
  proxy_temp_file_write_size 64k;
}

The configuration attempts to forward the Authorization header using X-Forwarded-User $http_authorization, but this has two issues:

  • The raw Authorization header contains the Base64-encoded credentials, not just the username
  • Jenkins might not automatically interpret the X-Forwarded-User header

We can improve this by extracting just the username from the Authorization header:

location / {
  auth_basic "Restricted";
  auth_basic_user_file /usr/share/nginx/.htpasswd;
  
  # Map the Authorization header to extract just the username
  set $auth_user "";
  if ($http_authorization ~ "^Basic +([A-Za-z0-9+/=]+)") {
    set $auth_b64 $1;
    set $auth_user "";
    
    # Decode Base64 and extract username (first part before colon)
    auth_ldap_user_password=$(echo -n "$auth_b64" | base64 -d);
    set $auth_user $1;
    if ($auth_ldap_user_password ~ "^([^:]+):") {
      set $auth_user $1;
    }
  }

  proxy_set_header   Authorization     $http_authorization;
  proxy_set_header   X-Forwarded-User  $auth_user;
  proxy_set_header   X-Forwarded-Prefix /;
  
  # Other proxy settings remain the same
  proxy_pass         http://192.168.178.102:8080;
  # ... rest of proxy configuration
}

On the Jenkins side, you'll need to configure the security realm to trust the forwarded user header:

  1. Go to Jenkins > Manage Jenkins > Configure Global Security
  2. Under "Security Realm", select "Delegate to servlet container"
  3. Under "Authorization", select your preferred method (often "Matrix-based security")
  4. Install and configure the "Reverse Proxy Auth Plugin" if needed

For more complex scenarios, you can use Nginx with Lua to properly handle the authentication:

location / {
  access_by_lua_block {
    local auth = ngx.var.http_authorization
    if auth then
      local _, _, b64 = string.find(auth, "^Basic%s+(.+)$")
      if b64 then
        local decoded = ngx.decode_base64(b64)
        local user = string.match(decoded, "^([^:]+)")
        ngx.req.set_header("X-Forwarded-User", user)
      end
    end
  }
  
  proxy_pass http://jenkins_backend;
  proxy_set_header X-Forwarded-User $http_x_forwarded_user;
}

When implementing this solution, consider these security aspects:

  • Ensure your Nginx-Jenkins communication is over HTTPS or internal network
  • Validate the X-Forwarded-User header in Jenkins if exposed to external networks
  • Consider rate limiting to prevent brute force attacks
  • Regularly audit your .htpasswd file for outdated credentials