Authentication Issue using basic_auth with Caddy

chrisschmit
HOBBY

3 months ago

I have a web application deployed on Railway with two environments:

  1. Production: Publicly accessible without authentication

  1. Staging: Should be protected with HTTP Basic Authentication

I've implemented Caddy as a reverse proxy to handle routing and, in the case of the staging environment, to add Basic Authentication. While the authentication works perfectly in my local environment, it appears to be bypassed in the Railway-hosted staging environment.

What Works

  1. Local Environment: When testing locally, the Basic Authentication works perfectly

  • Requests to test.localhost return 401 without credentials

  • Requests with valid credentials are authenticated properly

  • The staging.localhost route is also properly protected

  1. Production on Railway: Working as expected without authentication

What Doesn't Work

Staging on Railway: The Basic Authentication is bypassed

  • The response headers show server: railway-edge indicating Railway's edge network is handling the requests

  • Authentication headers are being sent but not enforced

    Here is my caddy_file:

    { # global options
        admin off
        persist_config off
        auto_https off
        log {
            format console
            level DEBUG
        }
        servers {
            trusted_proxies static private_ranges
        }
    }
    
    :{$PORT} {
        log {
            format console
            level DEBUG
        }    
    
        handle /join-waitlist/api/* {
            uri strip_prefix /join-waitlist
            reverse_proxy {$BACKEND_HOST}
        }
    
        handle /api/* {
            reverse_proxy {$BACKEND_HOST}
        }
    
        header {
            # Set JavaScript MIME type for module scripts
            Content-Type "application/javascript" *.js
            # Set CSS MIME type
            Content-Type "text/css" *.css
        }
    
        handle_path /join-waitlist/* {
            reverse_proxy {$FRONTEND_HOST}
        }
    
        redir / /join-waitlist/ permanent
    }
    
    # Production staging domain (protected with basic auth)
    staging.example.com {
        log {
            output stdout
            format console
        }
    
        # Authentication for all paths
        basicauth /* {
            dev <HASHED_PASSWORD_HERE>
        }
    
        # Add cache control headers to prevent edge caching
        header {
            Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate"
            Pragma "no-cache"
            Expires "0"
        }
        
        # Handle specific paths AFTER authentication
        handle /join-waitlist/api/* {
            uri strip_prefix /join-waitlist
            reverse_proxy {$BACKEND_HOST}
        }
    
        handle /api/* {
            reverse_proxy {$BACKEND_HOST}
        }
    
        header {
            Content-Type "application/javascript" *.js
            Content-Type "text/css" *.css
        }
    
        handle_path /join-waitlist/* {
            reverse_proxy {$FRONTEND_HOST}
        }
    
        redir / /join-waitlist/ permanent
    }
    
    # Local staging environment (also protected)
    staging.localhost.test:80 {
        log {
            output stdout
            format console
        }
    
        # Same authentication block as production staging
        basicauth /* {
            dev <HASHED_PASSWORD_HERE>
        }
        
        # Handle specific paths AFTER authentication
        handle /join-waitlist/api/* {
            uri strip_prefix /join-waitlist
            reverse_proxy {$BACKEND_HOST}
        }
    
        handle /api/* {
            reverse_proxy {$BACKEND_HOST}
        }
    
        header {
            Content-Type "application/javascript" *.js
            Content-Type "text/css" *.css
        }
    
        handle_path /join-waitlist/* {
            reverse_proxy {$FRONTEND_HOST}
        }
    
        redir / /join-waitlist/ permanent
    }
    
    # Test endpoint to verify authentication
    test.localhost.test:80 {
        basicauth /* {
            dev <HASHED_PASSWORD_HERE>
        }
        respond "Authentication successful"
    }
$10 Bounty

3 Replies

sim
FREETop 10% Contributor

2 months ago

Railway is routing traffic to the port instead of the staging block


sim
FREETop 10% Contributor

2 months ago

You will have to update the file so the port block handles that case which maybe a catch all


sim
FREETop 10% Contributor

2 months ago

Why don't try logging something in that block and if it is logged when you make a staging request then that will prove this theory