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
- Authentication Redirection: Support for authentication flows that require redirection
- Plugin-Specific Paths: Plugins can register their own paths for callbacks and other needs
- 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
# Or install with a specific authentication plugin
pip install python-auth-proxy auth-proxy-jwt
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
socket-chmod: 660 # Optional, sets socket permissions (octal format)
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
redirect_uri: https://your-app.com/auth/oidc/callback
callback_path: /auth/oidc/callback
# 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://your-oidc-provider.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.
Authentication with Redirection
Some authentication methods like OIDC require redirecting the user to an external login page. The proxy supports this flow:
OIDC Configuration
auth_plugins:
oidc:
issuer: https://your-oidc-provider.com
client_id: your-client-id
client_secret: your-client-secret
redirect_uri: https://your-app.com/auth/oidc/callback
callback_path: /auth/oidc/callback
scope: openid profile email
When a user tries to access a protected resource without authentication, they will be redirected to the identity provider's login page. After successful login, they will be redirected back to your application with an authorization code, which the proxy will exchange for tokens.
Plugin-Specific Paths
Authentication plugins can register their own paths to handle special endpoints like callbacks. These paths are automatically registered when the plugin is loaded:
OIDC Plugin Paths
The OIDC plugin registers a callback path to handle the redirect from the identity provider:
auth_plugins:
oidc:
issuer: https://your-oidc-provider.com
client_id: your-client-id
client_secret: your-client-secret
redirect_uri: https://your-app.com/auth/oidc/callback
callback_path: /auth/oidc/callback # This path will be handled by the OIDC plugin
You don't need to explicitly configure these paths in the paths section - they are automatically registered and handled by the plugin.
Multiple Authentication Plugins
When using multiple authentication plugins, each plugin can register its own paths:
auth_plugins:
google-oidc:
type: oidc
issuer: https://accounts.google.com
client_id: google-client-id
client_secret: google-client-secret
redirect_uri: https://your-app.com/auth/google/callback
callback_path: /auth/google/callback
github-oidc:
type: oidc
issuer: https://github.com
client_id: github-client-id
client_secret: github-client-secret
redirect_uri: https://your-app.com/auth/github/callback
callback_path: /auth/github/callback
In this example, each OIDC plugin registers its own callback path.
Available Authentication Plugins
The proxy currently comes with one built-in authentication plugin:
- basic: HTTP Basic Authentication
Other plugins are available as separate packages:
- auth-proxy-jwt: JSON Web Token (JWT) authentication - auth-proxy-jwt
- auth-proxy-oidc: OpenID Connect (OIDC) authentication - auth-proxy-oidc
To list all installed authentication plugins (including 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 for a regular path:
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
Example output for a plugin path:
Path: /auth/oidc/callback
Matching plugin path: /auth/oidc/callback
Plugin: oidc
Description: OIDC callback endpoint for oidc
Authentication required: False
Full plugin path info:
plugin: oidc
regex: False
authenticate: False
description: OIDC callback endpoint for oidc
This tool is useful for:
- Testing your path rules
- Debugging authentication issues
- Understanding rule precedence
- Verifying plugin path configuration
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 python-auth-proxy"
requires-python = ">=3.8"
dependencies = [
"python-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, List, Optional, Tuple
from auth_proxy.auth_plugins.base import AuthPlugin, AuthResult, PluginPath
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) -> AuthResult:
# Implement your authentication logic
# Return an AuthResult object
return AuthResult(
authenticated=True,
headers={'X-Auth-User': 'example-user'}
)
def get_plugin_paths(self) -> List[PluginPath]:
# Register any paths your plugin needs to handle
return [
PluginPath(
path='/auth/my-plugin/callback',
regex=False,
authenticate=False,
description="My plugin callback endpoint"
)
]
def handle_plugin_path(self, path: str, request_headers: Dict[str, str],
request_body: bytes) -> Optional[Tuple[int, Dict[str, str], bytes]]:
# Handle requests to your plugin's paths
if path.startswith('/auth/my-plugin/callback'):
return (
200,
{'Content-Type': 'application/json'},
b'{"status": "success"}'
)
return None
- 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:
authenticate(request_headers, path)
Authenticates a request and returns an AuthResult.
- Parameters:
request_headers(Dict[str, str]): The HTTP headers from the incoming requestpath(str): The request path
- Returns:
AuthResult: The result of the authentication attempt
get_auth_headers(request_headers, path)
Returns headers to add to the authenticated 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
get_plugin_paths()
Returns a list of paths that the plugin needs to handle.
- Returns:
List[PluginPath]: List of paths that this plugin needs to handle
handle_plugin_path(path, request_headers, request_body)
Handles a request to a plugin-specific path.
- Parameters:
path(str): The request pathrequest_headers(Dict[str, str]): Headers from the incoming requestrequest_body(bytes): Body from the incoming request
- Returns:
Optional[Tuple[int, Dict[str, str], bytes]]: If not None, a tuple of (status_code, response_headers, response_body)
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://your-oidc-provider.com
client_id: your-client-id
client_secret: your-client-secret
redirect_uri: https://your-app.com/auth/oidc/callback
callback_path: /auth/oidc/callback
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: git.private.coffee/kumi/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 -r requirements-dev.txt
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.3.tar.gz.
File metadata
- Download URL: python_auth_proxy-0.2.3.tar.gz
- Upload date:
- Size: 24.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98fe709dc6bb8b0ea96f04213f42395dad701e3ac0299aa6325ef6b3d3387de9
|
|
| MD5 |
4e6412de3dcccf93ce5b06fd03247b9b
|
|
| BLAKE2b-256 |
660d66e84fc9a56e06bd64cc4bec03f3d6c01c00a9ac64ba539bbcdb64b96cb8
|
File details
Details for the file python_auth_proxy-0.2.3-py3-none-any.whl.
File metadata
- Download URL: python_auth_proxy-0.2.3-py3-none-any.whl
- Upload date:
- Size: 19.8 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 |
f08eaccb01169f80ab5cb01edc727e5e971b021f3cde6c4ebd28db31163197e0
|
|
| MD5 |
d2bd154e5c5074ceb724bfbe4ec004b8
|
|
| BLAKE2b-256 |
06f5c91f6393102614a0073e7e7d22fa6da7d436048c61e515e0d4ae9cd67183
|