-
-
Save magicalyak/0d0606f88bf808da0afa9c7fe54d6138 to your computer and use it in GitHub Desktop.
OIDC Sync
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
server { | |
listen 8080; | |
server_name _; | |
access_log off; | |
location /api { | |
api write=off; | |
} | |
location = /dashboard.html { | |
root /usr/share/nginx/html; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
limit_req_zone $uri zone=mylimit:10m rate=10r/s; | |
# Custom log format to include the 'sub' claim in the REMOTE_USER field | |
log_format main_jwt '$remote_addr - $jwt_claim_sub [$time_local] "$request" $status ' | |
'$body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"'; | |
# JavaScript code for OpenID Connect | |
js_include conf.d/openid_connect.js; | |
js_set $requestid_hash hashRequestId; | |
auth_jwt_claim_set $jwt_audience aud; # In case aud is an array | |
keyval_zone zone=opaque_sessions:1M state=conf.d/opaque_sessions.json timeout=1h; # CHANGE timeout to JWT/exp validity period | |
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h; # CHANGE timeout to refresh validity period | |
keyval $cookie_auth_token $session_jwt zone=opaque_sessions; # Exchange cookie for JWT | |
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token | |
keyval $request_id $new_session zone=opaque_sessions; # For initial session creation | |
keyval $request_id $new_refresh zone=refresh_tokens; # " | |
map $refresh_token $no_refresh { | |
"" 1; # Before login | |
"-" 1; # After logout | |
default 0; | |
} | |
# JWK Set will be fetched from $oidc_jwks_uri and cached here - ensure writable by nginx user | |
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:64k max_size=1m; | |
# The frontend server - reverse proxy with OpenID Connect authentication | |
# | |
resolver 8.8.8.8 valid=30s; | |
upstream nodejs { | |
server <REMOVED>:3000; | |
zone nodejs 64k; | |
} | |
upstream apigw { | |
server <REMOVED>:443 resolve; | |
zone apigw 64k; | |
} | |
upstream s3 { | |
server <REMOVED> resolve; | |
zone s3 64k; | |
} | |
upstream mirror { | |
server <REMOVED>; | |
zone mirror 64k; | |
} | |
server { | |
listen 80; | |
server_name <REMOVED>; | |
status_zone <REMOVED>m; | |
location / { | |
status_zone Unsecure_redirect; | |
return 302 https://$host$request_uri; | |
} | |
} | |
server { | |
listen 443 ssl http2 ; | |
server_name <REMOVED>; # managed by Certbot | |
status_zone <REMOVED>m; | |
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot | |
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot | |
ssl_certificate /etc/ssl/nginx/www.crt; | |
ssl_certificate_key /etc/ssl/nginx/www.key; | |
mirror /mirror; | |
limit_req zone=mylimit burst=20 delay=8; | |
location = /mirror { | |
internal; | |
proxy_pass http://mirror$request_uri; | |
} | |
location /api/mangos { | |
add_header 'Access-Control-Allow-Origin' '*'; | |
proxy_set_header Host <REMOVED>; | |
proxy_http_version 1.1; | |
proxy_pass https://apigw; | |
status_zone mango_api; | |
} | |
location /api/articles { | |
status_zone article_api; | |
proxy_http_version 1.1; | |
proxy_set_header Host $host; | |
proxy_pass http://nodejs; | |
} | |
error_page 500 502 503 504 /50x.html; | |
location = /50x.html { | |
root /usr/share/nginx/html; | |
} | |
location / { | |
status_zone unsecure; | |
proxy_http_version 1.1; | |
resolver 8.8.8.8; | |
proxy_set_header Host <REMOVED>; | |
proxy_pass http://s3; | |
proxy_intercept_errors on; | |
proxy_redirect off; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_hide_header x-amz-id-2; | |
proxy_hide_header x-amz-request-id; | |
} | |
include conf.d/openid_connect.server_conf; # Authorization code flow and Relying Party processing | |
# OpenID Connect Provider (IdP) configuration | |
resolver 8.8.8.8; # For DNS lookup of IdP endpoints; | |
subrequest_output_buffer_size 32k; # To fit a complete tokenset response | |
set $oidc_jwt_keyfile "https://login.microsoftonline.com/<REMOVED>/discovery/v2.0/keys"; # URL when using 'auth_jwt_key_request' | |
set $oidc_logout_redirect "/_logout"; # Where to send browser after requesting /logout location | |
set $oidc_authz_endpoint "https://login.microsoftonline.com/<REMOVED>/oauth2/v2.0/authorize"; | |
set $oidc_token_endpoint "https://login.microsoftonline.com/<REMOVED>/oauth2/v2.0/token"; | |
set $oidc_client "<REMOVED>"; | |
set $oidc_client_secret "<REMOVED>"; | |
set $oidc_hmac_key "<REMOVED>"; # This should be unique for every NGINX instance/cluster | |
location /secure/ { | |
status_zone secure; | |
proxy_http_version 1.1; | |
auth_jwt "" token=$session_jwt; | |
auth_jwt_key_request /_jwks_uri; # Enable when using URL | |
error_page 401 = @oidc_auth; | |
proxy_set_header username $jwt_claim_sub; | |
access_log /var/log/nginx/access.log main_jwt; | |
rewrite ^([^.]*[^/])$ $1/ permanent; | |
proxy_pass http://s3/secure/; | |
proxy_set_header Host citi-demo1.s3-website-us-east-1.amazonaws.com; | |
proxy_intercept_errors on; | |
proxy_redirect off; | |
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_hide_header x-amz-id-2; | |
proxy_hide_header x-amz-request-id; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
load_module modules/ngx_http_js_module.so; | |
user nginx; | |
worker_processes auto; | |
error_log /var/log/nginx/error.log notice; | |
pid /var/run/nginx.pid; | |
events { | |
worker_connections 1024; | |
} | |
http { | |
include /etc/nginx/mime.types; | |
default_type application/octet-stream; | |
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' | |
'$status $body_bytes_sent "$http_referer" ' | |
'"$http_user_agent" "$http_x_forwarded_for"'; | |
access_log /var/log/nginx/access.log main; | |
sendfile on; | |
#tcp_nopush on; | |
keepalive_timeout 65; | |
#gzip on; | |
include /etc/nginx/conf.d/*.conf; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# First command is adding the sync directive to the keyavl zone | |
# Second command is adding the zone_sync listener and directive that lets NGINX sync the state of the keyval zone. It uses service discovery to find the other NGINX instances based off of a headless service in kubernetes for the nginx-ingress pods. | |
# Third command creates the headless service with port 12345 | |
# Fourth and Fifth apply the service and the nginx-config that includes the stream config for zone_sync and the keyval zones. | |
# sed -i 's/\(.*keyval_zone.*\);/\1 sync;/g' nginx-config.yaml.orig | |
# sed -i 's/\(^data:.*\)/\1 \n stream-snippets:\n resolver kube-dns.kube-system.svc.cluster.local valid=5s;\n\n server {\n listen 0.0.0.0:12345;\n zone_sync;\n zone_sync_server nginx-ingress-headless.nginx-ingress.svc.cluster.local:12345 resolve;\n }\n/g' nginx-config.yaml.orig | |
# cat << EOF >> headless.yaml | |
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: nginx-ingress-headless | |
namespace: nginx-ingress | |
spec: | |
clusterIP: None | |
ports: | |
- port: 12345 | |
targetPort: 12345 | |
protocol: TCP | |
name: zonesync | |
selector: | |
app: nginx-ingress | |
EOF | |
# kubectl apply -f headless.yaml | |
# kubectl apply -f nginx-config.yaml.orig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
location @oidc_auth { | |
if ($no_refresh) { | |
# No refresh token so redirect this request to the OpenID Connect identity provider login | |
# page for this server{} using authorization code flow (nonce sent to IdP is hash of $request_id) | |
add_header Set-Cookie "auth_nonce=$request_id; Path=/; HttpOnly;"; # Random value | |
add_header Set-Cookie "auth_redir=$request_uri; Path=/; HttpOnly;"; # So we know where to come back to | |
# This URL should work for most OpenID Connect providers. | |
# Adjust the scope or state values as required (offline_access enables refresh tokens) | |
return 302 "$oidc_authz_endpoint?response_type=code&scope=openid+profile+email+offline_access&client_id=$oidc_client&state=0&redirect_uri=$scheme://$host:$server_port$redir_location&nonce=$requestid_hash"; | |
} | |
# We have a refresh token so perform refresh operation | |
js_content oidcRefreshRequest; | |
# Catch errors from oidcRefreshRequest() | |
# 500 = token validation error, 502 = error from IdP, 504 = IdP timeout | |
error_page 500 502 504 @oidc_error; | |
access_log /var/log/nginx/oidc_auth.log main; | |
error_log /var/log/nginx/oidc_error.log debug; | |
} | |
location = /_jwks_uri { | |
internal; | |
proxy_cache jwk; # Cache the JWK Set recieved from IdP | |
proxy_cache_valid 200 12h; # How long to consider keys "fresh" | |
proxy_cache_use_stale error timeout updating; # Use old JWK Set if cannot reach IdP | |
proxy_ignore_headers Cache-Control Expires Set-Cookie; # Does not influence caching | |
proxy_method GET; # In case client request was non-GET | |
proxy_pass $oidc_jwt_keyfile; # Expecting to find a URI here | |
} | |
set $redir_location "/_codexch"; | |
location = /_codexch { | |
# This is where the IdP will send the authorization code after user login | |
js_content oidcCodeExchange; # JavaScript function to obtain JWT and issue cookie | |
add_header Set-Cookie "auth_token=$request_id; Path=/; HttpOnly;"; | |
# Catch errors from oidcCodeExchange() | |
# 500 = token validation error, 502 = error from IdP, 504 = IdP timeout | |
error_page 500 502 504 @oidc_error; | |
access_log /var/log/nginx/oidc_auth.log main_jwt; | |
error_log /var/log/nginx/oidc_error.log debug; | |
} | |
location = /_token { | |
# This location is called by oidcCodeExchange(). We use the proxy_ directives | |
# to construct the OpenID Connect token request, as per: | |
# http://openid.net/specs/openid-connect-core-1_0.html#TokenRequest | |
internal; | |
gunzip on; # Decompress if necessary | |
proxy_set_header Content-Type "application/x-www-form-urlencoded"; | |
proxy_method POST; | |
proxy_set_body "grant_type=authorization_code&code=$arg_code&client_id=$oidc_client&client_secret=$oidc_client_secret&redirect_uri=$scheme://$host:$server_port$redir_location"; | |
proxy_pass $oidc_token_endpoint; | |
error_log /var/log/nginx/oidc_error.log debug; | |
} | |
location = /_refresh { | |
# This location is called by oidcRefreshRequest(). We use the proxy_ directives | |
# to construct the OpenID Connect token request, as per: | |
# https://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken | |
internal; | |
gunzip on; # Decompress if necessary | |
proxy_set_header Content-Type "application/x-www-form-urlencoded"; | |
proxy_method POST; | |
proxy_set_body "grant_type=refresh_token&refresh_token=$arg_token&client_id=$oidc_client&client_secret=$oidc_client_secret"; | |
proxy_pass $oidc_token_endpoint; | |
error_log /var/log/nginx/oidc_error.log debug; | |
} | |
location = /_id_token_validation { | |
# This location is called by oidcCodeExchange() and oidcRefreshRequest(). We use | |
# the auth_jwt_module to validate the OpenID Connect token response, as per: | |
# https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation | |
internal; | |
auth_jwt "" token=$arg_token; | |
js_content validateIdToken; | |
error_page 500 502 504 @oidc_error; | |
error_log /var/log/nginx/oidc_error.log debug; | |
} | |
location = /logout { | |
set $session_jwt -; # Clear tokens from keyval, set to - to indicate logout, | |
set $refresh_token -; # and so that the new value is propagated by zone_sync. | |
add_header Set-Cookie "auth_token=; Path=/; HttpOnly;"; # Send empty cookie | |
add_header Set-Cookie "auth_redir=; Path=/; HttpOnly;"; # Erase original cookie | |
return 302 $oidc_logout_redirect; | |
} | |
location = /_logout { | |
# This location is the default value of $oidc_logout_redirect (in case it wasn't configured) | |
default_type text/plain; | |
return 200 "Logged out\n"; | |
} | |
location @oidc_error { | |
# This location is called when oidcCodeExchange() or oidcRefreshRequest() returns an error | |
default_type text/plain; | |
return 500 "NGINX / OpenID Connect login failure\n"; | |
} | |
location /api/ { | |
api write=on; | |
allow 127.0.0.1; # Only the NGINX host may call the NIGNX Plus API | |
deny all; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment