Skip to content

Commit 8584038

Browse files
committed
NGINX Plus R17 update
1 parent fdf2224 commit 8584038

File tree

4 files changed

+188
-8
lines changed

4 files changed

+188
-8
lines changed

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,26 +63,28 @@ All files can be copied to **/etc/nginx/conf.d**
6363
* Set the **redirect URI** to the address of your NGINX Plus instance (including the port number), with `/_codexch` as the path, e.g. `https://my-nginx.example.com:443/_codexch`
6464
* Ensure NGINX Plus is configured as a confidential client (with a client secret)
6565
* Make a note of the `client ID` and `client secret`
66+
67+
* If your IdP supports OpenID Connect Discovery (usually at the URI `/.well-known-openid-configuration`) then use the `configure.sh` script to complete configuration. In this case you can skip the **frontend.conf** configuration. Otherwise:
6668
* Download the `jwks_uri` JWK file to your NGINX Plus instance
67-
68-
* Obtain the URL for the **authorization endpoint**
69-
70-
* Obtain the URL for the **token endpoint**
69+
* Obtain the URL for the **authorization endpoint**
70+
* Obtain the URL for the **token endpoint**
7171

7272
## Configuring NGINX Plus
7373

7474
Review the following files copied from the GitHub repository so that they match your IdP configuration.
7575

76-
* **frontend.conf** - this is the reverse proxy configuration and where the IdP is configured
76+
* **frontend.conf** - this is the reverse proxy configuration and where the IdP is configured. This file can be automatically configured by using the `configure.sh` script.
7777
* Modify the upstream group to match your backend site or app
78+
* Modify the `resolver` directive to match a DNS server that is capable of resolving the IdP defined in `$oidc_token_endpoint`
7879
* Configure the preferred listen port and [enable SSL/TLS configuration](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/)
7980
* Set the value of `$oidc_jwt_keyfile` to match the downloaded JWK file from the IdP and ensure that it is readable by the NGINX worker processes
8081
* Modify all of the `set $oidc_` directives to match your IdP configuration
8182
* Set a unique value for `$oidc_hmac_key` to ensure nonce values are unpredictable
8283

