diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0958e37 --- /dev/null +++ b/.env.example @@ -0,0 +1,41 @@ +# Database +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_DB=transit_db +POSTGRES_USER_AUTH=auth_user +POSTGRES_PASSWORD_AUTH=change_me +POSTGRES_USER_EMPLOYEE=employee_user +POSTGRES_PASSWORD_EMPLOYEE=change_me +POSTGRES_USER_TRANSIT=transit_user +POSTGRES_PASSWORD_TRANSIT=change_me +POSTGRES_USER_LOGGER=logger_user +POSTGRES_PASSWORD_LOGGER=change_me + +# Auth +JWT_SECRET=change_me_in_production +ADMIN_USER_CODES=1234,5678 +DEV_BYPASS=0 + +# Internal service-to-service authentication +INTERNAL_API_KEY=generate_strong_random_string_here + +# Error Log API URL (used by services to forward errors) +ERROR_LOG_API_URL=https://error.xtransit.testinstance.nl/error_log + +# Monitoring UI (runtime config) +AUTH_API=https://auth.xtransit.testinstance.nl +EMPLOYEE_API=https://employees.xtransit.testinstance.nl +TRANSIT_API=https://transit.xtransit.testinstance.nl +ERROR_LOG_API=https://error.xtransit.testinstance.nl +ADMIN_UI=https://admin.xtransit.testinstance.nl + +# Transit API +DEV_MOCK=0 + +# Registry +REGISTRY_HOST=gitea.testinstance.nl +IMAGE_NAMESPACE=sven/the-transit-project +IMAGE_TAG=latest + +# Portainer webhook for auto-deploy +PORTAINER_WEBHOOK_URL= \ No newline at end of file diff --git a/auth_api/index.js b/auth_api/index.js index 1dab1a6..dc64bf9 100644 --- a/auth_api/index.js +++ b/auth_api/index.js @@ -39,7 +39,10 @@ async function reportError({ partofservice, error }) { try { await fetch(ERROR_LOG_API_URL, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "x-internal-api-key": process.env.INTERNAL_API_KEY || "", + }, body: JSON.stringify({ service: "auth_api", partofservice, diff --git a/docker-compose.yml b/docker-compose.yml index 3f729cc..3daf6e7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,7 @@ services: PG_USER: ${POSTGRES_USER_LOGGER} PG_PASS: ${POSTGRES_PASSWORD_LOGGER} ERROR_LOG_API_URL: ${ERROR_LOG_API_URL:-https://error.xtransit.testinstance.nl/error_log} + INTERNAL_API_KEY: ${INTERNAL_API_KEY} restart: unless-stopped auth_api: @@ -31,6 +32,7 @@ services: ADMIN_USER_CODES: ${ADMIN_USER_CODES} DEV_BYPASS: ${DEV_BYPASS:-0} ERROR_LOG_API_URL: ${ERROR_LOG_API_URL:-https://error.xtransit.testinstance.nl/error_log} + INTERNAL_API_KEY: ${INTERNAL_API_KEY} depends_on: - error_log_api restart: unless-stopped @@ -48,6 +50,7 @@ services: PG_USER: ${POSTGRES_USER_EMPLOYEE} PG_PASS: ${POSTGRES_PASSWORD_EMPLOYEE} ERROR_LOG_API_URL: ${ERROR_LOG_API_URL:-https://error.xtransit.testinstance.nl/error_log} + INTERNAL_API_KEY: ${INTERNAL_API_KEY} depends_on: - error_log_api restart: unless-stopped @@ -67,6 +70,7 @@ services: PG_PASS: ${POSTGRES_PASSWORD_TRANSIT} DEV_MOCK: ${DEV_MOCK:-0} ERROR_LOG_API_URL: ${ERROR_LOG_API_URL:-https://error.xtransit.testinstance.nl/error_log} + INTERNAL_API_KEY: ${INTERNAL_API_KEY} depends_on: - error_log_api restart: unless-stopped diff --git a/employee_api/index.js b/employee_api/index.js index c194d75..13fd4ab 100644 --- a/employee_api/index.js +++ b/employee_api/index.js @@ -35,7 +35,10 @@ async function reportError({ partofservice, error }) { try { await fetch(ERROR_LOG_API_URL, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "x-internal-api-key": process.env.INTERNAL_API_KEY || "", + }, body: JSON.stringify({ service: "employee_api", partofservice, diff --git a/error_log_api/index.js b/error_log_api/index.js index 3c4d6ce..73e178e 100644 --- a/error_log_api/index.js +++ b/error_log_api/index.js @@ -32,6 +32,21 @@ const app = express(); app.use(express.json({ limit: "64kb" })); app.use(cors()); +const INTERNAL_API_KEY = process.env.INTERNAL_API_KEY || ""; + +function requireInternalKey(req, res, next) { + const key = req.headers["x-internal-api-key"]; + if (!INTERNAL_API_KEY) { + log("error", "INTERNAL_API_KEY not configured on server"); + return res.status(500).json({ error: "server misconfiguration" }); + } + if (!key || key !== INTERNAL_API_KEY) { + log("warn", "Unauthorized error_log attempt", { ip: req.ip, path: req.originalUrl }); + return res.status(401).json({ error: "unauthorized" }); + } + next(); +} + const ERROR_LOG_API_URL = process.env.ERROR_LOG_API_URL || "https://error.xtransit.testinstance.nl/error_log"; @@ -48,7 +63,10 @@ async function reportError({ partofservice, error }) { try { await fetch(ERROR_LOG_API_URL, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "x-internal-api-key": process.env.INTERNAL_API_KEY || "", + }, body: JSON.stringify({ service: "error_log_api", partofservice, @@ -142,7 +160,7 @@ app.get("/health", async (_req, res) => { } }); -app.post("/error_log", async (req, res) => { +app.post("/error_log", requireInternalKey, async (req, res) => { try { const service = req.body?.service; const partofservice = diff --git a/transit_api/index.js b/transit_api/index.js index 78cfef0..53550c5 100644 --- a/transit_api/index.js +++ b/transit_api/index.js @@ -38,7 +38,10 @@ async function reportError({ partofservice, error }) { try { await fetch(ERROR_LOG_API_URL, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "x-internal-api-key": process.env.INTERNAL_API_KEY || "", + }, body: JSON.stringify({ service: "transit_api", partofservice,