Authentication Guide
Authentication Guide
Section titled “Authentication Guide”Bifrost authenticates connections using an ordered list of auth providers.
Each provider declares a plugin type, an optional priority (lower numbers are
tried first), and a config map with the plugin-specific settings.
Schema note: The legacy
auth.modefield and the top-level typed blocks (auth.native,auth.ldap,auth.oauth,auth.system, …) are no longer supported and the server rejects them at startup. Useauth.providers[]with an explicittypeand a nestedconfigmap, as shown below.
No Authentication
Section titled “No Authentication”Default behaviour - allows all connections.
auth: providers: - name: open type: none enabled: true priority: 1Native Authentication
Section titled “Native Authentication”Username/password authentication with bcrypt hashes.
auth: providers: - name: native type: native enabled: true priority: 1 config: users: - username: admin password_hash: "$2a$10$..." groups: - admins email: admin@example.com full_name: "Admin User" - username: user1 password_hash: "$2a$10$..." disabled: falseGenerating Password Hashes
Section titled “Generating Password Hashes”Using the bcrypt tool:
# Using htpasswdhtpasswd -bnBC 10 "" password | tr -d ':\n'
# Using Pythonpython3 -c "import bcrypt; print(bcrypt.hashpw(b'password', bcrypt.gensalt()).decode())"
# Using Gogo run -mod=mod github.com/rennerdo30/bifrost-proxy/tools/hashpw passwordLDAP Authentication
Section titled “LDAP Authentication”Authenticate against an LDAP directory.
auth: providers: - name: ldap type: ldap enabled: true priority: 1 config: url: "ldap://ldap.example.com:389" base_dn: "dc=example,dc=com" bind_dn: "cn=service,dc=example,dc=com" bind_password: "${LDAP_BIND_PASSWORD}" user_filter: "(uid=%s)" group_filter: "(memberUid=%s)" require_group: "proxy-users" tls: false insecure_skip_verify: falseLDAP Configuration
Section titled “LDAP Configuration”| Field | Description |
|---|---|
url | LDAP server URL |
base_dn | Base DN for searches |
bind_dn | DN for service account |
bind_password | Service account password |
user_filter | Filter to find users (%s = username) |
group_filter | Filter to find user groups |
require_group | Only allow users in this group |
System Authentication
Section titled “System Authentication”Authenticate against the operating system’s user database (PAM on Linux, Directory Services on macOS).
Warning: Platform Support System authentication is only supported on Linux and macOS. Windows is not currently supported. If you need authentication on Windows, use
native,ldap, oroauthinstead.
auth: providers: - name: system type: system enabled: true priority: 1 config: service: "login" # PAM service name (Linux only) allowed_users: # Optional: restrict to specific users - alice - bob allowed_groups: # Optional: restrict to specific groups - admin - staffPlatform Support Matrix
Section titled “Platform Support Matrix”| Platform | Support | Method |
|---|---|---|
| Linux | ⚠️ Requires custom build | PAM (only in CGO_ENABLED=1 -tags pam builds; the default/Docker build fails closed) |
| macOS | ✅ Supported | Directory Services (dscl) |
| Windows | ❌ Not Supported | Use native/ldap/oauth instead |
Warning: The default (and Docker) binaries are built without the
pamtag and with cgo disabled, so Linux system authentication fails closed — every login is rejected. To enable it, build from source withCGO_ENABLED=1 go build -tags pam ./...(requires libpam headers, e.g.libpam0g-dev/pam-devel). macOS does not need a special build.
System Authentication Configuration
Section titled “System Authentication Configuration”| Field | Description |
|---|---|
service | PAM service name (default: login) - Linux only |
allowed_users | Optional list of allowed usernames |
allowed_groups | Optional list of allowed groups (user must be in at least one) |
Requirements
Section titled “Requirements”- Linux: Requires a binary built with
CGO_ENABLED=1 -tags pamand the PAM development headers. The process must be able to authenticate against the configured PAM service (often needs root or a suitable/etc/pam.dentry). Without thepambuild tag the plugin fails closed. - macOS: Requires access to Directory Services via
dscl
OAuth/OIDC Authentication
Section titled “OAuth/OIDC Authentication”Authenticate using OAuth 2.0 or OpenID Connect.
auth: providers: - name: oauth type: oauth enabled: true priority: 1 config: provider: "generic" client_id: "${OAUTH_CLIENT_ID}" client_secret: "${OAUTH_CLIENT_SECRET}" issuer_url: "https://auth.example.com" redirect_url: "http://localhost:7081/callback" scopes: - openid - profile - emailMultiple Providers
Section titled “Multiple Providers”Providers are tried in priority order until one accepts the credentials. This
allows, for example, a static admin account alongside LDAP:
auth: providers: - name: break-glass type: native enabled: true priority: 1 config: users: - username: admin password_hash: "$2a$10$..." - name: corp-ldap type: ldap enabled: true priority: 2 config: url: "ldap://ldap.example.com:389" base_dn: "dc=example,dc=com" user_filter: "(uid=%s)"Using Authentication with Proxy
Section titled “Using Authentication with Proxy”HTTP Proxy Authentication
Section titled “HTTP Proxy Authentication”Clients authenticate using Proxy-Authorization header:
curl -x http://user:pass@localhost:7080 http://example.comSOCKS5 Authentication
Section titled “SOCKS5 Authentication”curl --socks5 user:pass@localhost:7180 http://example.comClient Authentication
Section titled “Client Authentication”The client can authenticate with the server:
server: address: "proxy.example.com:7080" username: "myuser" password: "mypass"API Key Authentication
Section titled “API Key Authentication”Authenticate using API keys passed in headers.
auth: providers: - name: apikey type: apikey enabled: true priority: 1 config: header: "X-API-Key" # Header name to check keys: - name: "service-a" key_hash: "$2a$10$..." # bcrypt hash of the API key groups: ["api-access"] - name: "service-b" key_hash: "$2a$10$..." groups: ["api-access", "admin"]Generating API Key Hashes
Section titled “Generating API Key Hashes”# Generate a random API keyopenssl rand -base64 32
# Hash the key for storagepython3 -c "import bcrypt; print(bcrypt.hashpw(b'your-api-key', bcrypt.gensalt()).decode())"JWT Authentication
Section titled “JWT Authentication”Validate JWT tokens with JWKS support for key rotation.
auth: providers: - name: jwt type: jwt enabled: true priority: 1 config: issuer: "https://auth.example.com" audience: "bifrost-proxy" jwks_url: "https://auth.example.com/.well-known/jwks.json" # Or use static key: # signing_key: "${JWT_SIGNING_KEY}" username_claim: "sub" groups_claim: "groups" allowed_algorithms: - RS256 - ES256JWT Configuration
Section titled “JWT Configuration”| Field | Description |
|---|---|
issuer | Expected issuer (iss claim) |
audience | Expected audience (aud claim) |
jwks_url | URL to fetch JSON Web Key Set |
signing_key | Static signing key (alternative to JWKS) |
username_claim | Claim to use as username |
groups_claim | Claim containing user groups |
TOTP Authentication
Section titled “TOTP Authentication”Time-based One-Time Password authentication, compatible with Google Authenticator.
auth: providers: - name: totp type: totp enabled: true priority: 1 config: issuer: "Bifrost Proxy" digits: 6 period: 30 algorithm: "SHA1" secrets: user1: "JBSWY3DPEHPK3PXP" # Base32-encoded secret user2: "GEZDGNBVGY3TQOJQ"Setting Up TOTP
Section titled “Setting Up TOTP”- Generate a secret for each user
- Share the secret with the user via QR code or manual entry
- User scans with authenticator app (Google Authenticator, Authy, etc.)
# Generate a random TOTP secretpython3 -c "import secrets; import base64; print(base64.b32encode(secrets.token_bytes(20)).decode())"HOTP Authentication
Section titled “HOTP Authentication”Counter-based One-Time Password authentication, compatible with YubiKey and similar hardware tokens.
auth: providers: - name: hotp type: hotp enabled: true priority: 1 config: digits: 6 algorithm: "SHA1" secrets: user1: secret: "JBSWY3DPEHPK3PXP" counter: 0 user2: secret: "GEZDGNBVGY3TQOJQ" counter: 100 look_ahead: 10 # Accept codes within this windowmTLS Certificate Authentication
Section titled “mTLS Certificate Authentication”Client certificate authentication for mutual TLS.
auth: providers: - name: mtls type: mtls enabled: true priority: 1 config: ca_cert: "/path/to/ca.crt" # Optional: require specific certificate attributes require_cn: true allowed_cns: - "client1.example.com" - "*.internal.example.com" # Map certificate subject to user username_from: "cn" # cn, email, or custom OIDCertificate Requirements
Section titled “Certificate Requirements”- Client must present a valid certificate signed by the configured CA
- Certificate must not be expired or revoked
- Subject CN or email is used as username
Kerberos/SPNEGO Authentication
Section titled “Kerberos/SPNEGO Authentication”Enterprise SSO using Kerberos with SPNEGO (HTTP Negotiate).
auth: providers: - name: kerberos type: kerberos enabled: true priority: 1 config: keytab_file: "/etc/krb5.keytab" service_principal: "HTTP/proxy.example.com" realm: "EXAMPLE.COM" strip_realm: true username_to_lowercase: trueHTTP browser SSO requires the Negotiate middleware. A bare
kerberosprovider validates username/password credentials. For transparent SPNEGO SSO over the HTTP proxy, also enable theauth.negotiateblock and pointkerberos_providerat this provider (see the Advanced Authentication guide).
Kerberos Setup
Section titled “Kerberos Setup”- Create a service principal for the proxy
- Export the keytab file
- Configure the client to use Kerberos (kinit)
# Create service principal (on KDC)kadmin -q "addprinc -randkey HTTP/proxy.example.com"kadmin -q "ktadd -k /etc/krb5.keytab HTTP/proxy.example.com"NTLM Authentication
Section titled “NTLM Authentication”Not functional — fails closed by design. The NTLM plugin cannot verify NTLM responses: Bifrost has no credential source (password/NT-hash store or domain-controller pass-through) against which to recompute and compare the client’s response. Accepting the attacker-supplied Type 3 message without cryptographic verification would be an authentication bypass, so the plugin rejects every login. It is retained only so a misconfiguration does not silently fall through to another provider. Do not rely on NTLM for access; use Kerberos,
native,ldap, oroauthinstead.
There is no working NTLM configuration. Any type: ntlm provider will fail
every authentication attempt with an “NTLM response verification is not
supported” error.
MFA Wrapper (Two-Factor Authentication)
Section titled “MFA Wrapper (Two-Factor Authentication)”Combine a primary authentication provider with an OTP provider for two-factor
authentication. The wrapper itself is a provider whose config embeds the
primary and secondary provider definitions.
auth: providers: - name: mfa type: mfa_wrapper enabled: true priority: 1 config: # Primary authentication (username/password) primary: mode: native config: users: - username: admin password_hash: "$2a$10$..." - username: user1 password_hash: "$2a$10$..."
# Secondary authentication (OTP) secondary: mode: totp config: issuer: "Bifrost" secrets: admin: "JBSWY3DPEHPK3PXP" user1: "GEZDGNBVGY3TQOJQ"
# How to submit OTP otp_header: "X-OTP" # Or append to password with separator otp_separator: ":" # password:123456MFA Authentication Flow
Section titled “MFA Authentication Flow”- Client sends username and password
- If OTP header is present, validate both
- If OTP is appended to password (password:123456), split and validate
- Both factors must pass for authentication to succeed
Session Management
Section titled “Session Management”Sessions for the Web UI / API can be stored in memory (default) or in Redis for persistence across restarts and sharing across replicas.
session: store: redis # "memory" (default) or "redis" duration: "24h" # Session lifetime (default 8h) max_sessions_per_user: 0 # 0 = unlimited cleanup_interval: "5m" # Memory store reap interval (ignored by Redis) redis: addr: "127.0.0.1:6379" # Required when store: redis password: "${REDIS_PASSWORD}" db: 0 key_prefix: "bifrost:session:" op_timeout: "5s"Session Stores
Section titled “Session Stores”| Store | Persistence | Scaling | Use Case |
|---|---|---|---|
memory | No | Single instance | Development, simple deployments |
redis | Yes | Multi-instance | Production, HA deployments |
Note: When
store: redisis selected,redis.addris required and the server fails to start if it is empty.
Authentication Plugin System
Section titled “Authentication Plugin System”The authentication system uses a plugin architecture. Custom authentication providers can be registered:
import "github.com/rennerdo30/bifrost-proxy/internal/auth"
func init() { auth.RegisterPlugin("custom", &CustomAuthPlugin{})}
type CustomAuthPlugin struct{}
func (p *CustomAuthPlugin) Name() string { return "custom" }
func (p *CustomAuthPlugin) Init(config map[string]interface{}) error { // Initialize plugin return nil}
func (p *CustomAuthPlugin) Authenticate(ctx context.Context, creds auth.Credentials) (*auth.User, error) { // Validate credentials return &auth.User{Username: creds.Username}, nil}
func (p *CustomAuthPlugin) Close() error { return nil}