From 45f4c9859d7ca8edcfac71d74df1de4b35eee814 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 15 May 2026 14:23:09 -0700 Subject: [PATCH] Add Authelia auth gateway, portal landing page, and subpath routing Adds Authelia (forward-auth) and nginx portal container for single-endpoint authenticated access via Caddy reverse proxy. Configures Grafana auth proxy for header-based auto-login. Updates Vue UI base paths and API routes for /exabgp/ and /traffic/ subpath serving. Adds traffic-gen responder container on dedicated Docker network. Co-Authored-By: Claude Opus 4.6 (1M context) --- docker-compose.yml | 58 ++++++++++++++++++- exabgp-ui/src/api.js | 5 +- exabgp-ui/vite.config.js | 1 + portal/index.html | 106 ++++++++++++++++++++++++++++++++++ traffic-gen-ui/nginx.conf | 6 ++ traffic-gen-ui/src/api.js | 6 +- traffic-gen-ui/vite.config.js | 1 + 7 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 portal/index.html diff --git a/docker-compose.yml b/docker-compose.yml index a5d8ad5..9cdf826 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -92,7 +92,13 @@ services: - ${OBMP_DATA_ROOT}/grafana/provisioning:/etc/grafana/provisioning/ environment: - GF_SECURITY_ADMIN_PASSWORD=openbmp - - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ENABLED=false + - GF_SERVER_ROOT_URL=https://bmp.apodacalab.com/grafana/ + - GF_SERVER_SERVE_FROM_SUB_PATH=true + - GF_AUTH_PROXY_ENABLED=true + - GF_AUTH_PROXY_HEADER_NAME=Remote-User + - GF_AUTH_PROXY_HEADER_PROPERTY=username + - GF_AUTH_PROXY_AUTO_SIGN_UP=true - GF_USERS_HOME_PAGE=d/obmp-home/obmp-home - GF_INSTALL_PLUGINS=agenty-flowcharting-panel,grafana-piechart-panel,grafana-worldmap-panel,grafana-simple-json-datasource,vonage-status-panel @@ -275,8 +281,9 @@ services: - NET_RAW - NET_ADMIN environment: - - TRAFFIC_GEN_API_PORT=5051 + - TRAFFIC_GEN_PORT=5051 - TRAFFIC_GEN_MODE=sender + - RESPONDER_URL=http://172.30.0.10:5053 traffic-gen-ui: restart: unless-stopped @@ -287,6 +294,26 @@ services: network_mode: host # Serves on port 5002 (host network, defined in nginx.conf) + traffic-gen-responder: + restart: unless-stopped + container_name: obmp-traffic-gen-responder + build: + context: ./traffic-gen + dockerfile: Dockerfile + cap_add: + - NET_RAW + - NET_ADMIN + environment: + - TRAFFIC_GEN_PORT=5053 + - TRAFFIC_GEN_MODE=responder + - TRAFFIC_GEN_RESPONDER_MODE=echo + - TRAFFIC_GEN_INTERFACE=eth0 + networks: + traffic-test-net: + ipv4_address: 172.30.0.10 + ports: + - "5053:5053" + whois: restart: unless-stopped container_name: obmp-whois @@ -305,3 +332,30 @@ services: - POSTGRES_DB=openbmp - POSTGRES_HOST=obmp-psql - POSTGRES_PORT=5432 + + authelia: + restart: unless-stopped + container_name: obmp-authelia + image: authelia/authelia:4.38 + ports: + - "9091:9091" + volumes: + - ${OBMP_DATA_ROOT}/authelia:/config + environment: + - TZ=UTC + + portal: + restart: unless-stopped + container_name: obmp-portal + image: nginx:alpine + ports: + - "8080:80" + volumes: + - ./portal:/usr/share/nginx/html:ro + +networks: + traffic-test-net: + driver: bridge + ipam: + config: + - subnet: 172.30.0.0/24 diff --git a/exabgp-ui/src/api.js b/exabgp-ui/src/api.js index 23b33b8..41a0465 100644 --- a/exabgp-ui/src/api.js +++ b/exabgp-ui/src/api.js @@ -1,4 +1,4 @@ -const BASE = '/api' +const BASE = '/exabgp/api' async function req(method, path, body) { const opts = { method, headers: { 'Content-Type': 'application/json' } } @@ -18,4 +18,7 @@ export const api = { announce: payload => req('POST', '/announce', payload), withdraw: prefixes => req('POST', '/withdraw', { prefixes }), withdrawAll: () => req('POST', '/withdraw/all'), + fullTableStart: (count, batchSize) => req('POST', '/full-table/start', { count, batch_size: batchSize }), + fullTableStatus: () => req('GET', '/full-table/status'), + fullTableStop: () => req('POST', '/full-table/stop'), } diff --git a/exabgp-ui/vite.config.js b/exabgp-ui/vite.config.js index 71b1406..d689a9e 100644 --- a/exabgp-ui/vite.config.js +++ b/exabgp-ui/vite.config.js @@ -2,6 +2,7 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ + base: '/exabgp/', plugins: [vue()], server: { proxy: { diff --git a/portal/index.html b/portal/index.html new file mode 100644 index 0000000..bff238c --- /dev/null +++ b/portal/index.html @@ -0,0 +1,106 @@ + + + + + + OpenBMP Lab Portal + + + +
+

OpenBMP Lab

+

BGP Monitoring Protocol · Route Analysis · Telemetry

+
+ + + + + + diff --git a/traffic-gen-ui/nginx.conf b/traffic-gen-ui/nginx.conf index ab4e9f3..0e6e20c 100644 --- a/traffic-gen-ui/nginx.conf +++ b/traffic-gen-ui/nginx.conf @@ -11,5 +11,11 @@ server { location / { try_files $uri $uri/ /index.html; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; } } diff --git a/traffic-gen-ui/src/api.js b/traffic-gen-ui/src/api.js index 09f1a01..a0ab9c2 100644 --- a/traffic-gen-ui/src/api.js +++ b/traffic-gen-ui/src/api.js @@ -1,4 +1,4 @@ -const BASE = '/api' +const BASE = '/traffic/api' async function req(method, path, body) { const opts = { method, headers: { 'Content-Type': 'application/json' } } @@ -12,6 +12,7 @@ export const api = { health: () => req('GET', '/healthz'), interfaces: () => req('GET', '/interfaces'), mode: () => req('GET', '/mode'), + setMode: (mode) => req('POST', '/mode', { mode }), // Flows flows: () => req('GET', '/flows'), @@ -38,6 +39,9 @@ export const api = { // Stats statsHistory: () => req('GET', '/stats/history'), + // Ping + ping: (target, count) => req('POST', '/ping', { target, count: count || 5 }), + // Responder responderStats: () => req('GET', '/responder/stats'), responderReset: () => req('POST', '/responder/reset'), diff --git a/traffic-gen-ui/vite.config.js b/traffic-gen-ui/vite.config.js index 4c9535b..ea23b96 100644 --- a/traffic-gen-ui/vite.config.js +++ b/traffic-gen-ui/vite.config.js @@ -2,6 +2,7 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ + base: '/traffic/', plugins: [vue()], server: { proxy: {