diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bbf99ea --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "logs/logserver"] + path = logs/logserver + url = https://github.com/uwcirg/logserver diff --git a/base/config/keycloak/import/ltt-realm.json b/base/config/keycloak/import/ltt-realm.json index b9bb5ac..53de42b 100644 --- a/base/config/keycloak/import/ltt-realm.json +++ b/base/config/keycloak/import/ltt-realm.json @@ -238,6 +238,63 @@ "configure": true, "manage": true } + }, + { + "clientId": "logs_oauth2_proxy", + "name": "Logserver authentication", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "${KEYCLOAK_LTT_LOGS_CLIENT_SECRET}", + "redirectUris": [ + "*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "client.secret.creation.time": "1736274653", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "acr.loa.map": "{}", + "require.pushed.authorization.requests": "false", + "tls.client.certificate.bound.access.tokens": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] } ], "clientScopes": [ diff --git a/dev/keycloak.env.default b/dev/keycloak.env.default index 4bbffea..f596820 100644 --- a/dev/keycloak.env.default +++ b/dev/keycloak.env.default @@ -9,3 +9,6 @@ # KEYCLOAK_EMAIL_PASSWORD= # must match OIDC_CLIENT_SECRET in cpro.env KEYCLOAK_LTT_CPRO_CLIENT_SECRET= + +# must match OAUTH2_PROXY_CLIENT_SECRET in logs/.env +# KEYCLOAK_LTT_LOGS_CLIENT_SECRET= diff --git a/logs/.gitignore b/logs/.gitignore new file mode 100644 index 0000000..03bd412 --- /dev/null +++ b/logs/.gitignore @@ -0,0 +1 @@ +*.env diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 0000000..c3aab54 --- /dev/null +++ b/logs/README.md @@ -0,0 +1,12 @@ +# Logserver + +Sets up a central location to store and view logs + + +## Setup + +Copy the .env file default: + + cp default.env .env + +Modify each newly copied env file as necessary. Lines that are not commented-out are required, commented lines are optional. diff --git a/logs/default.env b/logs/default.env new file mode 100644 index 0000000..0cc66d5 --- /dev/null +++ b/logs/default.env @@ -0,0 +1,20 @@ +# Default docker-compose environment file (.env) +# https://docs.docker.com/compose/environment-variables/#the-env-file +# environmental variables for interpolation in docker-compose YAML files + +BASE_DOMAIN= + +# https://docs.docker.com/compose/reference/envvars/#compose_project_name +# Containers started with the below value will have their names prefixed with it +# Required on shared infrastructure +COMPOSE_PROJECT_NAME=logserver-dev + +# generate via `openssl rand -base64 32 +PGRST_JWT_SECRET= + +#POSTGRES_IMAGE_TAG= + +# must match KEYCLOAK_LTT_LOGS_CLIENT_SECRET in keycloak.env +OAUTH2_PROXY_CLIENT_SECRET= +# generate via: python3 -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())' +OAUTH2_PROXY_COOKIE_SECRET= diff --git a/logs/docker-compose.yaml b/logs/docker-compose.yaml new file mode 100644 index 0000000..3d18687 --- /dev/null +++ b/logs/docker-compose.yaml @@ -0,0 +1,93 @@ +--- +services: + postgrest: + extends: + file: ./logserver/docker-compose.yaml + service: postgrest + labels: + - traefik.enable=true + - traefik.http.routers.logserver-auth-${COMPOSE_PROJECT_NAME}.rule=Method(`GET`) && Host(`logs.${BASE_DOMAIN}`) + - traefik.http.routers.logserver-auth-${COMPOSE_PROJECT_NAME}.entrypoints=websecure + - traefik.http.routers.logserver-auth-${COMPOSE_PROJECT_NAME}.middlewares=oidc-auth-${COMPOSE_PROJECT_NAME} + - traefik.http.routers.logserver-auth-${COMPOSE_PROJECT_NAME}.tls=true + - traefik.http.routers.logserver-auth-${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt + + # POST requests to /events are authenticated by postgrest JWT + - traefik.http.routers.logserver-unauth-${COMPOSE_PROJECT_NAME}.rule=Method(`POST`) && Host(`logs.${BASE_DOMAIN}`) + - traefik.http.routers.logserver-unauth-${COMPOSE_PROJECT_NAME}.entrypoints=websecure + - traefik.http.routers.logserver-unauth-${COMPOSE_PROJECT_NAME}.tls=true + - traefik.http.routers.logserver-unauth-${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt + networks: + internal: + aliases: + - postgrest-internal-${COMPOSE_PROJECT_NAME} + ingress: + + postgres: + extends: + file: ./logserver/docker-compose.yaml + service: postgres + networks: + - internal + + auth-proxy: + image: quay.io/oauth2-proxy/oauth2-proxy:v7.3.0 + # oauth2-proxy does not EXPOSE (advertise) the ports it listens on in its docker image + expose: + - 4180 + environment: + OAUTH2_PROXY_CLIENT_ID: logs_oauth2_proxy + OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180 + OAUTH2_PROXY_REVERSE_PROXY: "true" + + # when authenticated, return a static 202 response + # https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview/#forwardauth-with-static-upstreams-configuration + OAUTH2_PROXY_UPSTREAMS: static://202 + + # needed to set X-Auth-Request-Email + OAUTH2_PROXY_SET_XAUTHREQUEST: "true" + + # general cookie settings + OAUTH2_PROXY_COOKIE_SECRET: ${OAUTH2_PROXY_COOKIE_SECRET} + OAUTH2_PROXY_COOKIE_DOMAINS: .${BASE_DOMAIN} + OAUTH2_PROXY_WHITELIST_DOMAINS: .${BASE_DOMAIN} + OAUTH2_PROXY_COOKIE_EXPIRE: 30m + OAUTH2_PROXY_COOKIE_REFRESH: 1m + OAUTH2_PROXY_EMAIL_DOMAINS: "*" + OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL: "true" + OAUTH2_PROXY_OIDC_ISSUER_URL: https://keycloak.${BASE_DOMAIN}/realms/ltt + OAUTH2_PROXY_USER_ID_CLAIM: preferred_username + + # OIDC integration settings + OAUTH2_PROXY_PROVIDER: oidc + OAUTH2_PROXY_SCOPE: openid profile email + OAUTH2_PROXY_CLIENT_SECRET: ${OAUTH2_PROXY_CLIENT_SECRET} + + labels: + - traefik.enable=true + - traefik.http.routers.auth-proxy-${COMPOSE_PROJECT_NAME}.rule=Host(`auth-proxy.${BASE_DOMAIN}`) || (PathPrefix(`/oauth2`) && (Host(`${BASE_DOMAIN}`) || HostRegexp(`{subdomain:.+}.${BASE_DOMAIN}`))) + + - traefik.http.routers.auth-proxy-${COMPOSE_PROJECT_NAME}.entrypoints=websecure + - traefik.http.routers.auth-proxy-${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt + + # https://oauth2-proxy.github.io/oauth2-proxy/configuration/integration/#forwardauth-with-static-upstreams-configuration + - traefik.http.middlewares.oidc-auth-${COMPOSE_PROJECT_NAME}.forwardAuth.address=http://auth-proxy-${COMPOSE_PROJECT_NAME}:4180/ + - traefik.http.middlewares.oidc-auth-${COMPOSE_PROJECT_NAME}.forwardAuth.trustForwardHeader=true + - traefik.http.middlewares.oidc-auth-${COMPOSE_PROJECT_NAME}.forwardAuth.authResponseHeaders=X-Auth-Request-User,X-Auth-Request-Email,X-Auth-Request-Access-Token,Authorization + networks: + ingress: + aliases: + - auth-proxy-${COMPOSE_PROJECT_NAME} + internal: + +volumes: + postgres-data: {} + +networks: + # internal network for backing services + internal: + + # ingress network + ingress: + external: true + name: external_web diff --git a/logs/logserver b/logs/logserver new file mode 160000 index 0000000..d6e485a --- /dev/null +++ b/logs/logserver @@ -0,0 +1 @@ +Subproject commit d6e485ab78421c395719cb3566d7ecc8400d25c4