Implement durable OAuth MCP bridge with local Codex worker and deployment guides

This commit is contained in:
Marcos Lohmann
2026-10-14 18:42:41 -03:00
commit 29fcf73c83
39 changed files with 4130 additions and 0 deletions

4
deploy/base-images.json Normal file
View File

@@ -0,0 +1,4 @@
{
"python:3.12-slim-bookworm": "python@sha256:34386ef0cb081344d7ec1c103ba398e6e9f64e9ab3a1509accc92a4e24a07258",
"nginx:1.28-alpine": "nginx@sha256:a8b39bd9cf0f83869a2162827a0caf6137ddf759d50a171451b335cecc87d236"
}

View File

@@ -0,0 +1,19 @@
[Unit]
Description=Codex outbound MCP bridge worker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/absolute/path/to/repository
EnvironmentFile=/absolute/path/to/private/worker.env
ExecStart=/absolute/path/to/repository/.venv/bin/codex-bridge worker
Restart=on-failure
RestartSec=10
TimeoutStopSec=45
UMask=0077
# Do not add NoNewPrivileges here without testing: it can interfere with Codex's sandbox.
# This is a user unit. All approval decisions remain local.
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,36 @@
pid /tmp/nginx.pid;
error_log /dev/stderr warn;
events { worker_connections 128; }
http {
access_log off;
client_body_temp_path /tmp/client_body;
proxy_temp_path /tmp/proxy_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
server {
listen 8080;
server_name ${BRIDGE_PUBLIC_HOST};
client_max_body_size 128k;
location = /.well-known/oauth-authorization-server/realms/codex-bridge {
proxy_pass ${BRIDGE_KEYCLOAK_UPSTREAM}/realms/codex-bridge/.well-known/openid-configuration;
proxy_set_header Host ${BRIDGE_PUBLIC_HOST};
proxy_set_header X-Forwarded-Host ${BRIDGE_PUBLIC_HOST};
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
}
location /realms/codex-bridge/ {
proxy_pass ${BRIDGE_KEYCLOAK_UPSTREAM};
proxy_set_header Host ${BRIDGE_PUBLIC_HOST};
proxy_set_header X-Forwarded-Host ${BRIDGE_PUBLIC_HOST};
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
}
location /resources/ {
proxy_pass ${BRIDGE_KEYCLOAK_UPSTREAM};
proxy_set_header Host ${BRIDGE_PUBLIC_HOST};
proxy_set_header X-Forwarded-Proto https;
}
location / { return 404; }
}
}

View File

@@ -0,0 +1,32 @@
# Optional routing for an EXISTING Keycloak on the existing proxy network.
# No Keycloak restart, credentials or Docker socket required by this container.
services:
identity-route:
image: ${BRIDGE_NGINX_IMAGE:?Set a verified Nginx image digest}
user: "101:101"
entrypoint: ["/bin/sh", "-c"]
command:
- 'envsubst ''$${BRIDGE_KEYCLOAK_UPSTREAM} $${BRIDGE_PUBLIC_HOST}'' < /etc/bridge/nginx.conf.template > /tmp/nginx.conf && exec nginx -c /tmp/nginx.conf -g ''daemon off;'''
environment:
BRIDGE_KEYCLOAK_UPSTREAM: ${BRIDGE_KEYCLOAK_UPSTREAM:?Set existing Keycloak internal URL}
BRIDGE_PUBLIC_HOST: ${BRIDGE_PUBLIC_HOST}
volumes:
- ./deploy/keycloak-nginx.conf.template:/etc/bridge/nginx.conf.template:ro
networks:
- proxy
restart: unless-stopped
read_only: true
tmpfs:
- /tmp:size=32M,mode=1777
cap_drop: [ALL]
security_opt: [no-new-privileges:true]
mem_limit: 64m
pids_limit: 32
labels:
traefik.enable: "true"
traefik.docker.network: ${BRIDGE_PROXY_NETWORK}
traefik.http.routers.codex-bridge-identity.rule: Host(`${BRIDGE_PUBLIC_HOST}`) && (PathPrefix(`/realms/codex-bridge/`) || PathPrefix(`/resources/`) || Path(`/.well-known/oauth-authorization-server/realms/codex-bridge`))
traefik.http.routers.codex-bridge-identity.entrypoints: ${BRIDGE_PROXY_ENTRYPOINT:-websecure}
traefik.http.routers.codex-bridge-identity.tls: "true"
traefik.http.routers.codex-bridge-identity.tls.certresolver: ${BRIDGE_TLS_RESOLVER}
traefik.http.services.codex-bridge-identity.loadbalancer.server.port: "8080"

View File

@@ -0,0 +1,12 @@
# Optional alternative for an existing Nginx proxy. Inspect existing config first.
# Inside an existing TLS vhost whose server_name comes from deployment configuration:
location / {
proxy_pass http://codex-bridge:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 90s;
}
# The app ignores forwarded headers entirely; it uses BRIDGE_PUBLIC_URL for metadata.