Skip to content

Commit b59a839

Browse files
add support for higher-bit HS/RS algorithms
fixes TeslaGov#77
1 parent d150705 commit b59a839

17 files changed

+500
-297
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ RUN set -x \
2929
FROM nginx:${NGINX_VERSION}
3030
LABEL stage=builder
3131
RUN apt-get update \
32-
&& apt-get -y install libjansson4 libjwt0 \
32+
&& apt-get -y install libjansson4 libjwt0 \
3333
&& cd /etc/nginx \
3434
&& cp nginx.conf nginx.conf.orig \
3535
&& sed -ri '/pid\s+\/var\/run\/nginx\.pid;$/a load_module \/usr\/lib64\/nginx\/modules\/ngx_http_auth_jwt_module\.so;' nginx.conf

Dockerfile-test-nginx

Lines changed: 0 additions & 10 deletions
This file was deleted.

Makefile

Lines changed: 0 additions & 73 deletions
This file was deleted.

README.md

Lines changed: 92 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,96 @@
11
# Intro
2+
23
This is an NGINX module to check for a valid JWT and proxy to an upstream server or redirect to a login page.
34

45
## Building and testing
6+
57
To build the Docker image, start NGINX, and run our Bash test against it, run
68

79
```bash
8-
make
10+
./scripts.sh all
911
```
1012

11-
When you make a change to the module, run `make rebuild-nginx`.
13+
When you make a change to the module or the NGINX test config, run `./scripts.sh rebuild_nginx` to rebuild the NGINX Docker image.
1214

13-
When you make a change to `test.sh`, run `make rebuild-test-runner`.
15+
When you make a change to `test.sh`, run `./scripts.sh rebuild_test_runner test` to rebuild the test runner image and run the tests.
1416

15-
| Command | Description |
16-
| -------------------------- |:-----------------------------------------------------------------:|
17-
| `make build-nginx` | Builds the NGINX image |
18-
| `make rebuild-nginx` | Re-builds the NGINX image |
19-
| `make build-test-runner` | Builds the images used by the test stack (uses Docker compose) |
20-
| `make rebuild-test-runner` | Re-builds the images used by the test stack |
21-
| `make start-nginx` | Starts the NGINX container |
22-
| `make stop-nginx` | Stops the NGINX container |
23-
| `make test` | Runs `test.sh` against the NGINX container (uses Docker compose) |
17+
The `./scripts.sh` file contains multiple commands to make things easy:
2418

25-
The image produced with `make build-nginx` only differs from the official Nginx image in two ways: the module itself and the nginx.conf configuration entry that loads it.
19+
| Command | Description |
20+
| --------------------- | ----------------------------------------------------------------- |
21+
| `build_nginx` | Builds the NGINX image. |
22+
| `rebuild_nginx` | Re-builds the NGINX image. |
23+
| `start_nginx` | Starts the NGINX container. |
24+
| `stop_nginx` | Stops the NGINX container. |
25+
| `cp_bin` | Copies the compiled binaries out of the NGINX container. |
26+
| `build_test_runner` | Builds the images used by the test stack (uses Docker compose). |
27+
| `rebuild_test_runner` | Re-builds the images used by the test stack. |
28+
| `test` | Runs `test.sh` against the NGINX container (uses Docker compose). |
2629

27-
The tests use a customized Nginx image, distinct from the main image, as well as a test runner image. By running `make test`, the two test containers will be created up with Docker compose, started, and the tests run. At the end, both containers will be automatically stopped and destroyed.
30+
You can run multiple commands in sequence by separating them with a space, e.g.:
2831

29-
This project is 100% Docker-ized.
32+
```shell
33+
./scripts.sh rebuild_nginx rebuild_test_runner test
34+
```
3035