8384
* **openid_connect.server_conf** - this is the NGINX configuration for handling the various stages of OpenID Connect authorization code flow
85+
* No changes are usually required here
86+
* If using [`auth_jwt_key_request`](http://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_key_request) to automatically fetch the JWK file from the IdP then modify the validity period and other caching options to suit your IdP
8487
* Modify the `add_header Set-Cookie` directives with appropriate [cookie flags](https://en.wikipedia.org/wiki/HTTP_cookie#Terminology) to control the scope of single sign-on and security options, e.g. Domain; Path; Secure;
85-
* Modify the `resolver` directive to match a DNS server that is capable of resolving the IdP defined in `$oidc_token_endpoint`
8688

8789
* **openid_connect.js** - this is the JavaScript code for performing the authorization code exchange and nonce hashing
8890
* No changes are required unless modifying the code exchange or validation process

configure.sh

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env bash
2+
# configure.sh (c) NGINX, Inc. [23-Nov-2018] Liam Crilly <[email protected]>
3+
4+
COMMAND=${0##*/}
5+
CONFDIR=${0%/*}
6+
if [ $# -lt 1 ]; then
7+
echo "USAGE: $COMMAND [options] <OpenID Connect confinguration URL>"
8+
echo ""
9+
echo "Configures NGINX Plus OpenID Connect reference implementation by using the IdP's Discovery interface"
10+
echo ""
11+
echo " URL typically ends with '/openid-configuration'"
12+
echo " Options:"
13+
echo " -k | --auth_jwt_key <file|request> # Use auth_jwt_key_file (default) or auth_jwt_key_request (R17+)"
14+
echo " -i | --client_id <id> # Client ID as obtained from OpenID Connect Provider"
15+
echo " -s | --client_secret <secret> # Client secret as obtained from OpenID Connect Provider"
16+
echo " -x | --insecure # Do not verify IdP's SSL certificate"
17+
echo " -d | --dry_run # Produce configuration to stdout without modifying frontend.conf"
18+
echo ""
19+
exit 1
20+
fi
21+
22+
# Process command line options
23+
#
24+
DO_JWKS_URI=0
25+
CLIENT_ID=""
26+
CLIENT_SECRET=""
27+
SED_OPT="-i.ORIG "
28+
while [ $# -gt 1 ]; do
29+
case "$1" in
30+
"-k" | "--auth_jwt_key")
31+
if [ "$2" == "request" ]; then
32+
DO_JWKS_URI=1
33+
elif [ "$2" != "file" ]; then
34+
echo "$COMMAND: ERROR: Valid arguments to $1 are 'file' or 'request'"
35+
exit 1
36+
fi
37+
shift; shift
38+
;;
39+
"-i" | "--client_id" | "--client-id")
40+
CLIENT_ID=$2
41+
shift; shift
42+
;;
43+
"-s" | "--client_secret" | "--client-secret")
44+
CLIENT_SECRET=$2
45+
shift; shift
46+
;;
47+
"-x" | "--insecure" )
48+
CURL_OPT="-k "
49+
WGET_OPT="--no-check-certificate "
50+
shift
51+
;;
52+
"-d" | "--dry_run" | "--dry-run")
53+
SED_OPT=""
54+
shift
55+
;;
56+
*)
57+
echo "$COMMAND: ERROR: Invalid command line option ($1) - quitting"
58+
exit 1
59+
;;
60+
esac
61+
done
62+
IDP_URL=$1
63+
64+
# Check for dependencies
65+
#
66+
hash jq 2> /dev/null
67+
if [ $? -ne 0 ]; then
68+
echo "$COMMAND: ERROR: 'jq' must be installed"
69+
jq
70+
exit 1
71+
fi
72+
73+
for http_cli in "wget ${WGET_OPT}-q -O -" "curl ${CURL_OPT}-sS"; do
74+
hash ${http_cli%% *} 2> /dev/null # Remove chars beyond space
75+
if [ $? -eq 0 ]; then
76+
GET_URL=$http_cli
77+
break #for
78+
fi
79+
done
80+
if [ "$GET_URL" == "" ]; then
81+
echo "$COMMAND: ERROR: 'curl' or 'wget' must be installed to download configuration data"
82+
exit 1
83+
fi
84+
85+
# Download the OpenID Connect Discovery document
86+
$GET_URL $IDP_URL > /tmp/${COMMAND}_$$_json
87+
88+
# Test for exit error
89+
if [ $? -ne 0 ]; then
90+
echo "$COMMAND: ERROR: Unable to connect to $IDP_URL"
91+
cat /tmp/${COMMAND}_$$_json
92+
rm /tmp/${COMMAND}_$$_json
93+
exit 1
94+
fi
95+
96+
# Test for valid JSON object
97+
jq -r .authorization_endpoint < /tmp/${COMMAND}_$$_json 2>&1 | grep -c ^http > /dev/null
98+
if [ $? -ne 0 ]; then
99+
echo "$COMMAND: ERROR: $IDP_URL returned invalid OpenID Connect Discovery document"
100+
cat /tmp/${COMMAND}_$$_json
101+
rm /tmp/${COMMAND}_$$_json
102+
exit 1
103+
fi
104+
105+
# Build an intermediate configuration file (will be converted to sed(1) command file.
106+
# File format is: <NGINX variable name><space><IdP value>
107+
#
108+
jq -r '. | "$oidc_authz_endpoint \(.authorization_endpoint)\n$oidc_token_endpoint \(.token_endpoint)\n$oidc_jwks_uri \(.jwks_uri)"' < /tmp/${COMMAND}_$$_json > /tmp/${COMMAND}_$$_conf
109+
110+
# Create a random value for HMAC key, adding to the base mapping file
111+
echo "\$oidc_hmac_key `openssl rand -base64 18`" >> /tmp/${COMMAND}_$$_conf
112+
113+
# Add client ID and secret to the base mapping file (if provided)
114+
if [ "$CLIENT_ID" != "" ]; then
115+
echo "\$oidc_client $CLIENT_ID" >> /tmp/${COMMAND}_$$_conf
116+
fi
117+
if [ "$CLIENT_SECRET" != "" ]; then
118+
echo "\$oidc_client_secret $CLIENT_SECRET" >> /tmp/${COMMAND}_$$_conf
119+
fi
120+
121+
# Fetch or configure the JWK file depending on configuration input
122+
# Also apply appropriate auth_jwt_key_ configuration directive.
123+
# NB: auth_jwt_key_request requires NGINX Plus R17 or later
124+
#
125+
JWKS_URI=`jq -r .jwks_uri < /tmp/${COMMAND}_$$_json`
126+
if [ $DO_JWKS_URI -eq 0 ]; then
127+
echo "$COMMAND: NOTICE: Downloading $CONFDIR/idp_jwk.json"
128+
$GET_URL $JWKS_URI > $CONFDIR/idp_jwk.json
129+
if [ $? -ne 0 ] || [ ! -s $CONFDIR/idp_jwk.json ]; then
130+
echo "$COMMAND: ERROR: Failed to download from $JWKS_URI"
131+
cat $CONFDIR/idp_jwk.json
132+
exit 1
133+
fi
134+
echo "\$oidc_jwt_keyfile conf.d/idp_jwk.json" >> /tmp/${COMMAND}_$$_conf
135+
echo "s/#\(auth_jwt_key_file\)/\1/" > /tmp/${COMMAND}_$$_sed # Uncomment
136+
echo "s/ \(auth_jwt_key_request\)/ #\1/" >> /tmp/${COMMAND}_$$_sed # Comment-out
137+
else
138+
echo "\$oidc_jwt_keyfile $JWKS_URI" >> /tmp/${COMMAND}_$$_conf
139+
echo "s/ \(auth_jwt_key_file\)/ #\1/" > /tmp/${COMMAND}_$$_sed # Comment-out
140+
echo "s/#\(auth_jwt_key_request\)/\1/" >> /tmp/${COMMAND}_$$_sed # Uncomment
141+
fi
142+
143+
# Build the sed(1) command file (requires a lot of escaping)
144+
#
145+
sed -e "s/\//\\\\\//g" /tmp/${COMMAND}_$$_conf | awk '{print "s/\\("$1"\\) \\(.*\\);/\\1 \""$2"\";/"}' >> /tmp/${COMMAND}_$$_sed
146+
147+
# Perform the substitutions on frontend.conf
148+
#
149+
echo "$COMMAND: NOTICE: Configuring $CONFDIR/frontend.conf"
150+
sed ${SED_OPT}-f /tmp/${COMMAND}_$$_sed $CONFDIR/frontend.conf
151+
152+
if [ $? -eq 0 ]; then
153+
echo "$COMMAND: NOTICE: Success - test configuration with 'nginx -t'"
154+
rm /tmp/${COMMAND}_$$_*
155+
else
156+
echo "$COMMAND: ERROR: Configuration failed, check intermediate files `ls -1 /tmp/${COMMAND}_$$_* | tr '\n' ' '`"
157+
fi

frontend.conf

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ js_include conf.d/openid_connect.js;
1313
js_set $requestid_hash hashRequestId;
1414
js_set $auth_token getAuthToken;
1515

16+
# Caching for JWT keys when using 'auth_jwt_key_request' with NGINX Plus R17+
17+
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:1m max_size=10m;
18+
1619
# The frontend server - reverse proxy with OpenID Connect authentication
1720
#
1821
server {
@@ -22,7 +25,7 @@ server {
2225
resolver 8.8.8.8; # For DNS lookup of IdP endpoints;
2326
subrequest_output_buffer_size 32k; # To fit a complete tokenset response
2427

25-
set $oidc_jwt_keyfile /etc/nginx/my_idp_jwk.json;
28+
set $oidc_jwt_keyfile /etc/nginx/my_idp_jwk.json; # URL when using 'auth_jwt_key_request'
2629
set $oidc_authz_endpoint "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/auth";
2730
set $oidc_token_endpoint "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token";
2831
set $oidc_client "my-client-id";
@@ -36,6 +39,7 @@ server {
3639
# This site is protected with OpenID Connect
3740
auth_jwt "" token=$cookie_auth_token;
3841
auth_jwt_key_file $oidc_jwt_keyfile;
42+
#auth_jwt_key_request /_jwks_uri; # Requires NGINX Plus R17+
3943

4044
# Absent/invalid OpenID Connect token will (re)start auth process
4145
error_page 401 @oidc_auth;
@@ -46,6 +50,7 @@ server {
4650
proxy_pass http://my_backend; # The backend site/app
4751

4852
access_log /var/log/nginx/access.log main_jwt;
53+
error_log /var/log/nginx/oidc_error.log info;
4954
}
5055
}
5156

openid_connect.server_conf

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
location = /_jwks_uri {
2+
# This is where the JSON Web Key Set is fetched from the IdP and cached
3+
internal;
4+
proxy_cache jwk;
5+
proxy_pass $oidc_jwt_keyfile;
6+
7+
# This configuration ignores all response headers that influence caching,
8+
# and instead sets a fixed validity period before the JWK is re-fetched.
9+
# See http://nginx.org/r/proxy_cache for all configuration options
10+
proxy_cache_valid 200 12h;
11+
proxy_cache_lock on;
12+
proxy_cache_use_stale error timeout updating;
13+
proxy_ignore_headers Cache-Control;
14+
proxy_ignore_headers Expires;
15+
proxy_ignore_headers Set-Cookie;
16+
}
17+
118
location @oidc_auth {
219
# Redirect this request to the OpenID Connect identity provider login page for this server{}
320
# Using authorization code flow (nonce sent to IdP is hash of $request_id)
@@ -53,7 +70,6 @@
5370
# https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
5471
internal;
5572
auth_jwt "" token=$arg_token;
56-
auth_jwt_key_file $oidc_jwt_keyfile;
5773
js_content validateIdToken;
5874

5975
error_log /var/log/nginx/oidc_error.log debug;

0 commit comments

Comments
 (0)