Current Infrastructure Leverage
Existing Components We Can Use
- LDAP (
ldap/) - Store user public keys alongside user data - Postfix Relays (
postfix-smtp/) - Implement verification policy service - Flask API (
app/) - Key management endpoints - PostgreSQL - Revocation list, audit logs
- DNS Management - Publish user keys in TXT records
Integration Steps
1. LDAP Schema Extension
Location: ldap/bootstrap/01-users.ldif
Add attributes:
# Add to existing schema
dn: cn=authroute,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: authroute
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1
NAME 'authRoutePrimaryKey'
DESC 'User primary public key for authenticated routing'
EQUALITY caseIgnoreMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.2
NAME 'authRouteSubKey'
DESC 'User subkey for authenticated routing'
EQUALITY caseIgnoreMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.3
NAME 'authRouteKeyExpiry'
DESC 'Subkey expiration timestamp'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
Migration Script: scripts/add_authroute_ldap_schema.sh
2. Database Schema
Migration: migrations/add_authenticated_routing.sql
-- Store user key metadata
CREATE TABLE authroute_keys (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
key_type VARCHAR(20) NOT NULL, -- 'primary' or 'subkey'
key_id VARCHAR(64) UNIQUE NOT NULL,
public_key TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP,
revoked_at TIMESTAMP,
revoked_reason TEXT
);
CREATE INDEX idx_authroute_keys_user ON authroute_keys(user_id);
CREATE INDEX idx_authroute_keys_active ON authroute_keys(key_id)
WHERE revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW());
-- Audit trail for key operations
CREATE TABLE authroute_key_events (
id SERIAL PRIMARY KEY,
key_id VARCHAR(64) REFERENCES authroute_keys(key_id),
event_type VARCHAR(50) NOT NULL, -- 'created', 'used', 'rotated', 'revoked'
event_data JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
-- Verification results for compliance
CREATE TABLE authroute_verifications (
id SERIAL PRIMARY KEY,
message_id VARCHAR(255),
sender_email VARCHAR(255),
key_id VARCHAR(64),
verification_result VARCHAR(20), -- 'pass', 'fail', 'none'
failure_reason TEXT,
verified_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_authroute_verif_msg ON authroute_verifications(message_id);
CREATE INDEX idx_authroute_verif_time ON authroute_verifications(verified_at);
3. Flask API Extensions
New Module: app/authroute_api.py
from flask import Blueprint, request, jsonify
from app.models import db, AuthRouteKey
from app.api_auth import require_api_key
import nacl.signing
import nacl.encoding
authroute_bp = Blueprint('authroute', __name__)
@authroute_bp.route('/api/v1/authroute/keys', methods=['GET'])
@require_api_key
def get_user_keys():
"""Get user's current keys"""
user = request.current_user
keys = AuthRouteKey.query.filter_by(
user_id=user.id,
revoked_at=None
).all()
return jsonify([k.to_dict() for k in keys])
@authroute_bp.route('/api/v1/authroute/keys/generate', methods=['POST'])
@require_api_key
def generate_subkey():
"""Generate new subkey (client provides public key)"""
user = request.current_user
data = request.json
# Validate signature from primary key
if not verify_primary_signature(user, data):
return jsonify({'error': 'Invalid primary key signature'}), 403
# Store subkey
subkey = AuthRouteKey(
user_id=user.id,
key_type='subkey',
key_id=data['key_id'],
public_key=data['public_key'],
expires_at=data['expires_at']
)
db.session.add(subkey)
db.session.commit()
# Publish to DNS
publish_key_to_dns(user.email, subkey)
return jsonify(subkey.to_dict()), 201
@authroute_bp.route('/api/v1/authroute/keys/<key_id>/revoke', methods=['POST'])
@require_api_key
def revoke_key(key_id):
"""Revoke a key"""
user = request.current_user
key = AuthRouteKey.query.filter_by(
key_id=key_id,
user_id=user.id
).first_or_404()
key.revoked_at = datetime.now()
key.revoked_reason = request.json.get('reason', 'User requested')
db.session.commit()
# Update revocation list
update_revocation_list()
return jsonify({'status': 'revoked'})
@authroute_bp.route('/.well-known/authroute-revocations', methods=['GET'])
def get_revocations():
"""Public endpoint for revocation list"""
revoked = AuthRouteKey.query.filter(
AuthRouteKey.revoked_at.isnot(None)
).all()
return jsonify({
'version': '1.0',
'updated_at': datetime.now().isoformat(),
'revocations': [
{
'key_id': k.key_id,
'revoked_at': k.revoked_at.isoformat(),
'reason': k.revoked_reason
}
for k in revoked
]
})
Register in: app/__init__.py
from app.authroute_api import authroute_bp
app.register_blueprint(authroute_bp)
4. Postfix Integration
New File: postfix-smtp/authroute-policy.py
#!/usr/bin/env python3
"""
Postfix policy service for authenticated routing verification
"""
import sys
import socket
import json
import requests
from nacl.signing import VerifyKey
from nacl.encoding import Base64Encoder
def verify_auth_proof(sender, auth_proof):
"""Verify AUTH-PROOF from MAIL FROM"""
try:
# Parse proof
proof = json.loads(base64.b64decode(auth_proof))
# Fetch public key from DNS or API
public_key = fetch_public_key(sender, proof['key_id'])
if not public_key:
return 'DUNNO', 'Key not found'
# Check revocation
if is_revoked(proof['key_id']):
return 'REJECT', 'Key revoked'
# Verify signature
verify_key = VerifyKey(public_key, encoder=Base64Encoder)
message = f"{proof['timestamp']}:{proof['message_id']}:{sender}"
try:
verify_key.verify(message.encode(),
base64.b64decode(proof['signature']))
except:
return 'REJECT', 'Invalid signature'
# Check timestamp (5 min window)
if abs(time.time() - proof['timestamp']) > 300:
return 'REJECT', 'Timestamp out of range'
return 'PREPEND', f'Authenticated-Routing: pass key-id={proof["key_id"]}'
except Exception as e:
return 'DUNNO', f'Verification error: {str(e)}'
# Socket server for Postfix policy protocol
def policy_server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 10040))
sock.listen(5)
while True:
conn, addr = sock.accept()
handle_policy_request(conn)
if __name__ == '__main__':
policy_server()
Postfix Config: postfix-smtp/relay-a/main.cf
smtpd_recipient_restrictions =
...
check_policy_service inet:127.0.0.1:10040
...
5. Docker Compose Integration
Update: docker-compose.yml
services:
authroute-policy:
build: ./postfix-smtp/authroute-policy
container_name: authroute-policy
networks:
- msgs-network
environment:
- API_URL=http://flask-app:5000
- DNS_SERVER=1.1.1.1
depends_on:
- flask-app
- postgres
# Update postfix services
postfix-relay-a:
depends_on:
- authroute-policy
6. DNS Management
Script: scripts/publish_authroute_dns.sh
#!/bin/bash
# Publish user public keys to DNS
USER_EMAIL=$1
KEY_ID=$2
PUBLIC_KEY=$3
# Extract user and domain
USER=$(echo $USER_EMAIL | cut -d@ -f1)
DOMAIN=$(echo $USER_EMAIL | cut -d@ -f2)
# Create DNS record
cat > /tmp/authroute-${KEY_ID}.dns <<EOF
_authroute.${USER}._domainkey.${DOMAIN}. 300 IN TXT (
"v=AUTHROUTE1; "
"k=ed25519; "
"p=${PUBLIC_KEY}; "
"t=s; "
)
EOF
# Apply via your DNS provider API
# This depends on your DNS setup (Cloudflare, Route53, etc.)
Testing Plan
Phase 1: Unit Tests
# Test key generation
python -m pytest tests/test_authroute_keys.py
# Test verification logic
python -m pytest tests/test_authroute_verify.py
Phase 2: Integration Tests
# Test full flow: generate key -> sign -> verify
./test_authroute_flow.sh user@msgs.global
# Test revocation
./test_authroute_revocation.sh
Phase 3: Load Testing
# Verify performance impact
./load_test_authroute.sh 1000 # 1000 msg/sec
Rollout Strategy
Week 1-2: Infrastructure
- [ ] Add LDAP schema
- [ ] Create database tables
- [ ] Deploy revocation list endpoint
Week 3-4: API & Policy Service
- [ ] Implement Flask API endpoints
- [ ] Build Postfix policy service
- [ ] DNS automation scripts
Week 5-6: Testing
- [ ] Internal testing with test accounts
- [ ] Performance validation
- [ ] Security audit
Week 7-8: Gradual Rollout
- [ ] Beta users (opt-in)
- [ ] Monitor verification metrics
- [ ] Collect feedback
Week 9+: Full Deployment
- [ ] Enable for all users
- [ ] Document in API docs
- [ ] Publish specification
Success Metrics
- Adoption: % of users with keys generated
- Verification Rate: % of inbound mail with valid AUTH-PROOF
- Performance: Verification latency <100ms p99
- Security: Zero key compromises, revocation response <5min
- Compatibility: Zero delivery failures due to auth-routing
Documentation Updates
API Documentation
File: docs/API_AUTHENTICATION.md
- Add authroute endpoints
- Document key management flow
- Provide client examples
User Documentation
New File: docs/AUTHENTICATED_ROUTING.md
- What is authenticated routing?
- How to generate keys
- Integration with email clients
- Troubleshooting guide
Admin Documentation
New File: docs/AUTHROUTE_ADMIN.md
- Policy configuration
- DNS setup
- Monitoring and alerts
- Emergency revocation procedures