Skip to content

Commit 9ca73ae

Browse files
authored
R22 (#20)
* R22 release * Updated error log info * Added 500 error troubleshooting
1 parent c9ae3f3 commit 9ca73ae

File tree

6 files changed

+379
-247
lines changed

6 files changed

+379
-247
lines changed

README.md

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ With this environment, both the client and NGINX Plus communicate directly with
2424

2525
NGINX Plus is configured to perform OpenID Connect authentication. Upon a first visit to a protected resource, NGINX Plus initiates the OpenID Connect authorization code flow and redirects the client to the OpenID Connect provider (IdP). When the client returns to NGINX Plus with an authorization code, NGINX Plus exchanges that code for a set of tokens by communicating directly with the IdP.
2626

27-
The ID Token received from the IdP is then [validated](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). NGINX Plus then stores the ID token in the key-value store, issues a session cookie to the client using a random string, (which becomes the key to obtain the ID token from the key-value store) and redirects the client to the original URI requested prior to authentication.
27+
The ID Token received from the IdP is [validated](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). NGINX Plus then stores the ID token in the key-value store, issues a session cookie to the client using a random string, (which becomes the key to obtain the ID token from the key-value store) and redirects the client to the original URI requested prior to authentication.
2828

2929
Subsequent requests to protected resources are authenticated by exchanging the session cookie for the ID Token in the key-value store. JWT validation is performed on each request, as normal, so that the ID Token validity period is enforced.
3030

@@ -38,15 +38,19 @@ If a [refresh token](https://openid.net/specs/openid-connect-core-1_0.html#Refre
3838

3939
Requests made to the `/logout` location invalidate both the ID token and refresh token by erasing them from the key-value store. Therefore, subsequent requests to protected resources will be treated as a first-time request and send the client to the IdP for authentication. Note that the IdP may issue cookies such that an authenticated session still exists at the IdP.
4040

41-
## Installation
41+
### Multiple IdPs
42+
43+
Where NGINX Plus is configured to proxy requests for multiple websites or applications, or user groups, these may require authentication by different IdPs. Separate IdPs can be configured, with each one matching on an attribute of the HTTP request, e.g. hostname or part of the URI path.
4244

43-
The master branch of this repo requires the most recent release of NGINX Plus. Older releases should use the branch corresponding to that NGINX Plus release. For installation instructions, see [Installing NGINX Plus](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-plus/).
45+
> **Note:** When validating OpenID Connect tokens, NGINX Plus can be configured to read the signing key (JWKS) from disk, or a URL. When using multiple IdPs, each one must be configured to use the same method. It is not possible to use a mix of both disk and URLs for the `map…$oidc_jwt_keyfile` variable.
46+
47+
## Installation
4448

45-
In addition, the [njs module](https://www.nginx.com/blog/introduction-nginscript/) is required for handling the interaction between NGINX Plus and the OpenID Connect provider (IdP). Install the njs module after installing NGINX Plus by running one of the following:
49+
Start by [installing NGINX Plus](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-plus/). In addition, the [NGINX JavaScript module](https://www.nginx.com/blog/introduction-nginscript/) (njs) is required for handling the interaction between NGINX Plus and the OpenID Connect provider (IdP). Install the njs module after installing NGINX Plus by running one of the following:
4650

4751
`$ sudo apt install nginx-plus-module-njs` for Debian/Ubuntu
4852

49-
`$ sudo yum install nginx-plus-module-njs` for CentOS/RedHat
53+
`$ sudo yum install nginx-plus-module-njs` for CentOS/RHEL
5054

5155
The njs module needs to be loaded by adding the following configuration directive near the top of **nginx.conf**.
5256

@@ -58,13 +62,24 @@ Finally, create a clone of the GitHub repository.
5862

5963
`$ git clone https://github.com/nginxinc/nginx-openid-connect`
6064

61-
> **N.B.** There is a branch for each NGINX Plus release. Switch to the correct branch to ensure compatibility with the features and syntax of each release.
65+
> **Note:** There is a branch for each NGINX Plus release. Switch to the correct branch to ensure compatibility with the features and syntax of each release. The master branch works with the most recent NGINX Plus and JavaScript module releases.
6266
6367
All files can be copied to **/etc/nginx/conf.d**
6468

65-
> **N.B.** The GitHub repository contains [include](http://nginx.org/en/docs/ngx_core_module.html#include) files for NGINX configuration and JavaScript code for token exchange and initial token validation. These files are referenced with a relative path (relative to /etc/nginx). If NGINX Plus is running from a non-standard location then copy the files from the GitHub repository to `/path/to/conf/conf.d` and use the `-p` flag to start NGINX with a prefix path that specifies the location where the configuration files are located.
66-
>
67-
> `nginx -p /path/to/conf -c /path/to/conf/nginx.conf`
69+
### Non-standard directories
70+
The GitHub repository contains [`include`](http://nginx.org/en/docs/ngx_core_module.html#include) files for NGINX configuration, and JavaScript code for token exchange and initial token validation. These files are referenced with a relative path (relative to /etc/nginx). If NGINX Plus is running from a non-standard location then copy the files from the GitHub repository to `/path/to/conf/conf.d` and use the `-p` flag to start NGINX with a prefix path that specifies the location where the configuration files are located.
71+
72+
```shell
73+
$ nginx -p /path/to/conf -c /path/to/conf/nginx.conf
74+
```
75+
76+
### Running in containers
77+
This implementation is suitable for running in a container provided that the [base image](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-docker/) includes the NGINX JavaScript module. The GitHub repository is designed to facilitate testing with a container by binding the cloned repository to a mount volume on the container.
78+
79+
```shell
80+
$ cd nginx-openid-connect
81+
$ docker run -d -p 8010:8010 -v $PWD:/etc/nginx/conf.d nginx-plus
82+
```
6883

6984
## Configuring your IdP
7085

@@ -74,45 +89,48 @@ All files can be copied to **/etc/nginx/conf.d**
7489
* Ensure NGINX Plus is configured as a confidential client (with a client secret)
7590
* Make a note of the `client ID` and `client secret`
7691

77-
* 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:
92+
* 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 next section. Otherwise:
7893
* Obtain the URL for `jwks_uri` or download the JWK file to your NGINX Plus instance
7994
* Obtain the URL for the **authorization endpoint**
8095
* Obtain the URL for the **token endpoint**
8196

8297
## Configuring NGINX Plus
8398

84-
Review the following files copied from the GitHub repository so that they match your IdP configuration.
99+
Configuration can typically be completed automatically by using the `configure.sh` script.
100+
101+
Manual configuration involves reviewing the following files so that they match your IdP(s) configuration.
85102

86-
* **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.
103+
* **openid_connect_configuration.conf** - this contains the primary configuration for one or more IdPs in `map{}` blocks
104+
* Modify all of the `map…$oidc_` blocks to match your IdP configuration
105+
* Modify the URI defined in `map…$oidc_logout_redirect` to specify an unprotected resource to be displayed after requesting the `/logout` location
106+
* Set a unique value for `$oidc_hmac_key` to ensure nonce values are unpredictable
107+
108+
* **frontend.conf** - this is the reverse proxy configuration
87109
* Modify the upstream group to match your backend site or app
88-
* Modify the `resolver` directive to match a DNS server that is capable of resolving the IdP defined in `$oidc_token_endpoint`
89-
* Modify the URI defined in `$oidc_logout_redirect` to specify an unprotected resource to be displayed after requesting the `/logout` location
90110
* Configure the preferred listen port and [enable SSL/TLS configuration](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/)
91-
* Set the value of `$oidc_jwt_keyfile` to specify the `jwks_uri` value or match the JWK file downloaded from the IdP (ensuring that it is readable by the NGINX worker processes)
111+
* Modify the severity level of the `error_log` directive to suit the deployment environment
92112
* Comment/uncomment the `auth_jwt_key_file` or `auth_jwt_key_request` directives based on whether `$oidc_jwt_keyfile` is a file or URI, respectively
93-
* Modify all of the `set $oidc_` directives to match your IdP configuration
94-
* Set a unique value for `$oidc_hmac_key` to ensure nonce values are unpredictable
95113

96114
* **openid_connect.server_conf** - this is the NGINX configuration for handling the various stages of OpenID Connect authorization code flow
97115
* No changes are usually required here
116+
* Modify the `resolver` directive to match a DNS server that is capable of resolving the IdP defined in `$oidc_token_endpoint`
98117
* 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
99-
* 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;
100118

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

104122
### Configuring the Key-Value Store
105123

106-
The key-value store is used to maintain persistent storage for ID tokens and refresh tokens. The default configuration should be reviewed so that it suits the environment.
124+
The key-value store is used to maintain persistent storage for ID tokens and refresh tokens. The default configuration should be reviewed so that it suits the environment. This is part of the advanced configuration in **openid_connect_configuration.conf**.
107125

108126
```nginx
109-
keyval_zone zone=opaque_sessions:1M state=conf.d/opaque_sessions.json timeout=1h;
110-
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
127+
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
128+
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
111129
```
112130

113131
Each of the `keyval_zone` parameters are described below.
114132

115-
* **zone** - Specifies the name of the key-value store and how much memory to allocate for it. Each session will typically occupy 1-2KB, depending on the size of the JWT, so scale this value to exceed the number of unique users that may authenticate.
133+
* **zone** - Specifies the name of the key-value store and how much memory to allocate for it. Each session will typically occupy 1-2KB, depending on the size of the tokens, so scale this value to exceed the number of unique users that may authenticate.
116134

117135
* **state** (optional) - Specifies where all of the ID Tokens in the key-value store are saved, so that sessions will persist across restart or reboot of the NGINX host. The NGINX Plus user account, typically **nginx**, must have write permission to the directory where the state file is stored. Consider creating a dedicated directory for this purpose.
118136

@@ -127,35 +145,60 @@ The [NGINX Plus API](http://nginx.org/en/docs/http/ngx_http_api_module.html) is
127145
To query the current sessions in the key-value store:
128146

129147
```shell
130-
$ curl localhost:8010/api/4/http/keyvals/opaque_sessions
148+
$ curl localhost:8010/api/6/http/keyvals/oidc_id_tokens
131149
```
132150

133151
To delete a single session:
134152

135153
```shell
136-
$ curl -iX PATCH -d '{"<session ID>":null}' localhost:8010/api/4/http/keyvals/opaque_sessions
137-
$ curl -iX PATCH -d '{"<session ID>":null}' localhost:8010/api/4/http/keyvals/refresh_tokens
154+
$ curl -iX PATCH -d '{"<session ID>":null}' localhost:8010/api/6/http/keyvals/oidc_id_tokens
155+
$ curl -iX PATCH -d '{"<session ID>":null}' localhost:8010/api/6/http/keyvals/refresh_tokens
138156
```
139157

140158
To delete all sessions:
141159

142160
```shell
143-
$ curl -iX DELETE localhost:8010/api/3/http/keyvals/opaque_sessions
144-
$ curl -iX DELETE localhost:8010/api/3/http/keyvals/refresh_tokens
161+
$ curl -iX DELETE localhost:8010/api/6/http/keyvals/oidc_id_tokens
162+
$ curl -iX DELETE localhost:8010/api/6/http/keyvals/refresh_tokens
163+
```
164+
165+
## Real time monitoring
166+
167+
The **openid_connect.server_conf** file defines several [`status_zone`](http://nginx.org/en/docs/http/ngx_http_api_module.html#status_zone) directives to collect metrics about OpenID Connect activity and errors. Separate metrics counters are recorded for:
168+
169+
* **OIDC start** - New sessions are counted here. See step 2 in Figure 2, above. Success is recorded as a 3xx response.
170+
171+
* **OIDC code exchange** - Counters are incremented here when the browser returns to NGINX Plus after authentication. See steps 6-10 in Figure 2, above. Success is recorded as a 3xx response.
172+
173+
* **OIDC logout** - Requests to the /logout URI are counted here. Success is recorded as a 3xx response.
174+
175+
* **OIDC error** - Counters are incremented here when errors in the code exchange process are actively detected. Typically there will be a corresponding error_log entry.
176+
177+
To obtain the current set of metrics:
178+
179+
```shell
180+
$ curl localhost:8010/api/6/http/location_zones
145181
```
146182

183+
In addition, the [NGINX Plus Dashboard](https://docs.nginx.com/nginx/admin-guide/monitoring/live-activity-monitoring/#dashboard) can be configured to visualize the monitoring metrics in a GUI.
184+
147185
## Troubleshooting
148186

149-
Any errors generated by the OpenID Connect flow are logged in a separate file, `/var/log/nginx/oidc_error.log`. Check the contents of this file as it may include error responses received by the IdP.
187+
Any errors generated by the OpenID Connect flow are logged to the error log, `/var/log/nginx/error.log`. Check the contents of this file as it may include error responses received by the IdP. The level of detail recorded can be modified by adjusting the severity level of the `error_log` directive.
150188

151189
* **400 error from IdP**
152190
* This is typically caused by incorrect configuration related to the client ID and client secret.
153-
* Check the values of the `$oidc_client` and `$oidc_client_secret` variables against the IdP configuration.
191+
* Check the values of the `map…$oidc_client` and `map…$oidc_client_secret` variables against the IdP configuration.
192+
193+
* **500 error from nginx after successful authentication**
194+
* Check for `could not be resolved` and `empty JWK set while sending to client` messages in the error log. This is common when NGINX Plus cannot reach the IdP's `jwks_uri` endpoint.
195+
* Check the `map…$oidc_jwt_keyfile` variable is correct.
196+
* Check the `resolver` directive in **openid_connect.server_conf** is reachable from the NGINX Plus host.
154197

155198
* **Authentication is successful but browser shows too many redirects**
156199
* This is typically because the JWT sent to the browser cannot be validated, resulting in 'authorization required' `401` response and starting the authentication process again. But the user is already authenticated so is redirected back to NGINX, hence the redirect loop.
157-
* Check the error log `/var/log/nginx/oidc_error.log` for JWT/JWK errors.
158-
* Ensure that the JWK file (`$oidc_jwt_keyfile` variable) is correct and that the nginx user has permission to read it.
200+
* Check the error log `/var/log/nginx/error.log` for JWT/JWK errors.
201+
* Ensure that the JWK file (`map…$oidc_jwt_keyfile` variable) is correct and that the nginx user has permission to read it.
159202

160203
* **Logged out but next request does not require authentication**
161204
* This is typically caused by the IdP issuing its own session cookie(s) to the client. NGINX Plus sends the request to the IdP for authentication and the IdP immediately sends back a new authorization code because the session is still valid.
@@ -168,7 +211,6 @@ Any errors generated by the OpenID Connect flow are logged in a separate file, `
168211
```nginx
169212
proxy_set_header Host <IdP hostname>;
170213
proxy_ssl_name <IdP hostname>;
171-
proxy_ssl_server_name on;
172214
```
173215

174216
## Support
@@ -182,4 +224,4 @@ This reference implementation for OpenID Connect is supported for NGINX Plus sub
182224
* **R17** Configuration now supports JSON Web Key (JWK) set to be obtained by URI
183225
* **R18** Opaque session tokens now used by default. Added support for refresh tokens. Added `/logout` location.
184226
* **R19** Minor bug fixes
185-
227+
* **R22** Separate configuration file, supports multiple IdPs. Configurable scopes and cookie flags. JavaScript is imported as an indepedent module with `js_import`. Container-friendly logging. Additional metrics for OIDC activity.

0 commit comments

Comments
 (0)