31-
## Dependencies
32-
This module depends on the [JWT C Library](https://github.com/benmcollins/libjwt)
36+
The image produced with `./scripts.sh build_nginx` only differs from the official NGINX image in two ways:
37+
- the JWT module itself, and
38+
- the `nginx.conf` file is overwritten with our own.
3339

34-
Transitively, that library depends on a JSON Parser called
35-
[Jansson](https://github.com/akheron/jansson) as well as the OpenSSL library.
40+
The tests use a customized NGINX image, distinct from the main image, as well as a test runner image. By running `./scripts.sh test`, the two test containers will be stood up via Docker compose, then they'll be started, and the tests will run. At the end of the test run, both containers will be automatically stopped and destroyed. See below to learn how to trace test failures across runs.
3641

37-
## NGINX Directives
38-
This module requires several new `nginx.conf` directives,
39-
which can be specified in on the `main` `server` or `___location` level.
42+
### Tracing test failures
43+
44+
After making changes and finding that some tests fail, it can be difficult to understand why. By default, logs are written to Docker's internal log mechanism, but they won't be persisted after the test run completes and the containers are removed.
45+
46+
In order to persist logs, you can configure the log driver to use. You can do this by setting the environment variable `LOG_DRIVER` before running the tests. On Linux/Unix systems, you can use the driver `journald`, as follows:
47+
48+
```shell
49+
# need to rebuild the test runner with the proper log driver
50+
LOG_DRIVER=journald ./scripts.sh rebuild_test_runner
4051

52+
# run the tests
53+
./scripts.sh test
54+
55+
# check the logs
56+
journalctl -eu docker CONTAINER_NAME=jwt-nginx-test
4157
```
42-
auth_jwt_key "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF"; # see docs for format based on algorithm
43-
auth_jwt_loginurl "https://yourdomain.com/loginpage";
44-
auth_jwt_enabled on;
45-
auth_jwt_algorithm HS256; # or RS256
46-
auth_jwt_extract_sub on; # or off
47-
auth_jwt_validate_email on; # or off
48-
auth_jwt_use_keyfile off; # or on
49-
auth_jwt_keyfile_path "/app/pub_key";
58+
59+
Now you'll be able to see logs from previous test runs. The best way to make use of this is to open two terminals, one where you run the tests, and one where you follow the logs:
60+
61+
```shell
62+
# terminal 1
63+
./scripts.sh test
64+
65+
# terminal 2
66+
journalctl -fu docker CONTAINER_NAME=jwt-nginx-test
5067
```
5168

52-
The default algorithm is 'HS256', for symmetric key validation. When using HS256, the value for `auth_jwt_key` should be specified in binhex format. It is recommended to use at least 256 bits of data (32 pairs of hex characters or 64 characters in total) as in the example above. Note that using more than 512 bits will not increase the security. For key guidelines please see NIST Special Publication 800-107 Recommendation for Applications Using Approved Hash Algorithms, Section 5.3.2 The HMAC Key.
69+
## Dependencies
70+
71+
This module depends on the [JWT C Library](https://github.com/benmcollins/libjwt). Transitively, that library depends on a JSON Parser called [Jansson](https://github.com/akheron/jansson) as well as the OpenSSL library.
72+
73+
## NGINX Directives
74+
This module requires several new `nginx.conf` directives, which can be specified at the `http`, `server`, or `___location` levels.
75+
76+
| Directive | Description |
77+
| -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
78+
| `auth_jwt_key` | The key to use to decode/verify the JWT -- see below. |
79+
| `auth_jwt_redirect` | Set to "on" to redirect to `auth_jwt_loginurl` if authentication fails. |
80+
| `auth_jwt_loginurl` | The URL to redirect to if `auth_jwt_redirect` is enabled and authentication fails. |
81+
| `auth_jwt_enabled` | Set to "on" to enable JWT checking. |
82+
| `auth_jwt_algorithm` | The algorithm to use. One of: HS256, HS384, HS512, RS256, RS384, RS512 |
83+
| `auth_jwt_extract_sub` | Set to "on" to extract the `sub` claim (e.g. user id) from the JWT and into the `x-userid` header on the response. |
84+
| `auth_jwt_validate_email` | Set to "on" to extract the `emailAddress` claim from the JWT and into the `x-email` header on the response. |
85+
| `auth_jwt_use_keyfile` | Set to "on" to read the key from a file rather than from the `auth_jwt_key` directive. |
86+
| `auth_jwt_keyfile_path` | Set to the path from which the key should be read when `auth_jwt_use_keyfile` is enabled. |
87+
88+
89+
The default algorithm is `HS256`, for symmetric key validation. When using one of the `HS*` algorithms, the value for `auth_jwt_key` should be specified in binhex format. It is recommended to use at least 256 bits of data (32 pairs of hex characters or 64 characters in total) as in the example above. Note that using more than 512 bits will not increase the security. For key guidelines please see [NIST Special Publication 800-107 Recommendation for Applications Using Approved Hash Algorithms](https://csrc.nist.gov/publications/detail/sp/800-107/rev-1/final), Section 5.3.2 The HMAC Key.
90+
91+
The configuration also supports RSA public key validation via (e.g.) `auth_jwt_algorithm RS256`. When using the `RS*` alhorithms, the `auth_jwt_key` field must be set to your public key **OR** `auth_jwt_use_keyfile` should be set to `on` and `auth_jwt_keyfile_path` should point to the public key on disk. NGINX won't start if `auth_jwt_use_keyfile` is set to `on` and a key file is not provided.
5392

54-
The configuration also supports the `auth_jwt_algorithm` 'RS256', for RSA 256-bit public key validation. If using "auth_jwt_algorithm RS256;", then the `auth_jwt_key` field must be set to your public key **OR** `auth_jwt_use_keyfile` should be set to `on` with the `auth_jwt_keyfile_path` set to the public key path (nginx won't start if the `auth_jwt_use_keyfile` is set to `on` without a keyfile).
55-
That is the public key, rather than a PEM certificate. I.e.:
93+
When using an `RS*` algorithm with an inline key, be sure to set `auth_jwt_key` to the _public key_, rather than a PEM certificate. E.g.:
5694

5795
```
5896
auth_jwt_key "-----BEGIN PUBLIC KEY-----
@@ -66,40 +104,44 @@ oQIDAQAB
66104
-----END PUBLIC KEY-----";
67105
```
68106

69-
**OR**
107+
When using an `RS*` algorithm with a public key file, do as follows:
70108

71109
```
72110
auth_jwt_use_keyfile on;
73-
auth_jwt_keyfile_path "/etc/nginx/pub_key.pem";
111+
auth_jwt_keyfile_path "/path/to/pub_key.pem";
74112
```
75113

76-
A typical use would be to specify the key and loginurl on the main level
77-
and then only turn on the locations that you want to secure (not the login page).
78-
Unauthorized requests are given 302 "Moved Temporarily" responses with a ___location of the specified loginurl.
114+
A typical use would be to specify the key and login URL at the `http` level, and then only turn JWT authentication on for the locations which you want to secure. Unauthorized requests result in a 302 "Moved Temporarily" response with the `Location` header set to the URL specified in the `auth_jwt_loginurl` directive, and a querystring parameter `return_url` whose value is the current / attempted URL.
115+
116+
If you prefer to return `401 Unauthorized` rather than redirect, you may turn `auth_jwt_redirect` off:
79117

80118
```
81-
auth_jwt_redirect off;
119+
auth_jwt_redirect off;
82120
```
83-
If you prefer to return 401 Unauthorized, you may turn `auth_jwt_redirect` off.
121+
122+
By default the authorization header is used to provide a JWT for validation. However, you may use the `auth_jwt_validation_type` configuration to specify the name of a cookie that provides the JWT:
84123

85124
```
86-
auth_jwt_validation_type AUTHORIZATION;
87125
auth_jwt_validation_type COOKIE=jwt;
88126
```
89-
By default the authorization header is used to provide a JWT for validation.
90-
However, you may use the `auth_jwt_validation_type` configuration to specify the name of a cookie that provides the JWT.
91127

92-
```
93-
auth_jwt_extract_sub
94-
```
95128
By default, the module will attempt to extract the `sub` claim (e.g. the user's id) from the JWT. If successful, the
96129
value will be set in the `x-userid` HTTP header. An error will be logged if this option is enabled and the JWT does not
97-
contain the `sub` claim.
130+
contain the `sub` claim. You may disable this option as follows:
98131

99132
```
100-
auth_jwt_validate_email off;
133+
auth_jwt_extract_sub off
101134
```
135+
102136
By default, the module will attempt to validate the email address field of the JWT, then set the x-email header of the
103-
session, and will log an error if it isn't found. To disable this behavior, for instance if you are using a different
137+
session, and will log an error if it isn't found. To disable this behavior, for instance if you are using a different
104138
user identifier property such as `sub`, set `auth_jwt_validate_email` to the value `off`. _Note that this flag may be
105-
renamed to `auth_jwt_extract_email` in a future release._
139+
renamed to `auth_jwt_extract_email` in a future release._ You may disable this option as follows:
140+
141+
```
142+
auth_jwt_validate_email off;
143+
```
144+
145+
## Contributing
146+
147+
If you'd like to contribute to this repository, please first initiate the Git hooks by running `./bin/init` -- this will ensure that tests are run before you push your changes.

docker-compose-test.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.

resources/nginx.conf

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
1-
21
user nginx;
32
worker_processes auto;
43

54
error_log /var/log/nginx/error.log notice;
65
pid /var/run/nginx.pid;
76
load_module /usr/lib64/nginx/modules/ngx_http_auth_jwt_module.so;
87

9-
108
events {
119
worker_connections 1024;
1210
}
1311

14-
1512
http {
16-
include /etc/nginx/mime.types;
17-
default_type application/octet-stream;
13+
include /etc/nginx/mime.types;
14+
default_type application/octet-stream;
1815

19-
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
16+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
2017
'$status $body_bytes_sent "$http_referer" '
2118
'"$http_user_agent" "$http_x_forwarded_for"';
19+
access_log /var/log/nginx/access.log main;
2220

23-
access_log /var/log/nginx/access.log main;
24-
25-
sendfile on;
26-
#tcp_nopush on;
27-
28-
keepalive_timeout 65;
29-
30-
#gzip on;
21+
sendfile on;
22+
keepalive_timeout 65;
3123

3224
include /etc/nginx/conf.d/*.conf;
33-
}
25+
}

0 commit comments

Comments
 (0)