missing something with auth_jwt_key_request
Christopher Paul
chris.paul at rexconsulting.net
Tue Mar 12 03:09:53 UTC 2024
Hi NGINX-users,
I am running nginx version: nginx/1.25.3 (nginx-plus-r31-p1 on Rocky 9.3
in a lab, trying to get OIDC authentication working to KeyCloak 23.0.7.
Attached are the relevant files /etc/nginx.conf and included
/etc/nginx/conf.d files, most of which are from the nginx-openid-connect
github repo (https://github.com/nginxinc/nginx-openid-connect).
Keycloak and nginx are running on the same VM.
What am I missing/doing wrong? When I try to hit the server, the
redirect to Keycloak does not happen. I can tell this for sure by
running "sudo tcpdump -i lo". There are no packets transmitted to
localhost:8080. When I "curl -v https://rocky.rexconsulting.net",
besides no packets between nginx and keycloak, the output of curl is:
* Connected to rocky.rexconsulting.net (10.5.5.90) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/cert.pem
* CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Unknown (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / [blank] / UNDEF
* ALPN: server accepted http/1.1
* Server certificate:
* subject: CN=rocky.rexconsulting.net
* start date: Mar 7 23:46:13 2024 GMT
* expire date: Jun 5 23:46:12 2024 GMT
* subjectAltName: host "rocky.rexconsulting.net" matched cert's
"rocky.rexconsulting.net"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* Certificate level 0: Public key type ? (256/128 Bits/secBits),
signed using sha256WithRSAEncryption
* Certificate level 1: Public key type ? (2048/112 Bits/secBits),
signed using sha256WithRSAEncryption
* using HTTP/1.x
> GET / HTTP/1.1
> Host: rocky.rexconsulting.net
> User-Agent: curl/8.6.0
> Accept: */*
>
* old SSL session ID is stale, removing
< HTTP/1.1 401 Unauthorized
< Server: nginx/1.25.3
< Date: Tue, 12 Mar 2024 03:07:32 GMT
< Content-Type: text/html
< Content-Length: 179
< Connection: keep-alive
< WWW-Authenticate: Bearer realm="closed site"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
* Connection #0 to host rocky.rexconsulting.net left intact
Many thanks for any insight that might be offered on this.
Chris Paul
-------------- next part --------------
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log debug;
pid /var/run/nginx.pid;
load_module modules/ngx_http_js_module.so;
load_module modules/ngx_stream_js_module.so;
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;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
-------------- next part --------------
# OpenID Connect configuration
#
# Each map block allows multiple values so that multiple IdPs can be supported,
# the $host variable is used as the default input parameter but can be changed.
#
map $host $oidc_authz_endpoint {
#default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/auth";
#www.example.com "https://my-idp/oauth2/v1/authorize";
default "http://127.0.0.1:8080/realms/rexlab/protocol/openid-connect/auth";
}
map $host $oidc_authz_extra_args {
# Extra arguments to include in the request to the IdP's authorization
# endpoint.
# Some IdPs provide extended capabilities controlled by extra arguments,
# for example Keycloak can select an IdP to delegate to via the
# "kc_idp_hint" argument.
# Arguments must be expressed as query string parameters and URL-encoded
# if required.
default "";
#www.example.com "kc_idp_hint=another_provider"
}
map $host $oidc_token_endpoint {
#default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token";
default "http://127.0.0.1:8080/auth/realms/rexlab/protocol/openid-connect/token";
}
map $host $oidc_jwt_keyfile {
#default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs";
default "http://127.0.0.1:8080/realms/rexlab/protocol/openid-connect/certs";
}
map $host $oidc_client {
default "nginx-plus";
}
map $host $oidc_pkce_enable {
default 0;
}
map $host $oidc_client_secret {
default "UxPA37ZTMv36mTGSZhfSTFCl91YYzwcx";
}
map $host $oidc_scopes {
default "openid+profile+email+offline_access";
}
map $host $oidc_logout_redirect {
# Where to send browser after requesting /logout location. This can be
# replaced with a custom logout page, or complete URL.
default "/_logout"; # Built-in, simple logout page
}
map $host $oidc_hmac_key {
# This should be unique for every NGINX instance/cluster
default "f3etJkRhybOLWPAt59lWN4GmXz";
}
map $host $zone_sync_leeway {
# Specifies the maximum timeout for synchronizing ID tokens between cluster
# nodes when you use shared memory zone content sync. This option is only
# recommended for scenarios where cluster nodes can randomly process
# requests from user agents and there may be a situation where node "A"
# successfully received a token, and node "B" receives the next request in
# less than zone_sync_interval.
default 0; # Time in milliseconds, e.g. (zone_sync_interval * 2 * 1000)
}
map $proto $oidc_cookie_flags {
http "Path=/; SameSite=lax;"; # For HTTP/plaintext testing
https "Path=/; SameSite=lax; HttpOnly; Secure;"; # Production recommendation
}
map $http_x_forwarded_port $redirect_base {
"" $proto://$host:$server_port;
default $proto://$host:$http_x_forwarded_port;
}
map $http_x_forwarded_proto $proto {
"" $scheme;
default $http_x_forwarded_proto;
}
# ADVANCED CONFIGURATION BELOW THIS LINE
# Additional advanced configuration (server context) in openid_connect.server_conf
# 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;
# Change timeout values to at least the validity period of each token type
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
keyval_zone zone=oidc_access_tokens:1M state=conf.d/oidc_access_tokens.json timeout=1h;
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
keyval_zone zone=oidc_pkce:128K timeout=90s; # Temporary storage for PKCE code verifier.
keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for JWT
keyval $cookie_auth_token $access_token zone=oidc_access_tokens; # Exchange cookie for access token
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
keyval $request_id $new_access_token zone=oidc_access_tokens;
keyval $request_id $new_refresh zone=refresh_tokens; # ''
keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;
auth_jwt_claim_set $jwt_audience aud; # In case aud is an array
js_import oidc from conf.d/openid_connect.js;
# vim: syntax=nginx
-------------- next part --------------
# This is the backend application we are protecting with OpenID Connect
upstream my_backend {
zone my_backend 64k;
server 10.0.0.1:80;
}
# 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"';
# The frontend server - reverse proxy with OpenID Connect authentication
#
server {
include conf.d/openid_connect.server_conf; # Authorization code flow and Relying Party processing
error_log /var/log/nginx/error.log debug; # Reduce severity level as required
listen 8010; # Use SSL/TLS in production
location / {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
error_page 401 = @do_oidc_flow;
#auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Successfully authenticated users are proxied to the backend,
# with 'sub' claim passed as HTTP header
proxy_set_header username $jwt_claim_sub;
# Bearer token is uses to authorize NGINX to access protected backend
#proxy_set_header Authorization "Bearer $access_token";
# Intercept and redirect "401 Unauthorized" proxied responses to nginx
# for processing with the error_page directive. Necessary if Access Token
# can expire before ID Token.
#proxy_intercept_errors on;
proxy_pass http://my_backend; # The backend site/app
access_log /var/log/nginx/access.log main_jwt;
}
}
# vim: syntax=nginx
-------------- next part --------------
server {
listen 80;
server_name rocky.rexconsulting.net;
return 301 https://$host$request_uri;
}
server {
# listen 80 default_server;
listen 443 ssl;
# auth_jwt "API";
# auth_jwt_key_request "http://localhost:8080/realms/rexlab/protocol/openid-connect/certs";
# auth_jwt_type encrypted;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log debug;
server_name rocky.rexconsulting.net;
ssl_certificate /etc/ssl/nginx/nginx.crt;
ssl_certificate_key /etc/ssl/nginx/nginx.key;
# ssl_verify_client on;
# ssl_trusted_certificate /etc/ssl/cachain.pem;
# ssl_ocsp on; # Enable OCSP validation
#access_log /var/log/nginx/host.access.log main;
location / {
auth_jwt_key_request /_jwks_uri;
auth_jwt "closed site" token=$cookie_auth_token;
root /usr/share/nginx/html;
index index.html index.htm;
}
location = /_jwks_uri {
internal;
#proxy_pass $oidc_jwt_keyfile; # Uses the mapped value
proxy_pass "http://127.0.0.1:8080/realms/rexlab/protocol/openid-connect/certs";
proxy_cache_valid 200 1d; # Caches the keys for a day, adjust as needed
proxy_cache jwk; # Assumes you have a proxy_cache_path defined with `jwk` as the key
error_page 401 = @error401;
}
location @error401 {
internal;
proxy_pass $oidc_authz_endpoint; # Redirect to Keycloak for login
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
More information about the nginx
mailing list