Modular authenticating reverse proxy
Project description
Modular Authenticating Reverse Proxy
A flexible, modular reverse proxy with pluggable authentication mechanisms. Designed to sit behind an internet-facing web server like Nginx or Caddy, this proxy authenticates requests before forwarding them to your backend applications.
Features
- Modular Authentication: Pluggable authentication system with support for external plugins
- Multiple Authentication Methods: Support for using multiple auth methods per path
- Flexible Configuration: Simple YAML-based configuration
- Path-Based Rules: Configure which paths require authentication and which don't
- Regular Expression Matching: Powerful path matching with regex support
- Connection Flexibility: Support for both TCP and Unix sockets
- Header Forwarding: Authentication information is forwarded to the backend via HTTP headers
- Lightweight: Focused on authentication, not trying to replace your main web server
Installation
# Install from PyPI
pip install python-auth-proxy
This will set you up with the auth-proxy command, as well as the auth-proxy-validate tool for path rule validation. Out of the box, the proxy comes with a single built-in authentication plugin: HTTP Basic Authentication (basic).
Quick Start
- Create a configuration file:
# config.yaml
listen:
host: 127.0.0.1
port: 8000
backend:
scheme: http
host: localhost
port: 3000
auth_plugins:
basic:
users:
admin: password123
user1: secret456
auth:
default_plugins: [basic]
default_behavior: "authenticated"
paths:
- path: "^/api/.*$"
regex: true
authenticate: true
plugins: [basic]
- path: "^/public/.*$"
regex: true
authenticate: false
- path: "/health"
authenticate: false
- Run the proxy:
auth-proxy -c config.yaml
Configuration Options
Listener Configuration
listen:
# TCP socket
host: 127.0.0.1 # Optional, defaults to 127.0.0.1
port: 8000
# Or Unix socket
socket: /tmp/auth_proxy.sock
Backend Configuration
backend:
# TCP socket
scheme: http # Optional, defaults to http
host: localhost
port: 3000
# Or Unix socket
socket: /tmp/app.sock
Authentication Plugins
You can configure multiple authentication plugins and use them for different paths:
# Define all available plugins and their configurations
auth_plugins:
# Standard plugin instance
oidc:
issuer: https://your-oidc-provider.com
client_id: your-client-id
client_secret: your-client-secret
# Named instances of the basic auth plugin
user-basic:
type: basic
users:
user1: password123
user2: secret456
admin-basic:
type: basic
users:
admin: admin-password
superuser: super-secret
Global Authentication Settings
auth:
# Default plugins to use if not specified in a path
default_plugins: [oidc, jwt]
# Default authentication mode: "any" or "all"
default_mode: "any"
# Default behavior for paths not matching any rule: "authenticated" or "unauthenticated"
default_behavior: "authenticated"
Path Rules and Precedence
Path rules are processed in the order they appear in the configuration. The first matching rule takes precedence:
paths:
# More specific rule first
- path: "^/api/public/.*$"
regex: true
authenticate: false
# More general rule second
- path: "^/api/.*$"
regex: true
authenticate: true
plugins: [oidc]
In this example, paths starting with /api/public/ will not require authentication, while all other API paths will require OIDC authentication.
Regular Expression Path Matching
For more flexible path matching, you can use regular expressions:
paths:
# Match all paths that start with /api/ followed by anything
- path: "^/api/.*$"
regex: true
authenticate: true
plugins: [jwt]
# Match specific pattern
- path: "^/users/[0-9]+/profile$"
regex: true
authenticate: true
plugins: [oidc]
When using regex paths, set regex: true in the path rule.
Multiple Authentication Methods Per Path
You can specify multiple authentication plugins for a path:
paths:
# Allow access if either OIDC or JWT authentication succeeds
- path: "^/api/.*$"
regex: true
authenticate: true
plugins: [oidc, jwt]
mode: "any" # "any" (default) or "all"
# Require both OIDC and Basic authentication to succeed
- path: "^/secure/.*$"
regex: true
authenticate: true
plugins: [oidc, basic]
mode: "all"
The mode parameter determines how multiple plugins are evaluated:
any: The request is authenticated if any plugin succeeds (OR logic)all: The request is authenticated only if all plugins succeed (AND logic)
Named Plugin Instances
You can create multiple instances of the same plugin type with different configurations:
auth_plugins:
# Simple case - plugin name matches plugin type
oidc:
issuer: https://accounts.google.com
client_id: your-client-id
client_secret: your-client-secret
# Named instances must specify their type
user-basic:
type: basic
users:
user1: password123
user2: secret456
admin-basic:
type: basic
users:
admin: admin-password
superuser: super-secret
This allows you to have multiple configurations of the same plugin type for different purposes.
Default Behavior
You can specify the default behavior for paths that don't match any rule:
auth:
default_behavior: "authenticated" # or "unauthenticated"
This setting determines whether paths not matching any rule require authentication by default:
authenticated: Paths not matching any rule will require authentication using the default pluginsunauthenticated: Paths not matching any rule will not require authentication
If not specified, the default is authenticated.
Available Authentication Plugins
The proxy currently comes with one built-in authentication plugin:
- basic: HTTP Basic Authentication
To list all available authentication plugins (including installed third-party plugins):
auth-proxy --list-plugins
Path Rule Validation
To help understand which rule would match a given URL path, use the validation tool:
auth-proxy-validate -c config.yaml /api/v1/users
Example output:
Path: /api/v1/users
Matching rule #3: ^/api/v1/.*$
Authentication required: True
Authentication plugins: oidc, user-basic
Authentication mode: any
Full matching rule:
path: ^/api/v1/.*$
regex: True
authenticate: True
plugins: ['oidc', 'user-basic']
mode: any
This tool is useful for:
- Testing your path rules
- Debugging authentication issues
- Understanding rule precedence
Creating a Custom Authentication Plugin
You can create your own authentication plugins for auth-proxy:
- Create a new Python package for your plugin with a
pyproject.toml:
# pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "auth-proxy-my-plugin"
version = "0.1.0"
description = "My custom authentication plugin for auth-proxy"
requires-python = ">=3.8"
dependencies = [
"auth-proxy",
]
[project.entry-points."auth_proxy.plugins"]
my-plugin = "auth_proxy_my_plugin.plugin:MyAuthPlugin"
- Implement your plugin class:
# auth_proxy_my_plugin/plugin.py
from typing import Dict, Any
from auth_proxy.auth_plugins.base import AuthPlugin
class MyAuthPlugin(AuthPlugin):
def __init__(self, config: Dict[str, Any]):
super().__init__(config)
# Initialize with your config parameters
self.some_setting = config.get('some_setting')
def authenticate(self, request_headers: Dict[str, str], path: str) -> bool:
# Implement your authentication logic
# Return True if authenticated, False otherwise
return True
def get_auth_headers(self, request_headers: Dict[str, str], path: str) -> Dict[str, str]:
# Return headers to add to the proxied request
return {'X-Auth-User': 'example-user'}
- Install your plugin:
pip install -e .
- Configure the proxy to use your plugin:
auth_plugins:
my-plugin:
some_setting: some_value
paths:
- path: "^/api/.*$"
regex: true
authenticate: true
plugins: [my-plugin]
Plugin API
Authentication plugins must implement the AuthPlugin base class with two methods:
authenticate(request_headers, path)
Determines if the request is authenticated.
- Parameters:
request_headers(Dict[str, str]): The HTTP headers from the incoming requestpath(str): The request path
- Returns:
bool: True if the request is authenticated, False otherwise
get_auth_headers(request_headers, path)
Returns headers to add to the proxied request.
- Parameters:
request_headers(Dict[str, str]): The HTTP headers from the incoming requestpath(str): The request path
- Returns:
Dict[str, str]: Headers to add to the proxied request
Full Configuration Example
# config.yaml
listen:
host: 127.0.0.1
port: 8000
backend:
scheme: http
host: localhost
port: 3000
# Define all available plugins and their configurations
auth_plugins:
oidc:
issuer: https://accounts.google.com
client_id: your-client-id
client_secret: your-client-secret
user-basic:
type: basic
users:
user1: password123
user2: secret456
admin-basic:
type: basic
users:
admin: admin-password
superuser: super-secret
web-jwt:
type: jwt
secret: your-web-secret-key
algorithm: HS256
audience: web-app
mobile-jwt:
type: jwt
secret: your-mobile-secret-key
algorithm: HS256
audience: mobile-app
# Global authentication settings
auth:
default_plugins: [oidc]
default_mode: "any"
default_behavior: "authenticated"
# Path rules with plugin selection (processed in order)
paths:
# Specific public API endpoints (must come before general API rule)
- path: "^/api/v1/public/.*$"
regex: true
authenticate: false
# Admin API endpoints (specific before general)
- path: "^/api/v1/admin/.*$"
regex: true
authenticate: true
plugins: [admin-basic]
# General API endpoints (after more specific rules)
- path: "^/api/v1/.*$"
regex: true
authenticate: true
plugins: [oidc, user-basic]
mode: "any"
# Mobile API endpoints
- path: "^/api/mobile/.*$"
regex: true
authenticate: true
plugins: [mobile-jwt]
# Admin dashboard
- path: "^/admin/.*$"
regex: true
authenticate: true
plugins: [admin-basic]
# Super secure endpoints - require both OIDC and admin basic auth
- path: "^/secure/.*$"
regex: true
authenticate: true
plugins: [oidc, admin-basic]
mode: "all"
# Public resources
- path: "^/public/.*$"
regex: true
authenticate: false
# Static assets
- path: "^/assets/.*$"
regex: true
authenticate: false
# Health check
- path: "/health"
authenticate: false
# Metrics endpoint
- path: "/metrics"
authenticate: true
plugins: [admin-basic]
Deployment
Docker
The proxy can be easily deployed using Docker:
# Pull the image
docker pull git.private.coffee/kumi/auth-proxy
# Run with a config file
docker run -v $(pwd)/config.yaml:/config/config.yaml -p 8000:8000 git.private.coffee/kumi/auth-proxy
Or using Docker Compose:
# docker-compose.yml
version: "3"
services:
auth-proxy:
image: kumitterer/auth-proxy
ports:
- "8000:8000"
volumes:
- ./config.yaml:/config/config.yaml
Behind Nginx or Caddy
The auth-proxy is designed to run behind a reverse proxy like Nginx or Caddy.
Example Nginx configuration:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $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-Proto $scheme;
}
}
Example Caddy configuration:
example.com {
reverse_proxy localhost:8000
}
Command Line Options
usage: auth-proxy [-h] [-c CONFIG] [-v] [--list-plugins]
Modular Authenticating Reverse Proxy
optional arguments:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Path to config file (default: config.yaml)
-v, --verbose Enable verbose logging
--list-plugins List available authentication plugins
Security Considerations
- The proxy does not handle TLS termination - use a front-facing web server for this
- Store sensitive configuration (like secrets) securely
- Review the authentication plugins you use for security best practices
- Consider using environment variables for secrets in production
- Always put more specific path rules before more general ones to avoid security bypasses
Development
Prerequisites
- Python 3.8 or higher
- pip
Setting up the development environment
# Clone the repository
git clone https://git.private.coffee/kumi/auth-proxy.git
cd auth-proxy
# Create a virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install in development mode
pip install -e .
# Install development dependencies
pip install pytest black isort mypy
Running tests
pytest
Code formatting and linting
# Format code
black auth_proxy tests
# Sort imports
isort auth_proxy tests
# Type checking
mypy auth_proxy
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file python_auth_proxy-0.2.0.tar.gz.
File metadata
- Download URL: python_auth_proxy-0.2.0.tar.gz
- Upload date:
- Size: 23.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5a99982b91c66bccfb84b5ebb9c12729cd7e376a71d1c0456f17c5afbf0f08b
|
|
| MD5 |
df30fe4e0d4970f8bd063ab03c824358
|
|
| BLAKE2b-256 |
cf8a9bcbe861e7f96c675be7c3af243d4b3d8185b859a1628bc0c1de6b06ab01
|
File details
Details for the file python_auth_proxy-0.2.0-py3-none-any.whl.
File metadata
- Download URL: python_auth_proxy-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
623631dbc4da456cf3e95cd273d9bc3b1f2bf5ff510104ed22669c7d1b191291
|
|
| MD5 |
5187585a6aac327a5b40871826196f11
|
|
| BLAKE2b-256 |
332831e397ebe40eeccb7dc9cc0f6e2c23aa8be20fa0cc05f15103efedb7ca2a
|