From 032fa5c14b93d309d88b585f084e4db7dbd2f53c Mon Sep 17 00:00:00 2001 From: Stephan Wurm Date: Mon, 18 Mar 2024 17:32:42 +0100 Subject: [PATCH 01/29] add support for ES algorithms (#118) Signed-off-by: Stephan Wurm --- src/ngx_http_auth_jwt_module.c | 6 +- test/Dockerfile-test-nginx | 3 + test/ec_key_256.pem | 5 ++ test/ec_key_384.pem | 6 ++ test/ec_key_521.pem | 8 ++ test/etc/nginx/conf.d/test.conf | 123 ++++++++++++++++++++++++++++++ test/etc/nginx/ec_key_256-pub.pem | 4 + test/etc/nginx/ec_key_384-pub.pem | 5 ++ test/etc/nginx/ec_key_521-pub.pem | 6 ++ test/test.sh | 39 ++++++++++ 10 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 test/ec_key_256.pem create mode 100644 test/ec_key_384.pem create mode 100644 test/ec_key_521.pem create mode 100644 test/etc/nginx/ec_key_256-pub.pem create mode 100644 test/etc/nginx/ec_key_384-pub.pem create mode 100644 test/etc/nginx/ec_key_521-pub.pem diff --git a/src/ngx_http_auth_jwt_module.c b/src/ngx_http_auth_jwt_module.c index edef14a..85a646d 100644 --- a/src/ngx_http_auth_jwt_module.c +++ b/src/ngx_http_auth_jwt_module.c @@ -337,7 +337,7 @@ static ngx_int_t handle_request(ngx_http_request_t *r) return redirect(r, jwtcf); } } - else if (algorithm.len == 5 && ngx_strncmp(algorithm.data, "RS", 2) == 0) + else if (algorithm.len == 5 && (ngx_strncmp(algorithm.data, "RS", 2) == 0 || ngx_strncmp(algorithm.data, "ES", 2) == 0)) { if (jwtcf->use_keyfile == 1) { @@ -394,7 +394,7 @@ static int validate_alg(auth_jwt_conf_t *jwtcf, jwt_t *jwt) { const jwt_alg_t alg = jwt_get_alg(jwt); - if (alg != JWT_ALG_HS256 && alg != JWT_ALG_HS384 && alg != JWT_ALG_HS512 && alg != JWT_ALG_RS256 && alg != JWT_ALG_RS384 && alg != JWT_ALG_RS512) + if (alg != JWT_ALG_HS256 && alg != JWT_ALG_HS384 && alg != JWT_ALG_HS512 && alg != JWT_ALG_RS256 && alg != JWT_ALG_RS384 && alg != JWT_ALG_RS512 && alg != JWT_ALG_ES256 && alg != JWT_ALG_ES384 && alg != JWT_ALG_ES512) { return 1; } @@ -633,7 +633,7 @@ static char *get_jwt(ngx_http_request_t *r, ngx_str_t jwt_location) if (ngx_strncmp(jwtHeaderVal->value.data, BEARER_PREFIX, strlen(BEARER_PREFIX)) == 0) { ngx_str_t jwtHeaderValWithoutBearer = jwtHeaderVal->value; - + jwtHeaderValWithoutBearer.data += strlen(BEARER_PREFIX); jwtHeaderValWithoutBearer.len -= strlen(BEARER_PREFIX); diff --git a/test/Dockerfile-test-nginx b/test/Dockerfile-test-nginx index b70ca9e..5f01436 100644 --- a/test/Dockerfile-test-nginx +++ b/test/Dockerfile-test-nginx @@ -9,4 +9,7 @@ RUN echo "Config Hash: ${CONFIG_HASH}" COPY /docker-entrypoint.d/* /docker-entrypoint.d/ COPY /etc/nginx/conf.d/test.conf /etc/nginx/conf.d/test.conf COPY /etc/nginx/rsa_key_2048-pub.pem /etc/nginx/rsa-key.conf +COPY /etc/nginx/ec_key_256-pub.pem /etc/nginx/ec-256-key.conf +COPY /etc/nginx/ec_key_384-pub.pem /etc/nginx/ec-384-key.conf +COPY /etc/nginx/ec_key_521-pub.pem /etc/nginx/ec-521-key.conf RUN sed -i "s|%{PORT}|${PORT}|" /etc/nginx/conf.d/test.conf diff --git a/test/ec_key_256.pem b/test/ec_key_256.pem new file mode 100644 index 0000000..4206969 --- /dev/null +++ b/test/ec_key_256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgOlEBGcZxxhv8FkN0 +YIvax6fnhJbMeotzIEBxIglkNu6hRANCAATP1NpDzvZmKd2Mw6hIrv4nzUfNu7OK +mT5VuL5LhvUgzTqVGuxwevA7DlFsNVSfCljIBG3geio3fcd4k0Z9SygL +-----END PRIVATE KEY----- diff --git a/test/ec_key_384.pem b/test/ec_key_384.pem new file mode 100644 index 0000000..2aa5780 --- /dev/null +++ b/test/ec_key_384.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDADyrL6llSQoQOZ/PF/ +l761kAbrTwn4vu30Kr34ScW6bRKVXLq3cT3QssJ1nF9B63qhZANiAAQ48dOfIEd3 +0TCVE0JT4ZU0Db7Ftz+ex7lojP7uqTY9OI59yoMB01zUN4JK30BRXS9Yv0A9Bu1z +fgLu93FSn0kd0zIPMvuu5LUt60M/miSt2lA0OrqFhKjx6FFdN/lNh64= +-----END PRIVATE KEY----- diff --git a/test/ec_key_521.pem b/test/ec_key_521.pem new file mode 100644 index 0000000..10471dc --- /dev/null +++ b/test/ec_key_521.pem @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAKkag6aVn4XAbaALo +0b3pypdP5RBX7uKxHmKlkNCcpA0oVTdgjnM5NpJP8ZOM6NjVhEzsn6c/Tdn8hL8w +SI55hFWhgYkDgYYABABpTipSvbs8fq44u4fA+v7DTNYViA58sqbrxjxdzwWZ8eEj +CXsH7yzSGx3Y19NSyrX8HbjWmrj5uxiKeFCB8mGzTwDcFIKCMeMkHjZs/fmVOumR +a2XSpj7BP6wqcN6Pf+UqECivGAZGRHoabo/dm5zF9M3gO+G9eOrf3G1wgFFM7Vzb +Ow== +-----END PRIVATE KEY----- diff --git a/test/etc/nginx/conf.d/test.conf b/test/etc/nginx/conf.d/test.conf index 00c990b..3421b5b 100644 --- a/test/etc/nginx/conf.d/test.conf +++ b/test/etc/nginx/conf.d/test.conf @@ -72,6 +72,51 @@ server { try_files index.html =404; } + location /secure/cookie/es256 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm ES256; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz +ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/es384 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm ES384; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 +aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 +LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/es512 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm ES512; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO +fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC +gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh +vXjq39xtcIBRTO1c2zs= +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + location /secure/auth-header/default { auth_jwt_enabled on; auth_jwt_redirect on; @@ -119,6 +164,48 @@ BwIDAQAB try_files index.html =404; } + location /secure/auth-header/es256 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz +ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es384 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 +aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 +LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es512 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO +fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC +gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh +vXjq39xtcIBRTO1c2zs= +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + location /secure/auth-header/rs256/file { auth_jwt_enabled on; auth_jwt_redirect on; @@ -155,6 +242,42 @@ BwIDAQAB try_files index.html =404; } + location /secure/auth-header/es256/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm ES256; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/ec-256-key.conf"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es384/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm ES384; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/ec-384-key.conf"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es512/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm ES512; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/ec-521-key.conf"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + location /secure/custom-header/hs256 { auth_jwt_enabled on; auth_jwt_redirect on; diff --git a/test/etc/nginx/ec_key_256-pub.pem b/test/etc/nginx/ec_key_256-pub.pem new file mode 100644 index 0000000..3306ea0 --- /dev/null +++ b/test/etc/nginx/ec_key_256-pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz +ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== +-----END PUBLIC KEY----- diff --git a/test/etc/nginx/ec_key_384-pub.pem b/test/etc/nginx/ec_key_384-pub.pem new file mode 100644 index 0000000..e642ed1 --- /dev/null +++ b/test/etc/nginx/ec_key_384-pub.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 +aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 +LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu +-----END PUBLIC KEY----- diff --git a/test/etc/nginx/ec_key_521-pub.pem b/test/etc/nginx/ec_key_521-pub.pem new file mode 100644 index 0000000..0cb875c --- /dev/null +++ b/test/etc/nginx/ec_key_521-pub.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO +fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC +gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh +vXjq39xtcIBRTO1c2zs= +-----END PUBLIC KEY----- diff --git a/test/test.sh b/test/test.sh index 29a0bf3..5671b8f 100755 --- a/test/test.sh +++ b/test/test.sh @@ -100,6 +100,10 @@ main() { local JWT_RS256_INVALID=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._aQmIBL4CVBxU1fNMOHp0kkagFaaX2TvAEenizytwd0 local JWT_RS384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.H35bTcZRhepWIoa8pKCbUMRuAOkVX9K5hJjc6tPmQwWmTw8lrktsvmMzJg_rgqnJLnAkciSIQw5EDj7fngS5zX2ThyRxrkPuE2Uiyw2Ect-mo9Kg1lrWgnyZCuCgq-Up9HQRAv0160mePlm8Gs4TOY6CPr38zwTcDZsy_Keq93igDQV8WuuWAGICaGd5ZyUOPjjzGShRjTU8Szz7fnpZpTtYRCYVo0pc5yfRWYm0fdn-4AseyGvd8JJ2xfnAEe4kZOkz7X1MLKtL0slKg3m2PH1lD7HwxIawXRTPWxArhJ9dcTNiDUrqtde2juGwOuMD_zTsb2Jj0_rmRb0Q6aljNw local JWT_RS512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.iUupyKypfXJ5aZWfItSW-mOmx9a4C4X7Yr5p5Fk8W75ZhkOq0EeNfstTxx870brhkdPovBhO2LYI44_HoH9XicQNL6JnFprE0r61eJFngbuzlhRQiWpq0xYrazJWc9zB7_GgL2ZCwtw-Ts3G23Q0632wVm6-d7MKvG7RS8aEjN-MuVGdtLglH3forpItmFxw-if40EQsBL7hncN_XNcQTO4KPHkqmlpac_oKXRrLFDIIt2tB6OOpvY4QcpERoxexp4pi2f-JoINnWX_dU5JnIs3ypVJLQPfoJvxg8fsg3zYrOvMYnfsqOCYoHtZGK0O7jyfFmcGo5v2hLT-CpoF3Zw + local JWT_ES256_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.WFfJXGr5whKHB7arjsTXPTJ6TAsS1LoRxu7Vj2_HrLaIQphWJM6BICf-M3cv52tFzt-XTZb6GxlDgAbHo8z9Zg + local JWT_ES256_INVALID=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._aQmIBL4CVBxU1fNMOHp0kkagFaaX2TvAEenizytwd0 + local JWT_ES384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._EFxXYOTAfT3gB3xUfgGR2UyXHeRTlDWqA94oZbB0DDa7YPZTEX9T4C_0ylnOFKZ6irGHZA8vxjgXDH3DZKWwBWcZ-XaQ_Q4Ws2J-AEeLqcl7_CS6q9mFo0Y7vUNEn-W + local JWT_ES512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.AFY4gNCtZNYkrTiijDkV4eKIt2UPMIuJBfZIk69jgI8FSGCQyUIMmIVg0fTvbaSiaryXzcjbG5TCm8a9Vu3KFJutAHGrgvZqcdklxx6Fbk3an3r_CH68n_ncwS3SUV58mDjf0OX8jRuNdudU1L5xYNQdodo-fxPIb1oHXfMJ0CmULDR9 run_test -n 'when auth disabled, should return 200' \ -p '/' \ @@ -173,6 +177,21 @@ main() { -c '200' \ -x ' --cookie "jwt=${JWT_RS256_VALID}"' + run_test -n 'when auth enabled with ES256 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/es256' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_ES256_VALID}"' + + run_test -n 'when auth enabled with ES384 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/es384' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_ES384_VALID}"' + + run_test -n 'when auth enabled with ES512 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/es512' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_ES512_VALID}"' + run_test -n 'when auth enabled with RS256 algorithm via file and valid JWT in Authorization header, returns 200' \ -p '/secure/auth-header/rs256/file' \ -c '200' \ @@ -193,6 +212,26 @@ main() { -c '200' \ -x '--header "Authorization: Bearer ${JWT_RS256_VALID}"' + run_test -n 'when auth enabled with ES256 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/es256/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_ES256_VALID}"' + + run_test -n 'when auth enabled with ES256 algorithm via file and invalid JWT in Authorization header, returns 401' \ + -p '/secure/auth-header/es256/file' \ + -c '302' \ + -x '--header "Authorization: Bearer ${JWT_ES256_INVALID}"' + + run_test -n 'when auth enabled with ES384 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/es384/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_ES384_VALID}"' + + run_test -n 'when auth enabled with ES512 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/es512/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_ES512_VALID}"' + run_test -n 'when auth enabled with HS256 algorithm and valid JWT in custom header without bearer, returns 200' \ -p '/secure/custom-header/hs256/' \ -c '200' \ From 03d95531d14afea1a2acbd023fd1cd8ae234881a Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 19 Mar 2024 14:32:57 -0400 Subject: [PATCH 02/29] build custom SSL images; add SSL tests (#126) --- .../workflows/{ci.yml => make-releases.yml} | 27 +-- Dockerfile | 59 ------- nginx.dockerfile | 136 +++++++++++++++ openssl.dockerfile | 37 ++++ scripts.sh | 165 ++++++++++++------ test/Dockerfile-test-nginx | 15 -- test/Dockerfile-test-runner | 13 -- test/docker-compose-test.yml | 13 +- test/docker-entrypoint.d/10-nginx-test.sh | 1 - test/etc/nginx/conf.d/test.conf | 19 +- test/etc/nginx/test.crt | 23 +++ test/etc/nginx/test.key | 28 +++ test/test-nginx.dockerfile | 18 ++ test/test-runner.dockerfile | 16 ++ test/test.sh | 64 ++++--- 15 files changed, 442 insertions(+), 192 deletions(-) rename .github/workflows/{ci.yml => make-releases.yml} (89%) delete mode 100644 Dockerfile create mode 100644 nginx.dockerfile create mode 100644 openssl.dockerfile delete mode 100644 test/Dockerfile-test-nginx delete mode 100644 test/Dockerfile-test-runner delete mode 100755 test/docker-entrypoint.d/10-nginx-test.sh create mode 100644 test/etc/nginx/test.crt create mode 100644 test/etc/nginx/test.key create mode 100644 test/test-nginx.dockerfile create mode 100644 test/test-runner.dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/make-releases.yml similarity index 89% rename from .github/workflows/ci.yml rename to .github/workflows/make-releases.yml index 3c31403..037823b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/make-releases.yml @@ -3,12 +3,12 @@ name: CI on: push: branches: - - 'master' + - master paths: - src/** pull_request: branches: - - 'master' + - master paths: - src/** workflow_dispatch: @@ -18,8 +18,9 @@ jobs: name: "NGINX: ${{ matrix.nginx-version }}; libjwt: ${{ matrix.libjwt-version }}" strategy: matrix: - # Each nginx version to build against + # NGINX versions to build/test against nginx-version: ['1.20.2', '1.22.1', '1.24.0', '1.25.3'] + # The following versions of libjwt are compatible: # * v1.0 - v1.12.0 # * v1.12.1 - v1.14.0 @@ -27,15 +28,16 @@ jobs: # At the time of writing this: # * Debian and Ubuntu's repos have v1.10.2 # * EPEL has v1.12.1 - # This compilles against each version prior to a breaking change and the latest release + # This compiles against each version prior to a breaking change and the latest release libjwt-version: ['1.12.0', '1.14.0', '1.15.3'] runs-on: ubuntu-latest steps: - - name: Checkout code + - name: Checkout Code uses: actions/checkout@v3 with: path: 'ngx-http-auth-jwt-module' + # TODO cache the build result so we don't have to do this every time? - name: Download jansson uses: actions/checkout@v3 with: @@ -50,7 +52,8 @@ jobs: make && \ make check && \ sudo make install - + + # TODO cache the build result so we don't have to do this every time? - name: Download libjwt uses: actions/checkout@v3 with: @@ -71,20 +74,22 @@ jobs: mkdir nginx curl -O http://nginx.org/download/nginx-${{matrix.nginx-version}}.tar.gz tar -xzf nginx-${{matrix.nginx-version}}.tar.gz --strip-components 1 -C nginx - - - name: Run configure + + - name: Configure NGINX working-directory: ./nginx run: | BUILD_FLAGS='' MAJ=$(echo ${{matrix.nginx-version}} | cut -f1 -d.) MIN=$(echo ${{matrix.nginx-version}} | cut -f2 -d.) REV=$(echo ${{matrix.nginx-version}} | cut -f3 -d.) + if [ "${MAJ}" -gt 1 ] || [ "${MAJ}" -eq 1 -a "${MIN}" -ge 23 ]; then - BUILD_FLAGS="${BUILD_FLAGS} --with-cc-opt='-DNGX_LINKED_LIST_COOKIES=1'" + BUILD_FLAGS="${BUILD_FLAGS} --with-cc-opt='-DNGX_LINKED_LIST_COOKIES=1'" fi - ./configure --with-compat --add-dynamic-module=../ngx-http-auth-jwt-module ${BUILD_FLAGS} - - name: Run make + ./configure --with-compat --add-dynamic-module=../ngx-http-auth-jwt-module ${BUILD_FLAGS} + + - name: Make Modules working-directory: ./nginx run: make modules diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 4f2db13..0000000 --- a/Dockerfile +++ /dev/null @@ -1,59 +0,0 @@ -ARG NGINX_VERSION -ARG SOURCE_HASH - - -FROM debian:bullseye-slim as ngx_http_auth_jwt_builder_base -LABEL stage=ngx_http_auth_jwt_builder -RUN <<` -apt-get update -apt-get install -y curl build-essential -` - - -FROM ngx_http_auth_jwt_builder_base as ngx_http_auth_jwt_builder_module -LABEL stage=ngx_http_auth_jwt_builder -ENV LD_LIBRARY_PATH=/usr/local/lib -ARG NGINX_VERSION -RUN <<` -apt-get install -y libjwt-dev libjwt0 libjansson-dev libjansson4 libpcre2-dev zlib1g-dev libpcre3-dev -mkdir -p /root/build/ngx-http-auth-jwt-module -` -WORKDIR /root/build/ngx-http-auth-jwt-module -ARG SOURCE_HASH -RUN echo "Source Hash: ${SOURCE_HASH}" -ADD config ./ -ADD src/*.h src/*.c ./src/ -WORKDIR /root/build -RUN <<` -mkdir nginx -curl -O http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz -tar -xzf nginx-${NGINX_VERSION}.tar.gz --strip-components 1 -C nginx -` -WORKDIR /root/build/nginx -RUN <<` -BUILD_FLAGS='' -MAJ=$(echo ${NGINX_VERSION} | cut -f1 -d.) -MIN=$(echo ${NGINX_VERSION} | cut -f2 -d.) -REV=$(echo ${NGINX_VERSION} | cut -f3 -d.) - -# NGINX 1.23.0+ changes cookies to use a linked list, and renames `cookies` to `cookie` -if [ "${MAJ}" -gt 1 ] || [ "${MAJ}" -eq 1 -a "${MIN}" -ge 23 ]; then - BUILD_FLAGS="${BUILD_FLAGS} --with-cc-opt='-DNGX_LINKED_LIST_COOKIES=1'" -fi - -./configure --with-compat --add-dynamic-module=../ngx-http-auth-jwt-module ${BUILD_FLAGS} -make modules -` - - -FROM nginx:${NGINX_VERSION} AS ngx_http_auth_jwt_builder_nginx -LABEL stage= -RUN rm /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh /etc/nginx/conf.d/default.conf -RUN <<` -apt-get update -apt-get -y install libjansson4 libjwt0 -cd /etc/nginx -sed -ri '/pid\s+\/var\/run\/nginx\.pid;$/a load_module \/usr\/lib64\/nginx\/modules\/ngx_http_auth_jwt_module\.so;' nginx.conf -` -LABEL maintainer="TeslaGov" email="developers@teslagov.com" -COPY --from=ngx_http_auth_jwt_builder_module /root/build/nginx/objs/ngx_http_auth_jwt_module.so /usr/lib64/nginx/modules/ diff --git a/nginx.dockerfile b/nginx.dockerfile new file mode 100644 index 0000000..21c7460 --- /dev/null +++ b/nginx.dockerfile @@ -0,0 +1,136 @@ +ARG BASE_IMAGE +ARG NGINX_VERSION + + +FROM ${BASE_IMAGE} as ngx_http_auth_jwt_builder_base +LABEL stage=ngx_http_auth_jwt_builder +RUN <<` +apt-get update +apt-get install -y curl build-essential +` + + +FROM ngx_http_auth_jwt_builder_base as ngx_http_auth_jwt_builder_module +LABEL stage=ngx_http_auth_jwt_builder +ENV PATH "${PATH}:/etc/nginx" +ENV LD_LIBRARY_PATH=/usr/local/lib +ARG NGINX_VERSION +RUN <<` + set -e + apt-get install -y libjwt-dev libjwt0 libjansson-dev libjansson4 libpcre2-dev zlib1g-dev libpcre3-dev + mkdir -p /root/build/ngx-http-auth-jwt-module +` +WORKDIR /root/build/ngx-http-auth-jwt-module +ADD config ./ +ADD src/*.h src/*.c ./src/ +WORKDIR /root/build +RUN <<` + set -e + mkdir nginx + curl -O http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz + tar -xzf nginx-${NGINX_VERSION}.tar.gz --strip-components 1 -C nginx +` +WORKDIR /root/build/nginx +RUN <<` + set -e + BUILD_FLAGS='' + MAJ=$(echo ${NGINX_VERSION} | cut -f1 -d.) + MIN=$(echo ${NGINX_VERSION} | cut -f2 -d.) + REV=$(echo ${NGINX_VERSION} | cut -f3 -d.) + + # NGINX 1.23.0+ changes cookies to use a linked list, and renames `cookies` to `cookie` + if [ "${MAJ}" -gt 1 ] || [ "${MAJ}" -eq 1 -a "${MIN}" -ge 23 ]; then + BUILD_FLAGS="${BUILD_FLAGS} --with-cc-opt='-DNGX_LINKED_LIST_COOKIES=1'" + fi + + ./configure \ + --prefix=/etc/nginx \ + --sbin-path=/usr/sbin/nginx \ + --modules-path=/usr/lib64/nginx/modules \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --pid-path=/var/run/nginx.pid \ + --lock-path=/var/run/nginx.lock \ + --http-client-body-temp-path=/var/cache/nginx/client_temp \ + --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ + --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \ + --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ + --http-scgi-temp-path=/var/cache/nginx/scgi_temp \ + --user=nginx \ + --group=nginx \ + --with-compat \ + --with-debug \ + --with-file-aio \ + --with-threads \ + --with-http_addition_module \ + --with-http_auth_request_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_gunzip_module \ + --with-http_gzip_static_module \ + --with-http_mp4_module \ + --with-http_random_index_module \ + --with-http_realip_module \ + --with-http_secure_link_module \ + --with-http_slice_module \ + --with-http_ssl_module \ + --with-http_stub_status_module \ + --with-http_sub_module \ + --with-http_v2_module \ + --with-mail \ + --with-mail_ssl_module \ + --with-stream \ + --with-stream_realip_module \ + --with-stream_ssl_module \ + --with-stream_ssl_preread_module \ + --with-cc-opt='-g -O2 -ffile-prefix-map=/data/builder/debuild/nginx-1.25.4/debian/debuild-base/nginx-1.25.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' \ + --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' \ + --add-dynamic-module=../ngx-http-auth-jwt-module \ + ${BUILD_FLAGS} + # --with-openssl=/usr/local \ +` +RUN make modules +RUN make install +WORKDIR /usr/lib64/nginx/modules +RUN cp /root/build/nginx/objs/ngx_http_auth_jwt_module.so . +RUN rm -rf /root/build +RUN adduser --system --no-create-home --shell /bin/false --group --disabled-login nginx +RUN mkdir -p /var/cache/nginx /var/log/nginx +WORKDIR /etc/nginx + +FROM ngx_http_auth_jwt_builder_module AS ngx_http_auth_jwt_nginx +LABEL maintainer="TeslaGov" email="developers@teslagov.com" +ARG NGINX_VERSION +RUN <<` + set -e + + apt-get update + apt-get install -y libjansson4 libjwt0 + apt-get clean +` +COPY <<` /etc/nginx/nginx.conf +user nginx; +pid /var/run/nginx.pid; + +load_module /usr/lib64/nginx/modules/ngx_http_auth_jwt_module.so; + +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + include 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; + include conf.d/*.conf; +} +` +ENTRYPOINT ["nginx", "-g", "daemon off;"] diff --git a/openssl.dockerfile b/openssl.dockerfile new file mode 100644 index 0000000..45140cc --- /dev/null +++ b/openssl.dockerfile @@ -0,0 +1,37 @@ +ARG BASE_IMAGE + +FROM ${BASE_IMAGE} +ARG SRC_DIR=/tmp/openssl-src +ARG OUT_DIR=/usr/local/.openssl +ARG SSL_VERSION +RUN <<` + set -e + apt-get update + apt-get install -y curl build-essential libssl-dev libz-dev + apt-get remove -y openssl + apt-get clean +` +WORKDIR ${SRC_DIR} +RUN <<` + set -e + curl --silent -O https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz + tar -xf openssl-${SSL_VERSION}.tar.gz --strip-components=1 +` +RUN ./config --prefix=${OUT_DIR} --openssldir=${OUT_DIR} shared zlib +RUN <<` + set -e + make + make test + make install +` +RUN <<` + set -e + echo "${OUT_DIR}/lib" > /etc/ld.so.conf.d/openssl-${SSL_VERSION}.conf + ldconfig + + ln -sf ${OUT_DIR}/bin/openssl /usr/bin/openssl + ln -sf ${OUT_DIR}/lib64/libssl.so.3 /lib/x86_64-linux-gnu/libssl.so.3 + ln -sf ${OUT_DIR}/lib64/libcrypto.so.3 /lib/x86_64-linux-gnu/libcrypto.so.3 +` +WORKDIR / +#RUN rm -rf ${SRC_DIR} \ No newline at end of file diff --git a/scripts.sh b/scripts.sh index e3fb254..421a4cb 100755 --- a/scripts.sh +++ b/scripts.sh @@ -1,40 +1,72 @@ #!/bin/bash -eu +MAGENTA='\u001b[35m' BLUE='\033[0;34m' GREEN='\033[0;32m' RED='\033[0;31m' NC='\033[0m' +# supported SSL versions +SSL_VERSION_1_1_1w='1.1.1w' +SSL_VERSION_3_0_11='3.0.11' +SSL_VERSION_3_2_1='3.2.1' +SSL_VERSIONS=(${SSL_VERSION_3_2_1}) +SSL_VERSION=${SSL_VERSION:-$SSL_VERSION_3_0_11} + +declare -A SSL_IMAGE_MAP +SSL_IMAGE_MAP[$SSL_VERSION_1_1_1w]="bullseye-slim:openssl-${SSL_VERSION_1_1_1w}" +SSL_IMAGE_MAP[$SSL_VERSION_3_0_11]="bookworm-slim:openssl-${SSL_VERSION_3_0_11}" +SSL_IMAGE_MAP[$SSL_VERSION_3_2_1]="bookworm-slim:openssl-${SSL_VERSION_3_2_1}" + # supported NGINX versions -- for binary distribution -NGINX_VERSION_MAINLINE='1.25.4' +NGINX_VERSION_LEGACY_1='1.20.2' +NGINX_VERSION_LEGACY_2='1.22.1' NGINX_VERSION_STABLE='1.24.0' -NGINX_VERSION_LEGACY_1='1.22.1' -NGINX_VERSION_LEGACY_2='1.20.2' +NGINX_VERSION_MAINLINE='1.25.4' +NGINX_VERSIONS=(${NGINX_VERSION_LEGACY_1} ${NGINX_VERSION_LEGACY_2} ${NGINX_VERSION_STABLE} ${NGINX_VERSION_MAINLINE}) +NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSION_STABLE}} -export ORG_NAME=${ORG_NAME:-teslagov} -export IMAGE_NAME=${IMAGE_NAME:-jwt-nginx} -export FULL_IMAGE_NAME=${ORG_NAME}/${IMAGE_NAME} -export CONTAINER_NAME_PREFIX=${CONTAINER_NAME_PREFIX:-jwt-nginx-test} -export NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSION_STABLE}} +IMAGE_NAME=${IMAGE_NAME:-nginx-auth-jwt} +FULL_IMAGE_NAME=${ORG_NAME:-teslagov}/${IMAGE_NAME} + +TEST_CONTAINER_NAME_PREFIX="${IMAGE_NAME}-test" all() { build_module - build_test_runner - test + build_test + test_all +} + +verify_and_build_base_image() { + local image=${SSL_IMAGE_MAP[$SSL_VERSION]} + local baseImage=${image%%:*} + + if [ -z ${image} ]; then + echo "Base image not set for SSL version :${SSL_VERSION}" + exit 1 + else + printf "${MAGENTA}Building base image for SSL ${SSL_VERSION}...${NC}\n" + docker image build \ + --build-arg BASE_IMAGE=debian:${baseImage} \ + --build-arg SSL_VERSION=${SSL_VERSION} \ + -f openssl.dockerfile \ + -t ${image} . + fi } build_module() { local dockerArgs=${1:-} - local sourceHash=$(get_hash config src/*) - - printf "${BLUE}Pulling images...${NC}\n" - docker image pull debian:bullseye-slim - docker image pull nginx:${NGINX_VERSION} + local baseImage=${SSL_IMAGE_MAP[$SSL_VERSION]} + + verify_and_build_base_image - printf "${BLUE}Building module for NGINX ${NGINX_VERSION}...${NC}\n" - docker image build -t ${FULL_IMAGE_NAME}:latest -t ${FULL_IMAGE_NAME}:${NGINX_VERSION} ${dockerArgs} \ + printf "${MAGENTA}Building module for NGINX ${NGINX_VERSION}...${NC}\n" + docker image build \ + -f nginx.dockerfile \ + -t ${FULL_IMAGE_NAME}:${NGINX_VERSION} \ + --build-arg BASE_IMAGE=${baseImage} \ --build-arg NGINX_VERSION=${NGINX_VERSION} \ - --build-arg SOURCE_HASH=${sourceHash} . + ${dockerArgs} . if [ "$?" -ne 0 ]; then printf "${RED}✘ Build failed ${NC}\n" @@ -55,7 +87,7 @@ clean_module() { start_nginx() { local port=$(get_port) - printf "${BLUE}Starting NGINX container (${IMAGE_NAME}) on port ${port}...${NC}\n" + printf "${MAGENTA}Starting NGINX container (${IMAGE_NAME}) on port ${port}...${NC}\n" docker run --rm --name "${IMAGE_NAME}" -d -p ${port}:80 ${FULL_IMAGE_NAME} >/dev/null } @@ -72,7 +104,7 @@ cp_bin() { stopContainer=1 fi - printf "${BLUE}Copying binaries to: ${destDir}${NC}\n" + printf "${MAGENTA}Copying binaries to: ${destDir}${NC}\n" rm -rf ${destDir}/* mkdir -p ${destDir} docker exec "${IMAGE_NAME}" sh -c "cd /; tar -chf - \ @@ -81,7 +113,7 @@ cp_bin() { usr/lib/x86_64-linux-gnu/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null if [ $stopContainer ]; then - printf "${BLUE}Stopping NGINX container (${IMAGE_NAME})...${NC}\n" + printf "${MAGENTA}Stopping NGINX container (${IMAGE_NAME})...${NC}\n" stop_nginx fi } @@ -93,7 +125,7 @@ make_release() { NGINX_VERSION=${2} - printf "${BLUE}Making release for version ${moduleVersion} for NGINX ${NGINX_VERSION}...${NC}\n" + printf "${MAGENTA}Making release for version ${moduleVersion} for NGINX ${NGINX_VERSION}...${NC}\n" rebuild_module rebuild_test_runner @@ -110,61 +142,88 @@ make_release() { # See: https://nginx.org/en/download.html make_releases() { local moduleVersion=$(git describe --tags --abbrev=0) - local nginxVersions=(${NGINX_VERSION_MAINLINE} ${NGINX_VERSION_STABLE} ${NGINX_VERSION_LEGACY_1} ${NGINX_VERSION_LEGACY_2}) rm -rf release/* - for v in ${nginxVersions[@]}; do + for v in ${NGINX_VERSIONS[@]}; do make_release ${moduleVersion} ${v} done } - -build_test_runner() { +build_test() { local dockerArgs=${1:-} - local configHash=$(get_hash $(find test -type f -not -name 'test.sh' -not -name '*.yml' -not -name 'Dockerfile*')) - local sourceHash=$(get_hash test/test.sh) local port=$(get_port) + local sslPort=$(get_port $((port + 1))) + local runnerBaseImage=${SSL_IMAGE_MAP[$SSL_VERSION]} - printf "${BLUE}Building test runner using port ${port}...${NC}\n" - docker compose -f ./test/docker-compose-test.yml build ${dockerArgs} \ - --build-arg CONFIG_HASH=${configHash}\ - --build-arg SOURCE_HASH=${sourceHash} \ - --build-arg PORT=${port} + export TEST_CONTAINER_NAME_PREFIX + export FULL_IMAGE_NAME + export NGINX_VERSION + + printf "${MAGENTA}Building test NGINX & runner using port ${port}...${NC}\n" + docker compose \ + -p ${TEST_CONTAINER_NAME_PREFIX} \ + -f ./test/docker-compose-test.yml build \ + --build-arg RUNNER_BASE_IMAGE=${runnerBaseImage} \ + --build-arg PORT=${port} \ + --build-arg SSL_PORT=${sslPort} \ + ${dockerArgs} } -rebuild_test_runner() { - build_test_runner --no-cache +rebuild_test() { + build_test --no-cache +} + +test_all() { + for SSL_VERSION in "${SSL_VERSIONS[@]}"; do + for NGINX_VERSION in "${NGINX_VERSIONS[@]}"; do + test + done + done } test() { - build_test_runner + build_module + build_test - printf "${BLUE}Running tests...${NC}\n" - docker compose -f ./test/docker-compose-test.yml up --no-start - docker start ${CONTAINER_NAME_PREFIX} - - if [ "$(docker container inspect -f '{{.State.Running}}' ${CONTAINER_NAME_PREFIX})" != "true" ]; then - printf "${RED}Failed to start NGINX test container. See logs below:\n" - docker logs ${CONTAINER_NAME_PREFIX} - printf "${NC}\n" - else - test_now - fi + printf "${MAGENTA}Running tests...${NC}\n" + docker compose \ + -p ${TEST_CONTAINER_NAME_PREFIX} \ + -f ./test/docker-compose-test.yml up \ + --no-start + + + trap 'docker compose -f ./test/docker-compose-test.yml down' 0 - docker compose -f ./test/docker-compose-test.yml down + test_now } test_now() { - docker start -a ${CONTAINER_NAME_PREFIX}-runner -} + nginxContainerName="${TEST_CONTAINER_NAME_PREFIX}-nginx" + runnerContainerName="${TEST_CONTAINER_NAME_PREFIX}-runner" -get_hash() { - sha1sum $@ | sed -E 's|\s+|:|' | tr '\n' ' ' | sha1sum | head -c 40 + docker start ${nginxContainerName} + + if [ "$(docker container inspect -f '{{.State.Running}}' ${nginxContainerName})" != "true" ]; then + printf "${RED}Failed to start container \"${nginxContainerName}\". See logs below:\n" + docker logs ${nginxContainerName} + printf "${NC}\n" + return + fi + + docker start -a ${runnerContainerName} + + echo + echo "Tests were executed with the following options:" + echo " SSL Version: ${SSL_VERSION}" + echo " NGINX Version: ${NGINX_VERSION}" } get_port() { - for p in $(seq 8000 8100); do + startPort=${1:-8000} + endPort=$((startPort + 100)) + + for p in $(seq ${startPort} ${endPort}); do if ! ss -ln | grep -q ":${p} "; then echo ${p} break diff --git a/test/Dockerfile-test-nginx b/test/Dockerfile-test-nginx deleted file mode 100644 index 5f01436..0000000 --- a/test/Dockerfile-test-nginx +++ /dev/null @@ -1,15 +0,0 @@ -ARG BASE_IMAGE -ARG CONFIG_HASH -ARG PORT - -FROM ${BASE_IMAGE} as NGINX -ARG CONFIG_HASH -ARG PORT -RUN echo "Config Hash: ${CONFIG_HASH}" -COPY /docker-entrypoint.d/* /docker-entrypoint.d/ -COPY /etc/nginx/conf.d/test.conf /etc/nginx/conf.d/test.conf -COPY /etc/nginx/rsa_key_2048-pub.pem /etc/nginx/rsa-key.conf -COPY /etc/nginx/ec_key_256-pub.pem /etc/nginx/ec-256-key.conf -COPY /etc/nginx/ec_key_384-pub.pem /etc/nginx/ec-384-key.conf -COPY /etc/nginx/ec_key_521-pub.pem /etc/nginx/ec-521-key.conf -RUN sed -i "s|%{PORT}|${PORT}|" /etc/nginx/conf.d/test.conf diff --git a/test/Dockerfile-test-runner b/test/Dockerfile-test-runner deleted file mode 100644 index c8cbff2..0000000 --- a/test/Dockerfile-test-runner +++ /dev/null @@ -1,13 +0,0 @@ -ARG SOURCE_HASH -ARG PORT - -FROM alpine:3.7 AS test-base -RUN apk add curl bash - -FROM test-base AS test -ARG SOURCE_HASH -ARG PORT -ENV PORT=${PORT} -RUN echo "Source Hash: ${SOURCE_HASH}" -COPY test.sh . -CMD ./test.sh ${PORT} diff --git a/test/docker-compose-test.yml b/test/docker-compose-test.yml index eff2460..3c0e9be 100644 --- a/test/docker-compose-test.yml +++ b/test/docker-compose-test.yml @@ -3,23 +3,20 @@ version: '3.3' services: nginx: - container_name: ${CONTAINER_NAME_PREFIX} + container_name: ${TEST_CONTAINER_NAME_PREFIX}-nginx build: context: . - dockerfile: Dockerfile-test-nginx + dockerfile: test-nginx.dockerfile args: - BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION:-latest} - command: [nginx-debug, '-g', 'daemon off;'] + BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION} logging: driver: ${LOG_DRIVER:-journald} runner: - container_name: ${CONTAINER_NAME_PREFIX}-runner + container_name: ${TEST_CONTAINER_NAME_PREFIX}-runner build: context: . - dockerfile: Dockerfile-test-runner - environment: - BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION:-latest} + dockerfile: test-runner.dockerfile depends_on: - nginx \ No newline at end of file diff --git a/test/docker-entrypoint.d/10-nginx-test.sh b/test/docker-entrypoint.d/10-nginx-test.sh deleted file mode 100755 index 0bf8791..0000000 --- a/test/docker-entrypoint.d/10-nginx-test.sh +++ /dev/null @@ -1 +0,0 @@ -nginx -t \ No newline at end of file diff --git a/test/etc/nginx/conf.d/test.conf b/test/etc/nginx/conf.d/test.conf index 3421b5b..229d545 100644 --- a/test/etc/nginx/conf.d/test.conf +++ b/test/etc/nginx/conf.d/test.conf @@ -3,7 +3,13 @@ access_log /var/log/nginx/access.log; server { listen %{PORT}; + listen %{SSL_PORT} ssl; server_name localhost; + + ssl_certificate /etc/nginx/test.crt; + ssl_certificate_key /etc/nginx/test.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; auth_jwt_key "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF"; auth_jwt_loginurl "https://example.com/login"; @@ -212,7 +218,7 @@ vXjq39xtcIBRTO1c2zs= auth_jwt_location HEADER=Authorization; auth_jwt_algorithm RS256; auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/rsa-key.conf"; + auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; alias /usr/share/nginx/html/; try_files index.html =404; @@ -224,7 +230,7 @@ vXjq39xtcIBRTO1c2zs= auth_jwt_location HEADER=Authorization; auth_jwt_algorithm RS384; auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/rsa-key.conf"; + auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; alias /usr/share/nginx/html/; try_files index.html =404; @@ -236,7 +242,7 @@ vXjq39xtcIBRTO1c2zs= auth_jwt_location HEADER=Authorization; auth_jwt_algorithm RS512; auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/rsa-key.conf"; + auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; alias /usr/share/nginx/html/; try_files index.html =404; @@ -248,7 +254,7 @@ vXjq39xtcIBRTO1c2zs= auth_jwt_location HEADER=Authorization; auth_jwt_algorithm ES256; auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/ec-256-key.conf"; + auth_jwt_keyfile_path "/etc/nginx/ec_key_256-pub.pem"; alias /usr/share/nginx/html/; try_files index.html =404; @@ -260,7 +266,7 @@ vXjq39xtcIBRTO1c2zs= auth_jwt_location HEADER=Authorization; auth_jwt_algorithm ES384; auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/ec-384-key.conf"; + auth_jwt_keyfile_path "/etc/nginx/ec_key_384-pub.pem"; alias /usr/share/nginx/html/; try_files index.html =404; @@ -272,7 +278,7 @@ vXjq39xtcIBRTO1c2zs= auth_jwt_location HEADER=Authorization; auth_jwt_algorithm ES512; auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/ec-521-key.conf"; + auth_jwt_keyfile_path "/etc/nginx/ec_key_521-pub.pem"; alias /usr/share/nginx/html/; try_files index.html =404; @@ -390,4 +396,3 @@ vXjq39xtcIBRTO1c2zs= } } } - diff --git a/test/etc/nginx/test.crt b/test/etc/nginx/test.crt new file mode 100644 index 0000000..fb406ba --- /dev/null +++ b/test/etc/nginx/test.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUMG9M4Itu0cOyX0+La+7huiIoX6YwDQYJKoZIhvcNAQEL +BQAwcTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRUwEwYDVQQHDAxG +YWxscyBDaHVyY2gxHzAdBgNVBAoMFlRlc2xhIEdvdmVybm1lbnQsIEluYy4xFzAV +BgNVBAsMDk5HSU5YIEF1dGggSldUMB4XDTI0MDMxNTE4MTM1MloXDTM0MDMxMzE4 +MTM1MlowcTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRUwEwYDVQQH +DAxGYWxscyBDaHVyY2gxHzAdBgNVBAoMFlRlc2xhIEdvdmVybm1lbnQsIEluYy4x +FzAVBgNVBAsMDk5HSU5YIEF1dGggSldUMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAih41Ct5XgcSTz7ZVAjBb0t0z9Qae08aseoMEKJf7AmNqKtsvzeAw +/DJxOWJR5VPtUWhFAmXxPfG2B6aiSIVJVpG9yzcdQlCvyJG7Ub4QCm5GXwpU+zDC +qmD5ksz9QMdOzvRLypAU1ciZiCXjwpUnW+BZyZ9Tpmsxm6/gOzkd3rxoIbc9uXxp +5o4n6k02EPSzLzUhkZnhLQrOAGUB7+q11FAU5eNMlTWC9gQUsbNaTVtKmM2eV9BA +UHdX2GbkfFbN22l3Wey4oyNZWmye1ZFOPyBR+tyU3pofhb+R+hTFmeNBzrJq3i30 +Qi0B8AnulKdOjnTysPYjDTrN6xcVDWNmPQIDAQABo1MwUTAdBgNVHQ4EFgQUczdy +7s64NJHNGsQTf/zwFnQe6LMwHwYDVR0jBBgwFoAUczdy7s64NJHNGsQTf/zwFnQe +6LMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfcxCiz6ShHof +lXiE2j+s556SM2n8oW/S1BSjFC2wF1uKVeMJA1gAaWObC3ElqffFlqTdCorhgRS/ +knWa+Sqe/jWBSgwLG/e5DvxXWjD7b7kZdAZNy9evs5nhVfcLT+GyvB/z5GdAFY7s +xYmLrC07ubhHIL9h7lhNKbRr++o+BcClQBZKRO4fxBwXxqx/rHudjH87Wr61Ov52 +90xNjwcqvevY0skmPao5+oyxkURdKZualNxiOGMPpywkpJkfl8Az5xKAJhUMAtFR +smhQduejEkcxfxtsiYgVoulI29GAsMr9zHps9zb5k0+SWIiSixjQ0CpRhLcNYu4F +QPgLQLGwUQ== +-----END CERTIFICATE----- diff --git a/test/etc/nginx/test.key b/test/etc/nginx/test.key new file mode 100644 index 0000000..13ec754 --- /dev/null +++ b/test/etc/nginx/test.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCKHjUK3leBxJPP +tlUCMFvS3TP1Bp7Txqx6gwQol/sCY2oq2y/N4DD8MnE5YlHlU+1RaEUCZfE98bYH +pqJIhUlWkb3LNx1CUK/IkbtRvhAKbkZfClT7MMKqYPmSzP1Ax07O9EvKkBTVyJmI +JePClSdb4FnJn1OmazGbr+A7OR3evGghtz25fGnmjifqTTYQ9LMvNSGRmeEtCs4A +ZQHv6rXUUBTl40yVNYL2BBSxs1pNW0qYzZ5X0EBQd1fYZuR8Vs3baXdZ7LijI1la +bJ7VkU4/IFH63JTemh+Fv5H6FMWZ40HOsmreLfRCLQHwCe6Up06OdPKw9iMNOs3r +FxUNY2Y9AgMBAAECggEAAkwEggGp/xb67FCyDJ8rdimTZFPi9U7coUCN8HNI/qrf +lTnfvox0oOUUqMMmIIQeS/HJ4ANvZe8GO3QkE8R5Sg7F0yjZL2tyTCNPgOMCMK8E +mmHS58brHdrbm658C1ILnfmssjNmNueNbuW00Koa8imCsY2ZEW+L7vTKuMFqg6c+ +BDJxC4yoCPwSTVfcajjzI6FVfphE0pd8Ho/sE8vTqdmovh23+vgfNUq1L9Smvf7R +YLM+hS1ouRP2BI5AN0sm04Kxd8MKPzuwCxteoZ9Y9YHyr1JeWGTTL0T24+LwUee/ +24zXZFrzpTgmtDYeEuVWsF5bP/fMS4Fctda3pdJMsQKBgQDCANjGDwwfSCCev2kl +WdrFJywhn5hWLWFwlo/FwLOsFJtejaBwIDRQCMPZ74H+KMHwUnO3vTanKJWqDRP9 +CdMh94C1BqobRV6rN4HgA4Opxim1EyRWHV6ui41zokk2mJrwUzKkR8t9lt9EZKrk +ZPyKER9A4hBqBmYvaYxodN8U1QKBgQC2QXUQq9j7niT7t4xMi0e9vnPLs0z1yUK9 +0nzKwTHDPflk3o2sKvH7199qVkc15JQ9DQ7NuYD7ezLbE3DJuVzpNDAfNXmfWHmp +7ukdnxyn6ZCmzQY7/fTpJTEGKVQMVCgf2f5ANgxm5EmN0yWRMcEt1VXIwCisY56p +o6nwv/1fyQKBgQCJBnIVyjEEszwfBBEvCX0kvVtFUGUXkSv+isl3onkFNPTcXuoP +6B8q3FYAy1MkggMhTAthnqpIfLjhCCWzFspidl8Y/WEOq/uGsUjxQWowcr+onqGO +lWX3oKfDIb/WaQkeb5UYRYFr7jE6LGQrt0xL9HX/rOxtBqIMIN/EM7ARFQKBgDAJ +zMtaIFUh9+mJFafPRleS7X6RggV+yOKzqkTe6zjlCuk1Z+4rW6Df43lpyFdCKnh1 +CqPa805VyK/Jzf69pumo4c44EBiZ/2d1G2i9WZZAj+oHPE9vvq/9J5DSL98YB4Nt +uABAvsAYB/Mj5lEA5kQoaPYDADWABH/+LXrRf/1RAoGAUvxPvmpkGMC+KdmjLam7 +CPC3+y4MZOyZ11BhOxLhd1K2qcQd9K7tkjUhNxRn5GVzpzOKeFJFtiih2uN+PBNJ +oylPR03uk/7D52b1OYaJhs9bQkth//Qk935nyRM26C2vG4tQLfT/cFi5F53n0ZCQ +7e8O6+QY0lZnpvsfnt8YIsM= +-----END PRIVATE KEY----- diff --git a/test/test-nginx.dockerfile b/test/test-nginx.dockerfile new file mode 100644 index 0000000..c4a6104 --- /dev/null +++ b/test/test-nginx.dockerfile @@ -0,0 +1,18 @@ +ARG BASE_IMAGE +ARG PORT +ARG SSL_PORT + +FROM ${BASE_IMAGE} as NGINX +ARG PORT +ARG SSL_PORT +COPY etc/ /etc/ +RUN sed -i "s|%{PORT}|${PORT}|" /etc/nginx/conf.d/test.conf +RUN sed -i "s|%{SSL_PORT}|${SSL_PORT}|" /etc/nginx/conf.d/test.conf +COPY <<` /usr/share/nginx/html/index.html + + Test + +

NGINX Auth-JWT Module Test

+ + +` diff --git a/test/test-runner.dockerfile b/test/test-runner.dockerfile new file mode 100644 index 0000000..0aca095 --- /dev/null +++ b/test/test-runner.dockerfile @@ -0,0 +1,16 @@ +ARG RUNNER_BASE_IMAGE +ARG PORT +ARG SSL_PORT + +FROM ${RUNNER_BASE_IMAGE} +ARG PORT +ARG SSL_PORT +ENV PORT=${PORT} +ENV SSL_PORT=${SSL_PORT} +RUN <<` + set -e + apt-get update + apt-get install -y curl bash +` +COPY test.sh . +CMD ./test.sh ${PORT} ${SSL_PORT} diff --git a/test/test.sh b/test/test.sh index 5671b8f..f54e0de 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,7 +1,6 @@ -#!/bin/bash -u +#!/bin/bash -eu # set a test # here to execute only that test and output additional info -PORT=${1:-8000} DEBUG= RED='\e[31m' @@ -18,35 +17,40 @@ run_test () { if [ "${DEBUG}" == '' ] || [ ${DEBUG} == ${NUM_TESTS} ]; then local OPTIND; - local name='' - local path='' - local expectedCode='' - local expectedResponseRegex='' - local extraCurlOpts='' - local curlCommand='' - local exitCode='' - local response='' + local name= + local path= + local expectedCode= + local expectedResponseRegex= + local extraCurlOpts= + local scheme='http' + local port=${PORT} + local curlCommand= + local exitCode= + local response= local testNum="${GRAY}${NUM_TESTS}${NC}\t" - while getopts "n:p:r:c:x:" option; do + while getopts "n:sp:r:c:x:" option; do case $option in - n) - name=$OPTARG;; - p) - path=$OPTARG;; - c) - expectedCode=$OPTARG;; - r) - expectedResponseRegex=$OPTARG;; - x) - extraCurlOpts=$OPTARG;; - \?) # Invalid option - printf "Error: Invalid option\n"; - exit;; + n) + name=$OPTARG;; + s) + scheme='https' + port=${SSL_PORT};; + p) + path=$OPTARG;; + c) + expectedCode=$OPTARG;; + r) + expectedResponseRegex=$OPTARG;; + x) + extraCurlOpts=$OPTARG;; + \?) # Invalid option + printf "Error: Invalid option\n"; + exit;; esac done - curlCommand="curl -s -v http://nginx:${PORT}${path} -H 'Cache-Control: no-cache' ${extraCurlOpts} 2>&1" + curlCommand="curl -skv ${scheme}://nginx:${port}${path} -H 'Cache-Control: no-cache' ${extraCurlOpts} 2>&1" response=$(eval "${curlCommand}") exitCode=$? @@ -108,10 +112,20 @@ main() { run_test -n 'when auth disabled, should return 200' \ -p '/' \ -c '200' + + run_test -s \ + -n '[SSL] when auth disabled, should return 200' \ + -p '/' \ + -c '200' run_test -n 'when auth enabled with default algorithm and no JWT in Authorization header, returns 302' \ -p '/secure/auth-header/default' \ -c '302' + + run_test -n '[SSL] when auth enabled with default algorithm and no JWT in Authorization header, returns 302' \ + -s \ + -p '/secure/auth-header/default' \ + -c '302' run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header missing Bearer, should return 200' \ -p '/secure/auth-header/default/no-redirect' \ From 02f4e17eb84dd2de78d5cedca8f0b4f8247470e3 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Wed, 28 Aug 2024 12:12:07 -0400 Subject: [PATCH 03/29] case-insenitive Bearer check #134 (#135) --- openssl.dockerfile | 14 +++++++------- scripts.sh | 6 +++--- src/ngx_http_auth_jwt_module.c | 2 +- test/docker-compose-test.yml | 2 -- test/test.sh | 6 ++++++ 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/openssl.dockerfile b/openssl.dockerfile index 45140cc..d8bb293 100644 --- a/openssl.dockerfile +++ b/openssl.dockerfile @@ -1,9 +1,9 @@ -ARG BASE_IMAGE +ARG BASE_IMAGE=debian:bookworm-slim FROM ${BASE_IMAGE} -ARG SRC_DIR=/tmp/openssl-src -ARG OUT_DIR=/usr/local/.openssl -ARG SSL_VERSION +ARG SSL_VERSION=3.2.1 +ENV SRC_DIR=/tmp/openssl-src +ENV OUT_DIR=/usr/local/.openssl RUN <<` set -e apt-get update @@ -13,8 +13,8 @@ RUN <<` ` WORKDIR ${SRC_DIR} RUN <<` - set -e - curl --silent -O https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz + set -ex + curl --silent -LO https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz tar -xf openssl-${SSL_VERSION}.tar.gz --strip-components=1 ` RUN ./config --prefix=${OUT_DIR} --openssldir=${OUT_DIR} shared zlib @@ -34,4 +34,4 @@ RUN <<` ln -sf ${OUT_DIR}/lib64/libcrypto.so.3 /lib/x86_64-linux-gnu/libcrypto.so.3 ` WORKDIR / -#RUN rm -rf ${SRC_DIR} \ No newline at end of file +RUN rm -rf ${SRC_DIR} \ No newline at end of file diff --git a/scripts.sh b/scripts.sh index 421a4cb..6f109d9 100755 --- a/scripts.sh +++ b/scripts.sh @@ -40,13 +40,13 @@ all() { verify_and_build_base_image() { local image=${SSL_IMAGE_MAP[$SSL_VERSION]} local baseImage=${image%%:*} - + if [ -z ${image} ]; then echo "Base image not set for SSL version :${SSL_VERSION}" exit 1 else - printf "${MAGENTA}Building base image for SSL ${SSL_VERSION}...${NC}\n" - docker image build \ + printf "${MAGENTA}Building ${baseImage} base image for SSL ${SSL_VERSION}...${NC}\n" + docker buildx build \ --build-arg BASE_IMAGE=debian:${baseImage} \ --build-arg SSL_VERSION=${SSL_VERSION} \ -f openssl.dockerfile \ diff --git a/src/ngx_http_auth_jwt_module.c b/src/ngx_http_auth_jwt_module.c index 85a646d..e21560c 100644 --- a/src/ngx_http_auth_jwt_module.c +++ b/src/ngx_http_auth_jwt_module.c @@ -630,7 +630,7 @@ static char *get_jwt(ngx_http_request_t *r, ngx_str_t jwt_location) { static const char *BEARER_PREFIX = "Bearer "; - if (ngx_strncmp(jwtHeaderVal->value.data, BEARER_PREFIX, strlen(BEARER_PREFIX)) == 0) + if (ngx_strncasecmp(jwtHeaderVal->value.data, (u_char *)BEARER_PREFIX, strlen(BEARER_PREFIX)) == 0) { ngx_str_t jwtHeaderValWithoutBearer = jwtHeaderVal->value; diff --git a/test/docker-compose-test.yml b/test/docker-compose-test.yml index 3c0e9be..14c88da 100644 --- a/test/docker-compose-test.yml +++ b/test/docker-compose-test.yml @@ -1,5 +1,3 @@ -version: '3.3' - services: nginx: diff --git a/test/test.sh b/test/test.sh index f54e0de..2bf9cb3 100755 --- a/test/test.sh +++ b/test/test.sh @@ -143,6 +143,12 @@ main() { -r "< Test-Authorization: Bearer ${JWT_HS256_VALID}" \ -x "--header \"Authorization: Bearer ${JWT_HS256_VALID}\"" + run_test -n 'when auth enabled with Authorization header with Bearer, lower-case "bearer" should be accepted' \ + -p '/secure/auth-header/default/proxy-header' \ + -c '200' \ + -r "< Test-Authorization: bearer ${JWT_HS256_VALID}" \ + -x "--header \"Authorization: bearer ${JWT_HS256_VALID}\"" + run_test -n 'when auth enabled with default algorithm and no JWT cookie, returns 302' \ -p '/secure/cookie/default' \ -c '302' From 272c02e2302208993df4f99a7f1751a793d16662 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Wed, 28 Aug 2024 12:31:26 -0400 Subject: [PATCH 04/29] fix release script --- scripts.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts.sh b/scripts.sh index 6f109d9..f6863f0 100755 --- a/scripts.sh +++ b/scripts.sh @@ -61,7 +61,7 @@ build_module() { verify_and_build_base_image printf "${MAGENTA}Building module for NGINX ${NGINX_VERSION}...${NC}\n" - docker image build \ + docker buildx build \ -f nginx.dockerfile \ -t ${FULL_IMAGE_NAME}:${NGINX_VERSION} \ --build-arg BASE_IMAGE=${baseImage} \ @@ -88,7 +88,7 @@ start_nginx() { local port=$(get_port) printf "${MAGENTA}Starting NGINX container (${IMAGE_NAME}) on port ${port}...${NC}\n" - docker run --rm --name "${IMAGE_NAME}" -d -p ${port}:80 ${FULL_IMAGE_NAME} >/dev/null + docker run --rm --name "${IMAGE_NAME}" -d -p ${port}:80 ${FULL_IMAGE_NAME}:${NGINX_VERSION} >/dev/null } stop_nginx() { @@ -128,7 +128,7 @@ make_release() { printf "${MAGENTA}Making release for version ${moduleVersion} for NGINX ${NGINX_VERSION}...${NC}\n" rebuild_module - rebuild_test_runner + rebuild_test test cp_bin From c5882b0c2106bc478af174bb36ea956a99038127 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Mon, 14 Oct 2024 10:43:53 -0400 Subject: [PATCH 05/29] fix Docker warnings --- nginx.dockerfile | 6 ++---- test/test-nginx.dockerfile | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/nginx.dockerfile b/nginx.dockerfile index 21c7460..c0e6c02 100644 --- a/nginx.dockerfile +++ b/nginx.dockerfile @@ -1,16 +1,14 @@ ARG BASE_IMAGE ARG NGINX_VERSION - -FROM ${BASE_IMAGE} as ngx_http_auth_jwt_builder_base +FROM ${BASE_IMAGE} AS ngx_http_auth_jwt_builder_base LABEL stage=ngx_http_auth_jwt_builder RUN <<` apt-get update apt-get install -y curl build-essential ` - -FROM ngx_http_auth_jwt_builder_base as ngx_http_auth_jwt_builder_module +FROM ngx_http_auth_jwt_builder_base AS ngx_http_auth_jwt_builder_module LABEL stage=ngx_http_auth_jwt_builder ENV PATH "${PATH}:/etc/nginx" ENV LD_LIBRARY_PATH=/usr/local/lib diff --git a/test/test-nginx.dockerfile b/test/test-nginx.dockerfile index c4a6104..e12acb4 100644 --- a/test/test-nginx.dockerfile +++ b/test/test-nginx.dockerfile @@ -2,7 +2,7 @@ ARG BASE_IMAGE ARG PORT ARG SSL_PORT -FROM ${BASE_IMAGE} as NGINX +FROM ${BASE_IMAGE} AS NGINX ARG PORT ARG SSL_PORT COPY etc/ /etc/ From 867562a318abb5b6997d77300f30b942e7e964ff Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Mon, 14 Oct 2024 10:44:06 -0400 Subject: [PATCH 06/29] fix /tmp dir perms for containers --- nginx.dockerfile | 1 + openssl.dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/nginx.dockerfile b/nginx.dockerfile index c0e6c02..360469b 100644 --- a/nginx.dockerfile +++ b/nginx.dockerfile @@ -3,6 +3,7 @@ ARG NGINX_VERSION FROM ${BASE_IMAGE} AS ngx_http_auth_jwt_builder_base LABEL stage=ngx_http_auth_jwt_builder +RUN chmod 1777 /tmp RUN <<` apt-get update apt-get install -y curl build-essential diff --git a/openssl.dockerfile b/openssl.dockerfile index d8bb293..42f824f 100644 --- a/openssl.dockerfile +++ b/openssl.dockerfile @@ -4,6 +4,7 @@ FROM ${BASE_IMAGE} ARG SSL_VERSION=3.2.1 ENV SRC_DIR=/tmp/openssl-src ENV OUT_DIR=/usr/local/.openssl +RUN chmod 1777 /tmp RUN <<` set -e apt-get update From b93b816c97937e6a60620165688f937bba8e4f91 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Mon, 14 Oct 2024 10:44:19 -0400 Subject: [PATCH 07/29] update NGINX versions to build against --- scripts.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts.sh b/scripts.sh index f6863f0..2ae02d4 100755 --- a/scripts.sh +++ b/scripts.sh @@ -21,9 +21,10 @@ SSL_IMAGE_MAP[$SSL_VERSION_3_2_1]="bookworm-slim:openssl-${SSL_VERSION_3_2_1}" # supported NGINX versions -- for binary distribution NGINX_VERSION_LEGACY_1='1.20.2' NGINX_VERSION_LEGACY_2='1.22.1' -NGINX_VERSION_STABLE='1.24.0' -NGINX_VERSION_MAINLINE='1.25.4' -NGINX_VERSIONS=(${NGINX_VERSION_LEGACY_1} ${NGINX_VERSION_LEGACY_2} ${NGINX_VERSION_STABLE} ${NGINX_VERSION_MAINLINE}) +NGINX_VERSION_LEGACY_3='1.24.0' +NGINX_VERSION_STABLE='1.26.2' +NGINX_VERSION_MAINLINE='1.27.2' +NGINX_VERSIONS=(${NGINX_VERSION_LEGACY_1} ${NGINX_VERSION_LEGACY_2} ${NGINX_VERSION_LEGACY_3} ${NGINX_VERSION_STABLE} ${NGINX_VERSION_MAINLINE}) NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSION_STABLE}} IMAGE_NAME=${IMAGE_NAME:-nginx-auth-jwt} From e8e60e652a14474dcef39fac95c79385813737a8 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Mon, 14 Oct 2024 10:44:53 -0400 Subject: [PATCH 08/29] rm redundant `-e` --- scripts.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts.sh b/scripts.sh index 2ae02d4..5c9b785 100755 --- a/scripts.sh +++ b/scripts.sh @@ -120,8 +120,6 @@ cp_bin() { } make_release() { - set -e - local moduleVersion=${1} NGINX_VERSION=${2} From 16b0369e8c369809de8bda8004c65b5d36fd2d56 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Mon, 14 Oct 2024 10:45:06 -0400 Subject: [PATCH 09/29] update scripts to support arts --- scripts.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts.sh b/scripts.sh index 5c9b785..4b46a85 100755 --- a/scripts.sh +++ b/scripts.sh @@ -233,7 +233,8 @@ get_port() { if [ $# -eq 0 ]; then all else - for fn in "$@"; do - ${fn} - done + fn=$1 + shift + + ${fn} "$@" fi From d8974ebd93539d7961fdc552cf563701961a16a3 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 4 Feb 2025 10:03:35 -0500 Subject: [PATCH 10/29] no releases from PRs --- .github/workflows/make-releases.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index 037823b..3a12f70 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -6,11 +6,6 @@ on: - master paths: - src/** - pull_request: - branches: - - master - paths: - - src/** workflow_dispatch: jobs: From 27fcd3d6fbfb561966d7e03a70000566123aee17 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 4 Feb 2025 10:03:53 -0500 Subject: [PATCH 11/29] update NGINX versions to build against --- .github/workflows/make-releases.yml | 2 +- scripts.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index 3a12f70..fdc5591 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: # NGINX versions to build/test against - nginx-version: ['1.20.2', '1.22.1', '1.24.0', '1.25.3'] + nginx-version: ['1.20.2', '1.22.1', '1.24.0', '1.26.2', '1.27.3'] # The following versions of libjwt are compatible: # * v1.0 - v1.12.0 diff --git a/scripts.sh b/scripts.sh index 4b46a85..af59e9b 100755 --- a/scripts.sh +++ b/scripts.sh @@ -23,7 +23,7 @@ NGINX_VERSION_LEGACY_1='1.20.2' NGINX_VERSION_LEGACY_2='1.22.1' NGINX_VERSION_LEGACY_3='1.24.0' NGINX_VERSION_STABLE='1.26.2' -NGINX_VERSION_MAINLINE='1.27.2' +NGINX_VERSION_MAINLINE='1.27.3' NGINX_VERSIONS=(${NGINX_VERSION_LEGACY_1} ${NGINX_VERSION_LEGACY_2} ${NGINX_VERSION_LEGACY_3} ${NGINX_VERSION_STABLE} ${NGINX_VERSION_MAINLINE}) NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSION_STABLE}} From 576fe71f0a693d5e380069a316ae5361fe73af30 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 4 Feb 2025 10:04:03 -0500 Subject: [PATCH 12/29] update workflow action version --- .github/workflows/make-releases.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index fdc5591..7316e6b 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -94,7 +94,7 @@ jobs: tar czf ngx_http_auth_jwt_module_libjwt_${{matrix.libjwt-version}}_nginx_${{matrix.nginx-version}}.tgz ngx_http_auth_jwt_module.so - name: Upload build artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: if-no-files-found: error name: ngx_http_auth_jwt_module_libjwt_${{matrix.libjwt-version}}_nginx_${{matrix.nginx-version}}.tgz From d29adbb2ced992961d8c9c01b3dcf162b4c3f851 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 4 Feb 2025 10:15:51 -0500 Subject: [PATCH 13/29] rename start/stop script functions --- scripts.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts.sh b/scripts.sh index af59e9b..edbb186 100755 --- a/scripts.sh +++ b/scripts.sh @@ -85,14 +85,14 @@ clean_module() { docker rmi -f $(docker images --filter=label=stage=ngx_http_auth_jwt_builder --quiet) 2> /dev/null || true } -start_nginx() { +start() { local port=$(get_port) printf "${MAGENTA}Starting NGINX container (${IMAGE_NAME}) on port ${port}...${NC}\n" docker run --rm --name "${IMAGE_NAME}" -d -p ${port}:80 ${FULL_IMAGE_NAME}:${NGINX_VERSION} >/dev/null } -stop_nginx() { +stop() { docker stop "${IMAGE_NAME}" >/dev/null } From a774c4208141b77d3144ac1cd296a420fbfe4b85 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 4 Feb 2025 10:16:09 -0500 Subject: [PATCH 14/29] rename scripts.sh --- scripts.sh => scripts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts.sh => scripts (100%) diff --git a/scripts.sh b/scripts similarity index 100% rename from scripts.sh rename to scripts From 7c9cb00f5e3bf86192c6fc7a2b6a11a706923106 Mon Sep 17 00:00:00 2001 From: Adrian Carreno Date: Tue, 4 Feb 2025 12:30:37 -0300 Subject: [PATCH 15/29] Feature: Add support for ARM64 (#139) --- openssl.dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openssl.dockerfile b/openssl.dockerfile index 42f824f..9839d29 100644 --- a/openssl.dockerfile +++ b/openssl.dockerfile @@ -31,8 +31,8 @@ RUN <<` ldconfig ln -sf ${OUT_DIR}/bin/openssl /usr/bin/openssl - ln -sf ${OUT_DIR}/lib64/libssl.so.3 /lib/x86_64-linux-gnu/libssl.so.3 - ln -sf ${OUT_DIR}/lib64/libcrypto.so.3 /lib/x86_64-linux-gnu/libcrypto.so.3 + ln -sf ${OUT_DIR}/lib64/libssl.so.3 /lib/$(uname -m)-linux-gnu/libssl.so.3 + ln -sf ${OUT_DIR}/lib64/libcrypto.so.3 /lib/$(uname -m)-linux-gnu/libcrypto.so.3 ` WORKDIR / RUN rm -rf ${SRC_DIR} \ No newline at end of file From edabc23442653cc82568dcd83b034408e4708815 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 4 Feb 2025 10:33:12 -0500 Subject: [PATCH 16/29] support ARM --- scripts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts b/scripts index edbb186..9d90185 100755 --- a/scripts +++ b/scripts @@ -110,8 +110,8 @@ cp_bin() { mkdir -p ${destDir} docker exec "${IMAGE_NAME}" sh -c "cd /; tar -chf - \ usr/lib64/nginx/modules/ngx_http_auth_jwt_module.so \ - usr/lib/x86_64-linux-gnu/libjansson.so.* \ - usr/lib/x86_64-linux-gnu/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null + usr/lib/$(uname -m)-linux-gnu/libjansson.so.* \ + usr/lib/$(uname -m)-linux-gnu/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null if [ $stopContainer ]; then printf "${MAGENTA}Stopping NGINX container (${IMAGE_NAME})...${NC}\n" From 81a2b445d20518d264cc32fcb51350ccbf8a7214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilson=20J=C3=BAnior?= Date: Tue, 4 Feb 2025 14:14:30 -0300 Subject: [PATCH 17/29] Support extracting claims to NGINX variables (#145) Co-authored-by: Matt Gilham <7717048+mgilham@users.noreply.github.com> Co-authored-by: Josh McCullough Co-authored-by: Josh McCullough --- README.md | 35 +-- src/ngx_http_auth_jwt_module.c | 387 +++++++++++++++++++++++++------- test/etc/nginx/conf.d/test.conf | 44 ++++ test/test.sh | 34 ++- 4 files changed, 393 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index ba3dde1..95c83af 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,20 @@ This module depends on the [JWT C Library](https://github.com/benmcollins/libjwt This module requires several new `nginx.conf` directives, which can be specified at the `http`, `server`, or `location` levels. -| Directive | Description | -| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| `auth_jwt_key` | The key to use to decode/verify the JWT, *in binhex format* -- see below. | -| `auth_jwt_redirect` | Set to "on" to redirect to `auth_jwt_loginurl` if authentication fails. | -| `auth_jwt_loginurl` | The URL to redirect to if `auth_jwt_redirect` is enabled and authentication fails. | -| `auth_jwt_enabled` | Set to "on" to enable JWT checking. | -| `auth_jwt_algorithm` | The algorithm to use. One of: HS256, HS384, HS512, RS256, RS384, RS512 | -| `auth_jwt_location` | Indicates where the JWT is located in the request -- see below. | -| `auth_jwt_validate_sub` | Set to "on" to validate the `sub` claim (e.g. user id) in the JWT. | -| `auth_jwt_extract_request_claims` | Set to a space-delimited list of claims to extract from the JWT and set as request headers. These will be accessible via e.g: `$http_jwt_sub` | -| `auth_jwt_extract_response_claims` | Set to a space-delimited list of claims to extract from the JWT and set as response headers. These will be accessible via e.g: `$sent_http_jwt_sub` | -| `auth_jwt_use_keyfile` | Set to "on" to read the key from a file rather than from the `auth_jwt_key` directive. | -| `auth_jwt_keyfile_path` | Set to the path from which the key should be read when `auth_jwt_use_keyfile` is enabled. | +| Directive | Description | +| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `auth_jwt_key` | The key to use to decode/verify the JWT, *in binhex format* -- see below. | +| `auth_jwt_redirect` | Set to "on" to redirect to `auth_jwt_loginurl` if authentication fails. | +| `auth_jwt_loginurl` | The URL to redirect to if `auth_jwt_redirect` is enabled and authentication fails. | +| `auth_jwt_enabled` | Set to "on" to enable JWT checking. | +| `auth_jwt_algorithm` | The algorithm to use. One of: HS256, HS384, HS512, RS256, RS384, RS512 | +| `auth_jwt_location` | Indicates where the JWT is located in the request -- see below. | +| `auth_jwt_validate_sub` | Set to "on" to validate the `sub` claim (e.g. user id) in the JWT. | +| `auth_jwt_extract_var_claims` | Set to a space-delimited list of claims to extract from the JWT and make available as NGINX variables. These will be accessible via e.g: `$jwt_claim_sub` | +| `auth_jwt_extract_request_claims` | Set to a space-delimited list of claims to extract from the JWT and set as request headers. These will be accessible via e.g: `$http_jwt_sub` | +| `auth_jwt_extract_response_claims` | Set to a space-delimited list of claims to extract from the JWT and set as response headers. These will be accessible via e.g: `$sent_http_jwt_sub` | +| `auth_jwt_use_keyfile` | Set to "on" to read the key from a file rather than from the `auth_jwt_key` directive. | +| `auth_jwt_keyfile_path` | Set to the path from which the key should be read when `auth_jwt_use_keyfile` is enabled. | ## Algorithms @@ -92,19 +93,19 @@ auth_jwt_validate_sub on; You may specify claims to be extracted from the JWT and placed on the request and/or response headers. This is especially handly because the claims will then also be available as NGINX variables. -If you only wish to access a claim as an NGINX variable, you should use `auth_jwt_extract_request_claims` so that the claim does not end up being sent to the client as a response header. However, if you do want the claim to be sent to the client in the response, then use `auth_jwt_extract_response_claims` instead. +If you only wish to access a claim as an NGINX variable, you should use `auth_jwt_extract_var_claims` so that the claim does not end up being sent to the client as a response header. However, if you do want the claim to be sent to the client in the response, you may use `auth_jwt_extract_response_claims` instead. _Please note that `number`, `boolean`, `array`, and `object` claims are not supported at this time -- only `string` claims are supported._ An error will be thrown if you attempt to extract a non-string claim. -### Using Request Claims +### Using Claims For example, you could configure an NGINX location which redirects to the current user's profile. Suppose `sub=abc-123`, the configuration below would redirect to `/profile/abc-123`. ```nginx location /profile/me { - auth_jwt_extract_request_claims sub; + auth_jwt_extract_var_claims sub; - return 301 /profile/$http_jwt_sub; + return 301 /profile/$jwt_claim_sub; } ``` diff --git a/src/ngx_http_auth_jwt_module.c b/src/ngx_http_auth_jwt_module.c index e21560c..fe428b4 100644 --- a/src/ngx_http_auth_jwt_module.c +++ b/src/ngx_http_auth_jwt_module.c @@ -31,6 +31,7 @@ typedef struct ngx_str_t jwt_location; ngx_str_t algorithm; ngx_flag_t validate_sub; + ngx_array_t *extract_var_claims; ngx_array_t *extract_request_claims; ngx_array_t *extract_response_claims; ngx_str_t keyfile_path; @@ -38,18 +39,28 @@ typedef struct ngx_str_t _keyfile; } auth_jwt_conf_t; +typedef struct +{ + ngx_int_t validation_status; + ngx_array_t *claim_values; +} auth_jwt_ctx_t; + static ngx_int_t init(ngx_conf_t *cf); static void *create_conf(ngx_conf_t *cf); static char *merge_conf(ngx_conf_t *cf, void *parent, void *child); +static char *merge_extract_var_claims(ngx_conf_t *cf, ngx_command_t *cmd, void *c); +static ngx_int_t get_jwt_var_claim(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static char *merge_extract_request_claims(ngx_conf_t *cf, ngx_command_t *cmd, void *c); static char *merge_extract_response_claims(ngx_conf_t *cf, ngx_command_t *cmd, void *c); +static auth_jwt_ctx_t *get_or_init_jwt_module_ctx(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf); +static auth_jwt_ctx_t *get_request_jwt_ctx(ngx_http_request_t *r); static ngx_int_t handle_request(ngx_http_request_t *r); static int validate_alg(auth_jwt_conf_t *jwtcf, jwt_t *jwt); static int validate_exp(auth_jwt_conf_t *jwtcf, jwt_t *jwt); static int validate_sub(auth_jwt_conf_t *jwtcf, jwt_t *jwt); +static ngx_int_t extract_var_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt, auth_jwt_ctx_t *ctx); static void extract_request_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt); static void extract_response_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt); -static ngx_int_t free_jwt_and_redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt); static ngx_int_t redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf); static ngx_int_t load_public_key(ngx_conf_t *cf, auth_jwt_conf_t *conf); static char *get_jwt(ngx_http_request_t *r, ngx_str_t jwt_location); @@ -106,6 +117,13 @@ static ngx_command_t auth_jwt_directives[] = { offsetof(auth_jwt_conf_t, validate_sub), NULL}, + {ngx_string("auth_jwt_extract_var_claims"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_1MORE, + merge_extract_var_claims, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(auth_jwt_conf_t, extract_var_claims), + NULL}, + {ngx_string("auth_jwt_extract_request_claims"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_1MORE, merge_extract_request_claims, @@ -194,6 +212,7 @@ static void *create_conf(ngx_conf_t *cf) conf->validate_sub = NGX_CONF_UNSET; conf->redirect = NGX_CONF_UNSET; conf->validate_sub = NGX_CONF_UNSET; + conf->extract_var_claims = NULL; conf->extract_request_claims = NULL; conf->extract_response_claims = NULL; conf->use_keyfile = NGX_CONF_UNSET; @@ -213,6 +232,7 @@ static char *merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->algorithm, prev->algorithm, "HS256"); ngx_conf_merge_str_value(conf->keyfile_path, prev->keyfile_path, ""); ngx_conf_merge_off_value(conf->validate_sub, prev->validate_sub, 0); + merge_array(cf->pool, &conf->extract_var_claims, prev->extract_var_claims, sizeof(ngx_str_t)); merge_array(cf->pool, &conf->extract_request_claims, prev->extract_request_claims, sizeof(ngx_str_t)); merge_array(cf->pool, &conf->extract_response_claims, prev->extract_response_claims, sizeof(ngx_str_t)); @@ -252,6 +272,108 @@ static char *merge_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_OK; } +static char *merge_extract_var_claims(ngx_conf_t *cf, ngx_command_t *cmd, void *c) +{ + auth_jwt_conf_t *conf = c; + ngx_array_t *claims = conf->extract_var_claims; + + if (claims == NULL) + { + claims = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); + conf->extract_var_claims = claims; + } + + ngx_str_t *values = cf->args->elts; + + // start at 1 because the first element is the directive (auth_jwt_extract_var_claims) + for (ngx_uint_t i = 1; i < cf->args->nelts; ++i) + { + // add this claim's name to the config struct + ngx_str_t *element = ngx_array_push(claims); + + *element = values[i]; + + // add an http variable for this claim + size_t var_name_len = 10 + element->len; + u_char *buf = ngx_palloc(cf->pool, sizeof(u_char) * var_name_len); + + if (buf == NULL) + { + return NGX_CONF_ERROR; + } + else + { + ngx_sprintf(buf, "jwt_claim_%V", element); + ngx_str_t *var_name = ngx_palloc(cf->pool, sizeof(ngx_str_t)); + + if (var_name == NULL) + { + return NGX_CONF_ERROR; + } + else + { + var_name->data = buf; + var_name->len = var_name_len; + + // NGX_HTTP_VAR_CHANGEABLE simplifies the required logic by assuming a JWT claim will always be the same for a given request + ngx_http_variable_t *http_var = ngx_http_add_variable(cf, var_name, NGX_HTTP_VAR_CHANGEABLE); + + if (http_var == NULL) + { + ngx_log_error(NGX_LOG_ERR, cf->log, 0, "failed to add variable %V", var_name); + + return NGX_CONF_ERROR; + } + else + { + http_var->get_handler = get_jwt_var_claim; + + // store the index of this new claim in the claims array as the "data" that will be passed to the getter + ngx_uint_t *claim_idx = ngx_palloc(cf->pool, sizeof(ngx_uint_t)); + + if (claim_idx == NULL) + { + return NGX_CONF_ERROR; + } + else + { + *claim_idx = claims->nelts - 1; + http_var->data = (uintptr_t) claim_idx; + } + } + } + } + } + + return NGX_CONF_OK; +} + +static ngx_int_t get_jwt_var_claim(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "getting jwt value for var index %l", *((ngx_uint_t*) data)); + auth_jwt_ctx_t *ctx = get_request_jwt_ctx(r); + + if (ctx == NULL) + { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "no module context found while getting jwt value"); + + return NGX_ERROR; + } + else + { + ngx_uint_t *claim_idx = (ngx_uint_t*) data; + ngx_str_t claim_value = ((ngx_str_t*) ctx->claim_values->elts)[*claim_idx]; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = claim_value.len; + v->data = claim_value.data; + + return NGX_OK; + } +} + static char *merge_extract_claims(ngx_conf_t *cf, ngx_array_t *claims) { ngx_str_t *values = cf->args->elts; @@ -295,98 +417,169 @@ static char *merge_extract_response_claims(ngx_conf_t *cf, ngx_command_t *cmd, v return merge_extract_claims(cf, claims); } -static ngx_int_t handle_request(ngx_http_request_t *r) +static auth_jwt_ctx_t *get_or_init_jwt_module_ctx(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf) +{ + auth_jwt_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_auth_jwt_module); + + if (ctx != NULL) + { + return ctx; + } + else + { + ctx = ngx_pcalloc(r->pool, sizeof(auth_jwt_ctx_t)); + + if (ctx == NULL) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "error allocating jwt module context"); + return ctx; + } + else { + if (jwtcf->extract_var_claims != NULL) + { + ctx->claim_values = ngx_array_create(r->pool, jwtcf->extract_var_claims->nelts, sizeof(ngx_str_t)); + + if (ctx->claim_values == NULL) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "error initializing jwt module context"); + return NULL; + } + } + + ctx->validation_status = NGX_AGAIN; + ngx_http_set_ctx(r, ctx, ngx_http_auth_jwt_module); + + return ctx; + } + } +} + +// this creates the module's context struct and extracts claim vars the first time it is called, +// either from the access-phase handler or an http var getter +static auth_jwt_ctx_t *get_request_jwt_ctx(ngx_http_request_t *r) { auth_jwt_conf_t *jwtcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_jwt_module); - if (!jwtcf->enabled) + if(!jwtcf->enabled) { - return NGX_DECLINED; + return NULL; + } + + auth_jwt_ctx_t *ctx = get_or_init_jwt_module_ctx(r, jwtcf); + + if (ctx == NULL) + { + return NULL; + } + else if (ctx->validation_status != NGX_AGAIN) + { + // we already validated and extacted everything we care about, so we just return the already-complete context + return ctx; + } + + char *jwtPtr = get_jwt(r, jwtcf->jwt_location); + + if (jwtPtr == NULL) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to find a JWT"); + ctx->validation_status = NGX_ERROR; + return ctx; } else { - // pass through options requests without token authentication - if (r->method == NGX_HTTP_OPTIONS) + ngx_str_t algorithm = jwtcf->algorithm; + int keyLength; + u_char *key; + jwt_t *jwt = NULL; + + if (algorithm.len == 0 || (algorithm.len == 5 && ngx_strncmp(algorithm.data, "HS", 2) == 0)) { - return NGX_DECLINED; + keyLength = jwtcf->key.len / 2; + key = ngx_palloc(r->pool, keyLength); + + if (0 != hex_to_binary((char *)jwtcf->key.data, key, jwtcf->key.len)) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to turn hex key into binary"); + ctx->validation_status = NGX_ERROR; + return ctx; + } } - else + else if (algorithm.len == 5 && (ngx_strncmp(algorithm.data, "RS", 2) == 0 || ngx_strncmp(algorithm.data, "ES", 2) == 0)) { - char *jwtPtr = get_jwt(r, jwtcf->jwt_location); - - if (jwtPtr == NULL) + if (jwtcf->use_keyfile == 1) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to find a JWT"); - return redirect(r, jwtcf); + keyLength = jwtcf->_keyfile.len; + key = (u_char *)jwtcf->_keyfile.data; } else { - ngx_str_t algorithm = jwtcf->algorithm; - int keyLength; - u_char *key; - jwt_t *jwt = NULL; - - if (algorithm.len == 0 || (algorithm.len == 5 && ngx_strncmp(algorithm.data, "HS", 2) == 0)) - { - keyLength = jwtcf->key.len / 2; - key = ngx_palloc(r->pool, keyLength); + keyLength = jwtcf->key.len; + key = jwtcf->key.data; + } + } + else + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsupported algorithm %s", algorithm); + ctx->validation_status = NGX_ERROR; + return ctx; + } - if (0 != hex_to_binary((char *)jwtcf->key.data, key, jwtcf->key.len)) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to turn hex key into binary"); - return redirect(r, jwtcf); - } - } - else if (algorithm.len == 5 && (ngx_strncmp(algorithm.data, "RS", 2) == 0 || ngx_strncmp(algorithm.data, "ES", 2) == 0)) - { - if (jwtcf->use_keyfile == 1) - { - keyLength = jwtcf->_keyfile.len; - key = (u_char *)jwtcf->_keyfile.data; - } - else - { - keyLength = jwtcf->key.len; - key = jwtcf->key.data; - } - } - else - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsupported algorithm %s", algorithm); - return redirect(r, jwtcf); - } + if (jwt_decode(&jwt, jwtPtr, key, keyLength) != 0 || !jwt) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to parse JWT"); + ctx->validation_status = NGX_ERROR; + } + else if (validate_alg(jwtcf, jwt) != 0) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid algorithm specified"); + ctx->validation_status = NGX_ERROR; + } + else if (validate_exp(jwtcf, jwt) != 0) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT has expired"); + ctx->validation_status = NGX_ERROR; + } + else if (validate_sub(jwtcf, jwt) != 0) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT does not contain a subject"); + ctx->validation_status = NGX_ERROR; + } + else + { + extract_request_claims(r, jwtcf, jwt); + extract_response_claims(r, jwtcf, jwt); + ctx->validation_status = extract_var_claims(r, jwtcf, jwt, ctx); + } - if (jwt_decode(&jwt, jwtPtr, key, keyLength) != 0) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to parse JWT"); - return redirect(r, jwtcf); - } + jwt_free(jwt); + return ctx; + } +} - if (validate_alg(jwtcf, jwt) != 0) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid algorithm specified"); - return free_jwt_and_redirect(r, jwtcf, jwt); - } - else if (validate_exp(jwtcf, jwt) != 0) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT has expired"); - return free_jwt_and_redirect(r, jwtcf, jwt); - } - else if (validate_sub(jwtcf, jwt) != 0) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT does not contain a subject"); - return free_jwt_and_redirect(r, jwtcf, jwt); - } - else - { - extract_request_claims(r, jwtcf, jwt); - extract_response_claims(r, jwtcf, jwt); - jwt_free(jwt); +static ngx_int_t handle_request(ngx_http_request_t *r) +{ + auth_jwt_conf_t *jwtcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_jwt_module); + auth_jwt_ctx_t *ctx = get_request_jwt_ctx(r); - return NGX_OK; - } - } - } + if (!jwtcf->enabled) + { + return NGX_DECLINED; + } + else if (r->method == NGX_HTTP_OPTIONS) // pass through options requests without token authentication + { + return NGX_DECLINED; + } + else if (!ctx) + { + return NGX_ERROR; + } + else if (ctx->validation_status == NGX_ERROR) + { + return redirect(r, jwtcf); + } + else + { + return ctx->validation_status; } } @@ -394,8 +587,7 @@ static int validate_alg(auth_jwt_conf_t *jwtcf, jwt_t *jwt) { const jwt_alg_t alg = jwt_get_alg(jwt); - if (alg != JWT_ALG_HS256 && alg != JWT_ALG_HS384 && alg != JWT_ALG_HS512 && alg != JWT_ALG_RS256 && alg != JWT_ALG_RS384 && alg != JWT_ALG_RS512 && alg != JWT_ALG_ES256 && alg != JWT_ALG_ES384 && alg != JWT_ALG_ES512) - { + if (alg != JWT_ALG_HS256 && alg != JWT_ALG_HS384 && alg != JWT_ALG_HS512 && alg != JWT_ALG_RS256 && alg != JWT_ALG_RS384 && alg != JWT_ALG_RS512 && alg != JWT_ALG_ES256 && alg != JWT_ALG_ES384 && alg != JWT_ALG_ES512) { return 1; } @@ -430,6 +622,37 @@ static int validate_sub(auth_jwt_conf_t *jwtcf, jwt_t *jwt) return 0; } +static ngx_int_t extract_var_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt, auth_jwt_ctx_t *ctx) +{ + ngx_array_t *claims = jwtcf->extract_var_claims; + + if (claims == NULL || claims->nelts == 0) + { + return NGX_OK; + } + else + { + const ngx_str_t *claimsPtr = claims->elts; + + for (uint i = 0; i < claims->nelts; ++i) + { + const ngx_str_t claim = claimsPtr[i]; + const char *claimValue = jwt_get_grant(jwt, (char *)claim.data); + ngx_str_t value = ngx_string(""); + + if (claimValue != NULL && strlen(claimValue) > 0) + { + value = char_ptr_to_ngx_str_t(r->pool, claimValue); + } + + ((ngx_str_t*) ctx->claim_values->elts)[i] = value; + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "set var %V to JWT claim value %s", &claim, value.data); + } + + return NGX_OK; + } +} + static void extract_claims(ngx_http_request_t *r, jwt_t *jwt, ngx_array_t *claims, ngx_int_t (*set_header)(ngx_http_request_t *r, ngx_str_t *key, ngx_str_t *value)) { if (claims != NULL && claims->nelts > 0) @@ -467,16 +690,6 @@ static void extract_response_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtc extract_claims(r, jwt, jwtcf->extract_response_claims, set_response_header); } -static ngx_int_t free_jwt_and_redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt) -{ - if (jwt) - { - jwt_free(jwt); - } - - return redirect(r, jwtcf); -} - static ngx_int_t redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf) { if (jwtcf->redirect) diff --git a/test/etc/nginx/conf.d/test.conf b/test/etc/nginx/conf.d/test.conf index 229d545..5359434 100644 --- a/test/etc/nginx/conf.d/test.conf +++ b/test/etc/nginx/conf.d/test.conf @@ -395,4 +395,48 @@ vXjq39xtcIBRTO1c2zs= try_files index.html =404; } } + + location /secure/extract-claim/if/sub { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_var_claims sub; + + if ($jwt_claim_sub = 'some-long-uuid') { + return 200; + } + return 401; + } + + location /secure/extract-claim/body/sub { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_var_claims sub; + + return 200 "sub: $jwt_claim_sub"; + } + + location /secure/extract-claim/body/multiple { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_validate_sub on; + auth_jwt_extract_var_claims firstName middleName lastName; + + return 200 "you are: $jwt_claim_firstName $jwt_claim_middleName $jwt_claim_lastName"; + } + + location /profile { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_validate_sub on; + + location /profile/me { + auth_jwt_extract_var_claims sub; + + return 301 /profile/$jwt_claim_sub; + } + } } diff --git a/test/test.sh b/test/test.sh index 2bf9cb3..747124c 100755 --- a/test/test.sh +++ b/test/test.sh @@ -72,7 +72,7 @@ run_test () { fi fi - if [ "${okay}" == '1' ] && [ "${expectedResponseRegex}" != "" ] && ! [[ "${response}" =~ "${expectedResponseRegex}" ]]; then + if [ "${okay}" == '1' ] && [ "${expectedResponseRegex}" != "" ] && ! [[ "${response}" =~ ${expectedResponseRegex} ]]; then printf "${RED}${name} -- regex not found in response\n\tPath: ${path}\n\tRegEx: ${expectedResponseRegex}" NUM_FAILED=$((${NUM_FAILED} + 1)) okay=0 @@ -279,7 +279,7 @@ main() { run_test -n 'extracts nested claim to request variable' \ -p '/secure/extract-claim/request/nested' \ - -r '< Test: username=hello.world' \ + -r '< Test: username=hello\.world' \ -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' run_test -n 'extracts single claim to response variable' \ @@ -319,7 +319,35 @@ main() { run_test -n 'extracts nested claim to response header' \ -p '/secure/extract-claim/response/nested' \ - -r '< JWT-username: hello.world' \ + -r '< JWT-username: hello\.world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'tests single claim with if statement' \ + -p '/secure/extract-claim/if/sub' \ + -c 200 \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'tests absence of single claim with if statement' \ + -p '/secure/extract-claim/if/sub' \ + -c 401 \ + -x '--header "Authorization: Bearer ${JWT_HS256_MISSING_SUB}"' + + run_test -n 'extracts single claim to response body' \ + -p '/secure/extract-claim/body/sub' \ + -c 200 \ + -r 'sub: some-long-uuid$' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims to response body' \ + -p '/secure/extract-claim/body/multiple' \ + -c 200 \ + -r 'you are: hello world$' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'redirect based on claim' \ + -p '/profile/me' \ + -c 301 \ + -r '< Location: http://nginx:8000/profile/some-long-uuid' \ -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' if [[ "${NUM_FAILED}" = '0' ]]; then From 5c3d1b9565edb5ac1243d3ebbaa869246f82f0f6 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 4 Feb 2025 12:35:50 -0500 Subject: [PATCH 18/29] fix workflow --- .github/workflows/make-releases.yml | 56 ++++++++++++++++------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index 7316e6b..3e58bfa 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -1,11 +1,6 @@ -name: CI +name: Make Releases on: - push: - branches: - - master - paths: - - src/** workflow_dispatch: jobs: @@ -27,14 +22,26 @@ jobs: libjwt-version: ['1.12.0', '1.14.0', '1.15.3'] runs-on: ubuntu-latest steps: + - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: - path: 'ngx-http-auth-jwt-module' + fetch-depth: 0 + path: ngx-http-auth-jwt-module + + - name: Get Metadata + id: meta + run: | + set -eux + cd ngx-http-auth-jwt-module + + tag=$(git describe --tags --abbrev=0) + + echo "filename=ngx-http-auth-jwt-module-${tag}_libjwt-${{matrix.libjwt-version}}_nginx-${{matrix.nginx-version}}.tgz" >> $GITHUB_OUTPUT # TODO cache the build result so we don't have to do this every time? - name: Download jansson - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: 'akheron/jansson' ref: 'v2.14' @@ -50,7 +57,7 @@ jobs: # TODO cache the build result so we don't have to do this every time? - name: Download libjwt - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: 'benmcollins/libjwt' ref: 'v${{matrix.libjwt-version}}' @@ -82,44 +89,43 @@ jobs: BUILD_FLAGS="${BUILD_FLAGS} --with-cc-opt='-DNGX_LINKED_LIST_COOKIES=1'" fi - ./configure --with-compat --add-dynamic-module=../ngx-http-auth-jwt-module ${BUILD_FLAGS} + ./configure --with-compat --without-http_rewrite_module --add-dynamic-module=../ngx-http-auth-jwt-module ${BUILD_FLAGS} - name: Make Modules working-directory: ./nginx run: make modules - - name: Create release archive + - name: Create Release Archive run: | cp ./nginx/objs/ngx_http_auth_jwt_module.so ./ - tar czf ngx_http_auth_jwt_module_libjwt_${{matrix.libjwt-version}}_nginx_${{matrix.nginx-version}}.tgz ngx_http_auth_jwt_module.so + tar czf ${{steps.meta.outputs.filename}} ngx_http_auth_jwt_module.so - - name: Upload build artifact + - name: Upload Build Artifact uses: actions/upload-artifact@v4 with: if-no-files-found: error - name: ngx_http_auth_jwt_module_libjwt_${{matrix.libjwt-version}}_nginx_${{matrix.nginx-version}}.tgz - path: ngx_http_auth_jwt_module_libjwt_${{matrix.libjwt-version}}_nginx_${{matrix.nginx-version}}.tgz + name: ${{steps.meta.outputs.filename}} + path: ${{steps.meta.outputs.filename}} update_releases_page: - name: Upload builds to Releases - if: github.event_name != 'pull_request' - needs: - - build + name: Upload Release + needs: build runs-on: ubuntu-latest permissions: contents: write steps: - - name: Set up variables + + - name: Set-up Variables id: vars run: | echo "date_now=$(date --rfc-3339=seconds)" >> "${GITHUB_OUTPUT}" - - name: Download build artifacts from previous jobs - uses: actions/download-artifact@v3 + - name: Download Build Artifacts from Previous Jobs + uses: actions/download-artifact@v4 with: path: artifacts - - name: Upload builds to Releases + - name: Upload Builds to Release uses: ncipollo/release-action@v1 with: allowUpdates: true @@ -128,7 +134,7 @@ jobs: body: | > [!WARNING] > This is an automatically generated pre-release version of the module, which includes the latest master branch changes. - > Please report any bugs you find to the issue tracker. + > Please report any bugs you find. - Build Date: `${{ steps.vars.outputs.date_now }}` - Commit: ${{ github.sha }} From c38ae696319e5106a6c171d4f6882d1ffffd58db Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Wed, 5 Feb 2025 14:23:23 -0500 Subject: [PATCH 19/29] fix release artifact upload --- .github/workflows/make-releases.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index 3e58bfa..8d7c906 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -105,10 +105,9 @@ jobs: with: if-no-files-found: error name: ${{steps.meta.outputs.filename}} - path: ${{steps.meta.outputs.filename}} - update_releases_page: - name: Upload Release + release: + name: Create/Update Release needs: build runs-on: ubuntu-latest permissions: @@ -120,7 +119,7 @@ jobs: run: | echo "date_now=$(date --rfc-3339=seconds)" >> "${GITHUB_OUTPUT}" - - name: Download Build Artifacts from Previous Jobs + - name: Download Build Artifacts uses: actions/download-artifact@v4 with: path: artifacts @@ -128,9 +127,7 @@ jobs: - name: Upload Builds to Release uses: ncipollo/release-action@v1 with: - allowUpdates: true - artifactErrorsFailBuild: true - artifacts: artifacts/*/* + name: 'Development Build: ${{ github.ref_name }}@${{ github.sha }}' body: | > [!WARNING] > This is an automatically generated pre-release version of the module, which includes the latest master branch changes. @@ -138,7 +135,9 @@ jobs: - Build Date: `${{ steps.vars.outputs.date_now }}` - Commit: ${{ github.sha }} - name: 'Development build: ${{ github.ref_name }}@${{ github.sha }}' prerelease: true + allowUpdates: true removeArtifacts: true + artifactErrorsFailBuild: true + artifacts: artifacts/* tag: dev-build From acbb12e3c83d9a1d33e673407530cc7bcc8031e6 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Wed, 5 Feb 2025 14:23:33 -0500 Subject: [PATCH 20/29] update redirect URL to include port (#146) --- README.md | 41 ++++++----- scripts | 17 +++-- src/ngx_http_auth_jwt_module.c | 119 +++++++++++++++++++++----------- test/docker-compose-test.yml | 7 +- test/etc/nginx/conf.d/test.conf | 6 ++ test/test-nginx.dockerfile | 11 +-- test/test-runner.dockerfile | 14 ++-- test/test.sh | 28 ++++++-- 8 files changed, 159 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 95c83af..f6b09d6 100644 --- a/README.md +++ b/README.md @@ -192,41 +192,43 @@ Once you save your changes to `.vscode/c_cpp_properties.json`, you should see th ### Building and Testing -The `./scripts.sh` file contains multiple commands to make things easy: +The `./scripts` file contains multiple commands to make things easy: | Command | Description | | --------------------- | ----------------------------------------------------------------- | | `build_module` | Builds the NGINX image. | | `rebuild_module` | Re-builds the NGINX image. | -| `start_nginx` | Starts the NGINX container. | -| `stop_nginx` | Stops the NGINX container. | +| `start` | Starts the NGINX container. | +| `stop` | Stops the NGINX container. | | `cp_bin` | Copies the compiled binaries out of the NGINX container. | -| `build_test_runner` | Builds the images used by the test stack (uses Docker compose). | -| `rebuild_test_runner` | Re-builds the images used by the test stack. | -| `test` | Runs `test.sh` against the NGINX container (uses Docker compose). | +| `build_test` | Builds the images used by the test stack. | +| `rebuild_test` | Re-builds the images used by the test stack. | +| `test` | Runs `test.sh` against the NGINX container. | | `test_now` | Runs `test.sh` without rebuilding. | You can run multiple commands in sequence by separating them with a space, e.g.: ```shell -./scripts.sh build_module test +./scripts build_module +./scripts test ``` -To build the Docker images, module, start NGINX, and run the tests against, you can simply do: +To build the Docker images, module, start NGINX, and run the tests against it for all versions, you can simply do: ```shell -./scripts.sh all +./scripts all ``` -When you make a change to the module run `./scripts.sh build_module test` to build a fresh module and run the tests. Note that `rebuild_module` is not often needed as `build_module` hashes the module's source files which will cause a cache miss while building the container, causing the module to be rebuilt. +When you make a change to the module, running `./scripts test` should build a fresh module and run the tests. Note that `rebuild_module` is not often needed as Docker will automatically rebuild the image if the source files have +changed. -When you make a change to the test NGINX config or `test.sh`, run `./scripts.sh test` to run the tests. Similar to above, the test sources are hashed and the containers will be rebuilt as needed. +When you make a change to the test NGINX config or `test.sh`, run `./scripts test` to run the tests. -The image produced with `./scripts.sh build_module` only differs from the official NGINX image in two ways: +The image produced with `./scripts build_module` only differs from the official NGINX image in two ways: - the JWT module itself, and - the `nginx.conf` file is overwritten with our own. -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. +The tests use a customized NGINX image, distinct from the main image, as well as a test runner image. By running `./scripts 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. #### Tracing Test Failures @@ -236,20 +238,23 @@ If you'd like to persist logs across test runs, you can configure the log driver ```shell # need to rebuild the test runner with the proper log driver -LOG_DRIVER=journald ./scripts.sh rebuild_test_runner +export LOG_DRIVER=journald + +# rebuild the test images +./scripts rebuild_test # run the tests -./scripts.sh test +./scripts test -# check the logs -journalctl -eu docker CONTAINER_NAME=jwt-nginx-test +# check the logs -- adjust the container name as needed +journalctl -eu docker CONTAINER_NAME=nginx-auth-jwt-test-nginx ``` 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: ```shell # terminal 1 -./scripts.sh test +./scripts test # terminal 2 journalctl -fu docker CONTAINER_NAME=jwt-nginx-test diff --git a/scripts b/scripts index 9d90185..59fc5c3 100755 --- a/scripts +++ b/scripts @@ -31,6 +31,7 @@ IMAGE_NAME=${IMAGE_NAME:-nginx-auth-jwt} FULL_IMAGE_NAME=${ORG_NAME:-teslagov}/${IMAGE_NAME} TEST_CONTAINER_NAME_PREFIX="${IMAGE_NAME}-test" +TEST_COMPOSE_FILE='test/docker-compose-test.yml' all() { build_module @@ -162,7 +163,8 @@ build_test() { printf "${MAGENTA}Building test NGINX & runner using port ${port}...${NC}\n" docker compose \ -p ${TEST_CONTAINER_NAME_PREFIX} \ - -f ./test/docker-compose-test.yml build \ + -f ${TEST_COMPOSE_FILE} \ + build \ --build-arg RUNNER_BASE_IMAGE=${runnerBaseImage} \ --build-arg PORT=${port} \ --build-arg SSL_PORT=${sslPort} \ @@ -188,12 +190,11 @@ test() { printf "${MAGENTA}Running tests...${NC}\n" docker compose \ -p ${TEST_CONTAINER_NAME_PREFIX} \ - -f ./test/docker-compose-test.yml up \ + -f ${TEST_COMPOSE_FILE} up \ --no-start - - trap 'docker compose -f ./test/docker-compose-test.yml down' 0 - + trap test_cleanup 0 + test_now } @@ -218,6 +219,12 @@ test_now() { echo " NGINX Version: ${NGINX_VERSION}" } +test_cleanup() { + docker compose \ + -p ${TEST_CONTAINER_NAME_PREFIX} \ + -f ${TEST_COMPOSE_FILE} down +} + get_port() { startPort=${1:-8000} endPort=$((startPort + 100)) diff --git a/src/ngx_http_auth_jwt_module.c b/src/ngx_http_auth_jwt_module.c index fe428b4..59b84ac 100644 --- a/src/ngx_http_auth_jwt_module.c +++ b/src/ngx_http_auth_jwt_module.c @@ -290,13 +290,13 @@ static char *merge_extract_var_claims(ngx_conf_t *cf, ngx_command_t *cmd, void * { // add this claim's name to the config struct ngx_str_t *element = ngx_array_push(claims); - + *element = values[i]; // add an http variable for this claim size_t var_name_len = 10 + element->len; u_char *buf = ngx_palloc(cf->pool, sizeof(u_char) * var_name_len); - + if (buf == NULL) { return NGX_CONF_ERROR; @@ -305,7 +305,7 @@ static char *merge_extract_var_claims(ngx_conf_t *cf, ngx_command_t *cmd, void * { ngx_sprintf(buf, "jwt_claim_%V", element); ngx_str_t *var_name = ngx_palloc(cf->pool, sizeof(ngx_str_t)); - + if (var_name == NULL) { return NGX_CONF_ERROR; @@ -314,31 +314,31 @@ static char *merge_extract_var_claims(ngx_conf_t *cf, ngx_command_t *cmd, void * { var_name->data = buf; var_name->len = var_name_len; - + // NGX_HTTP_VAR_CHANGEABLE simplifies the required logic by assuming a JWT claim will always be the same for a given request ngx_http_variable_t *http_var = ngx_http_add_variable(cf, var_name, NGX_HTTP_VAR_CHANGEABLE); - + if (http_var == NULL) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "failed to add variable %V", var_name); - + return NGX_CONF_ERROR; } else { http_var->get_handler = get_jwt_var_claim; - + // store the index of this new claim in the claims array as the "data" that will be passed to the getter ngx_uint_t *claim_idx = ngx_palloc(cf->pool, sizeof(ngx_uint_t)); - + if (claim_idx == NULL) { - return NGX_CONF_ERROR; + return NGX_CONF_ERROR; } else { *claim_idx = claims->nelts - 1; - http_var->data = (uintptr_t) claim_idx; + http_var->data = (uintptr_t)claim_idx; } } } @@ -350,26 +350,26 @@ static char *merge_extract_var_claims(ngx_conf_t *cf, ngx_command_t *cmd, void * static ngx_int_t get_jwt_var_claim(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "getting jwt value for var index %l", *((ngx_uint_t*) data)); + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "getting jwt value for var index %l", *((ngx_uint_t *)data)); auth_jwt_ctx_t *ctx = get_request_jwt_ctx(r); - + if (ctx == NULL) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "no module context found while getting jwt value"); - + return NGX_ERROR; } else { - ngx_uint_t *claim_idx = (ngx_uint_t*) data; - ngx_str_t claim_value = ((ngx_str_t*) ctx->claim_values->elts)[*claim_idx]; - + ngx_uint_t *claim_idx = (ngx_uint_t *)data; + ngx_str_t claim_value = ((ngx_str_t *)ctx->claim_values->elts)[*claim_idx]; + v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->len = claim_value.len; v->data = claim_value.data; - + return NGX_OK; } } @@ -420,7 +420,7 @@ static char *merge_extract_response_claims(ngx_conf_t *cf, ngx_command_t *cmd, v static auth_jwt_ctx_t *get_or_init_jwt_module_ctx(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf) { auth_jwt_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_auth_jwt_module); - + if (ctx != NULL) { return ctx; @@ -434,7 +434,8 @@ static auth_jwt_ctx_t *get_or_init_jwt_module_ctx(ngx_http_request_t *r, auth_jw ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "error allocating jwt module context"); return ctx; } - else { + else + { if (jwtcf->extract_var_claims != NULL) { ctx->claim_values = ngx_array_create(r->pool, jwtcf->extract_var_claims->nelts, sizeof(ngx_str_t)); @@ -460,7 +461,7 @@ static auth_jwt_ctx_t *get_request_jwt_ctx(ngx_http_request_t *r) { auth_jwt_conf_t *jwtcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_jwt_module); - if(!jwtcf->enabled) + if (!jwtcf->enabled) { return NULL; } @@ -587,7 +588,8 @@ static int validate_alg(auth_jwt_conf_t *jwtcf, jwt_t *jwt) { const jwt_alg_t alg = jwt_get_alg(jwt); - if (alg != JWT_ALG_HS256 && alg != JWT_ALG_HS384 && alg != JWT_ALG_HS512 && alg != JWT_ALG_RS256 && alg != JWT_ALG_RS384 && alg != JWT_ALG_RS512 && alg != JWT_ALG_ES256 && alg != JWT_ALG_ES384 && alg != JWT_ALG_ES512) { + if (alg != JWT_ALG_HS256 && alg != JWT_ALG_HS384 && alg != JWT_ALG_HS512 && alg != JWT_ALG_RS256 && alg != JWT_ALG_RS384 && alg != JWT_ALG_RS512 && alg != JWT_ALG_ES256 && alg != JWT_ALG_ES384 && alg != JWT_ALG_ES512) + { return 1; } @@ -625,7 +627,7 @@ static int validate_sub(auth_jwt_conf_t *jwtcf, jwt_t *jwt) static ngx_int_t extract_var_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt, auth_jwt_ctx_t *ctx) { ngx_array_t *claims = jwtcf->extract_var_claims; - + if (claims == NULL || claims->nelts == 0) { return NGX_OK; @@ -633,22 +635,22 @@ static ngx_int_t extract_var_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtc else { const ngx_str_t *claimsPtr = claims->elts; - + for (uint i = 0; i < claims->nelts; ++i) { const ngx_str_t claim = claimsPtr[i]; const char *claimValue = jwt_get_grant(jwt, (char *)claim.data); ngx_str_t value = ngx_string(""); - + if (claimValue != NULL && strlen(claimValue) > 0) { value = char_ptr_to_ngx_str_t(r->pool, claimValue); } - - ((ngx_str_t*) ctx->claim_values->elts)[i] = value; + + ((ngx_str_t *)ctx->claim_values->elts)[i] = value; ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "set var %V to JWT claim value %s", &claim, value.data); } - + return NGX_OK; } } @@ -708,11 +710,16 @@ static ngx_int_t redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf) if (r->method == NGX_HTTP_GET) { const int loginlen = jwtcf->loginurl.len; - const char *scheme = (r->connection->ssl) ? "https" : "http"; + const char *scheme = r->connection->ssl ? "https" : "http"; + ngx_str_t port_variable_name = ngx_string("server_port"); + ngx_int_t port_variable_hash = ngx_hash_key(port_variable_name.data, port_variable_name.len); + ngx_http_variable_value_t *port_var = ngx_http_get_variable(r, &port_variable_name, port_variable_hash); + char *port_str = ""; + uint port_str_len = 0; const ngx_str_t server = r->headers_in.server; ngx_str_t uri_variable_name = ngx_string("request_uri"); ngx_int_t uri_variable_hash = ngx_hash_key(uri_variable_name.data, uri_variable_name.len); - ngx_http_variable_value_t *request_uri_var = ngx_http_get_variable(r, &uri_variable_name, uri_variable_hash); + ngx_http_variable_value_t *uri_var = ngx_http_get_variable(r, &uri_variable_name, uri_variable_hash); ngx_str_t uri; ngx_str_t uri_escaped; uintptr_t escaped_len; @@ -720,12 +727,12 @@ static ngx_int_t redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf) int return_url_idx; // get the URI - if (request_uri_var && !request_uri_var->not_found && request_uri_var->valid) + if (uri_var && !uri_var->not_found && uri_var->valid) { // ideally we would like the URI with the querystring parameters - uri.data = ngx_palloc(r->pool, request_uri_var->len); - uri.len = request_uri_var->len; - ngx_memcpy(uri.data, request_uri_var->data, request_uri_var->len); + uri.data = ngx_palloc(r->pool, uri_var->len); + uri.len = uri_var->len; + ngx_memcpy(uri.data, uri_var->data, uri_var->len); } else { @@ -733,31 +740,59 @@ static ngx_int_t redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf) uri = r->uri; } + if (port_var && !port_var->not_found && port_var->valid) + { + const ngx_uint_t port_num = ngx_atoi(port_var->data, port_var->len); + const bool is_default_port_80 = !r->connection->ssl && port_num == 80; + const bool is_default_port_443 = r->connection->ssl && port_num == 443; + const bool is_non_default_port = !is_default_port_80 && !is_default_port_443; + + if (is_non_default_port) + { + port_str = ngx_palloc(r->pool, NGX_INT_T_LEN + 2); + + ngx_snprintf((u_char *)port_str, sizeof(port_str), ":%d", port_num); + port_str_len = strlen(port_str); + } + } + + // escape the URI escaped_len = 2 * ngx_escape_uri(NULL, uri.data, uri.len, NGX_ESCAPE_ARGS) + uri.len; uri_escaped.data = ngx_palloc(r->pool, escaped_len); uri_escaped.len = escaped_len; ngx_escape_uri(uri_escaped.data, uri.data, uri.len, NGX_ESCAPE_ARGS); - r->headers_out.location->value.len = loginlen + strlen("?return_url=") + strlen(scheme) + strlen("://") + server.len + uri_escaped.len; + // Add up the lengths of: login URL, "?return_url=", scheme, "://", server, port, uri (path) + r->headers_out.location->value.len = loginlen + 12 + strlen(scheme) + 3 + server.len + port_str_len + uri_escaped.len; return_url = ngx_palloc(r->pool, r->headers_out.location->value.len); - ngx_memcpy(return_url, jwtcf->loginurl.data, jwtcf->loginurl.len); + ngx_memcpy(return_url, jwtcf->loginurl.data, jwtcf->loginurl.len); return_url_idx = jwtcf->loginurl.len; - ngx_memcpy(return_url + return_url_idx, "?return_url=", strlen("?return_url=")); - return_url_idx += strlen("?return_url="); - ngx_memcpy(return_url + return_url_idx, scheme, strlen(scheme)); + ngx_memcpy(return_url + return_url_idx, "?return_url=", 12); + return_url_idx += 12; + ngx_memcpy(return_url + return_url_idx, scheme, strlen(scheme)); return_url_idx += strlen(scheme); - ngx_memcpy(return_url + return_url_idx, "://", strlen("://")); - return_url_idx += strlen("://"); - ngx_memcpy(return_url + return_url_idx, server.data, server.len); + ngx_memcpy(return_url + return_url_idx, "://", 3); + return_url_idx += 3; + ngx_memcpy(return_url + return_url_idx, server.data, server.len); return_url_idx += server.len; - ngx_memcpy(return_url + return_url_idx, uri_escaped.data, uri_escaped.len); + + if (port_str_len > 0) + { + ngx_memcpy(return_url + return_url_idx, port_str, port_str_len); + return_url_idx += port_str_len; + } + + if (uri_escaped.len > 0) + { + ngx_memcpy(return_url + return_url_idx, uri_escaped.data, uri_escaped.len); + } r->headers_out.location->value.data = (u_char *)return_url; } diff --git a/test/docker-compose-test.yml b/test/docker-compose-test.yml index 14c88da..72ff710 100644 --- a/test/docker-compose-test.yml +++ b/test/docker-compose-test.yml @@ -1,20 +1,19 @@ services: nginx: - container_name: ${TEST_CONTAINER_NAME_PREFIX}-nginx + container_name: ${TEST_CONTAINER_NAME_PREFIX:?required}-nginx build: context: . dockerfile: test-nginx.dockerfile args: - BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION} + BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION:?required} logging: driver: ${LOG_DRIVER:-journald} runner: - container_name: ${TEST_CONTAINER_NAME_PREFIX}-runner + container_name: ${TEST_CONTAINER_NAME_PREFIX:?required}-runner build: context: . dockerfile: test-runner.dockerfile - depends_on: - nginx \ No newline at end of file diff --git a/test/etc/nginx/conf.d/test.conf b/test/etc/nginx/conf.d/test.conf index 5359434..4e5d764 100644 --- a/test/etc/nginx/conf.d/test.conf +++ b/test/etc/nginx/conf.d/test.conf @@ -405,6 +405,7 @@ vXjq39xtcIBRTO1c2zs= if ($jwt_claim_sub = 'some-long-uuid') { return 200; } + return 401; } @@ -439,4 +440,9 @@ vXjq39xtcIBRTO1c2zs= return 301 /profile/$jwt_claim_sub; } } + + location /return-url { + auth_jwt_enabled on; + auth_jwt_redirect on; + } } diff --git a/test/test-nginx.dockerfile b/test/test-nginx.dockerfile index e12acb4..1065558 100644 --- a/test/test-nginx.dockerfile +++ b/test/test-nginx.dockerfile @@ -1,13 +1,11 @@ ARG BASE_IMAGE -ARG PORT -ARG SSL_PORT -FROM ${BASE_IMAGE} AS NGINX +FROM ${BASE_IMAGE:?required} AS NGINX ARG PORT ARG SSL_PORT + COPY etc/ /etc/ -RUN sed -i "s|%{PORT}|${PORT}|" /etc/nginx/conf.d/test.conf -RUN sed -i "s|%{SSL_PORT}|${SSL_PORT}|" /etc/nginx/conf.d/test.conf + COPY <<` /usr/share/nginx/html/index.html Test @@ -16,3 +14,6 @@ COPY <<` /usr/share/nginx/html/index.html ` + +RUN sed -i "s|%{PORT}|${PORT:?required}|" /etc/nginx/conf.d/test.conf +RUN sed -i "s|%{SSL_PORT}|${SSL_PORT:?required}|" /etc/nginx/conf.d/test.conf diff --git a/test/test-runner.dockerfile b/test/test-runner.dockerfile index 0aca095..18fc3d3 100644 --- a/test/test-runner.dockerfile +++ b/test/test-runner.dockerfile @@ -1,16 +1,18 @@ ARG RUNNER_BASE_IMAGE -ARG PORT -ARG SSL_PORT -FROM ${RUNNER_BASE_IMAGE} +FROM ${RUNNER_BASE_IMAGE:?required} ARG PORT ARG SSL_PORT -ENV PORT=${PORT} -ENV SSL_PORT=${SSL_PORT} + +ENV PORT=${PORT:?required} +ENV SSL_PORT=${SSL_PORT:?required} + RUN <<` set -e apt-get update apt-get install -y curl bash ` + COPY test.sh . -CMD ./test.sh ${PORT} ${SSL_PORT} + +CMD ./test.sh diff --git a/test/test.sh b/test/test.sh index 747124c..c726a75 100755 --- a/test/test.sh +++ b/test/test.sh @@ -29,7 +29,7 @@ run_test () { local response= local testNum="${GRAY}${NUM_TESTS}${NC}\t" - while getopts "n:sp:r:c:x:" option; do + while getopts "n:asp:r:c:x:" option; do case $option in n) name=$OPTARG;; @@ -73,7 +73,7 @@ run_test () { fi if [ "${okay}" == '1' ] && [ "${expectedResponseRegex}" != "" ] && ! [[ "${response}" =~ ${expectedResponseRegex} ]]; then - printf "${RED}${name} -- regex not found in response\n\tPath: ${path}\n\tRegEx: ${expectedResponseRegex}" + printf "${RED}${name} -- regex not found in response\n\tPath: ${path}\n\tRegEx: ${expectedResponseRegex//%/%%}" NUM_FAILED=$((${NUM_FAILED} + 1)) okay=0 fi @@ -113,8 +113,8 @@ main() { -p '/' \ -c '200' - run_test -s \ - -n '[SSL] when auth disabled, should return 200' \ + run_test -n '[SSL] when auth disabled, should return 200' \ + -s \ -p '/' \ -c '200' @@ -350,6 +350,26 @@ main() { -r '< Location: http://nginx:8000/profile/some-long-uuid' \ -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + run_test -n 'returns 302 if auth enabled and no JWT provided' \ + -p '/return-url' \ + -c '302' + + run_test -n 'redirects to login if auth enabled and no JWT provided' \ + -p '/return-url' \ + -r '< Location: https://example\.com/login.*' + + run_test -n 'adds return_url to login URL when redirected to login' \ + -p '/return-url' \ + -r '< Location: https://example\.com/login\?return_url=http://nginx.*' + + run_test -n 'return_url includes port when redirected to login' \ + -p '/return-url' \ + -r "< Location: https://example\.com/login\?return_url=http://nginx:${PORT}/return-url" + + run_test -n 'return_url includes query when redirected to login' \ + -p '/return-url?test=123' \ + -r '< Location: https://example\.com/login\?return_url=http://nginx.*/return-url%3Ftest=123' + if [[ "${NUM_FAILED}" = '0' ]]; then printf "\nRan ${NUM_TESTS} tests successfully (skipped ${NUM_SKIPPED}).\n" return 0 From a2e3e914998d4b82e91d2db9ec9e482f3b562c43 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Wed, 5 Feb 2025 14:37:41 -0500 Subject: [PATCH 21/29] fix artifact processing in workflow --- .github/workflows/make-releases.yml | 62 +++- test/docker-compose-test.yml | 19 -- test/ec_key_256.pem | 5 - test/ec_key_384.pem | 6 - test/ec_key_521.pem | 8 - test/etc/nginx/conf.d/test.conf | 448 ---------------------------- test/etc/nginx/ec_key_256-pub.pem | 4 - test/etc/nginx/ec_key_384-pub.pem | 5 - test/etc/nginx/ec_key_521-pub.pem | 6 - test/etc/nginx/rsa_key_2048-pub.pem | 9 - test/etc/nginx/test.crt | 23 -- test/etc/nginx/test.key | 28 -- test/rsa_key_2048.pem | 28 -- test/test-nginx.dockerfile | 19 -- test/test-runner.dockerfile | 18 -- test/test.sh | 387 ------------------------ 16 files changed, 49 insertions(+), 1026 deletions(-) delete mode 100644 test/docker-compose-test.yml delete mode 100644 test/ec_key_256.pem delete mode 100644 test/ec_key_384.pem delete mode 100644 test/ec_key_521.pem delete mode 100644 test/etc/nginx/conf.d/test.conf delete mode 100644 test/etc/nginx/ec_key_256-pub.pem delete mode 100644 test/etc/nginx/ec_key_384-pub.pem delete mode 100644 test/etc/nginx/ec_key_521-pub.pem delete mode 100755 test/etc/nginx/rsa_key_2048-pub.pem delete mode 100644 test/etc/nginx/test.crt delete mode 100644 test/etc/nginx/test.key delete mode 100755 test/rsa_key_2048.pem delete mode 100644 test/test-nginx.dockerfile delete mode 100644 test/test-runner.dockerfile delete mode 100755 test/test.sh diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index 8d7c906..487c4bf 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -4,8 +4,29 @@ on: workflow_dispatch: jobs: + meta: + name: Get Metadata + runs-on: ubuntu-latest + outputs: + tag: ${{steps.meta.outputs.tag}} + steps: + + - name: Checkout Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get Metadata + id: meta + run: | + set -eu + tag=$(git describe --tags --abbrev=0) + + echo "tag=${tag}" >> $GITHUB_OUTPUT + build: name: "NGINX: ${{ matrix.nginx-version }}; libjwt: ${{ matrix.libjwt-version }}" + needs: meta strategy: matrix: # NGINX versions to build/test against @@ -26,18 +47,17 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 with: - fetch-depth: 0 path: ngx-http-auth-jwt-module - name: Get Metadata id: meta run: | - set -eux - cd ngx-http-auth-jwt-module - - tag=$(git describe --tags --abbrev=0) + set -eu + artifact="ngx-http-auth-jwt-module-${{needs.meta.outputs.tag}}_libjwt-${{matrix.libjwt-version}}_nginx-${{matrix.nginx-version}}" + + echo "artifact=${artifact}" >> $GITHUB_OUTPUT + echo "filename=${artifact}.tgz" >> $GITHUB_OUTPUT - echo "filename=ngx-http-auth-jwt-module-${tag}_libjwt-${{matrix.libjwt-version}}_nginx-${{matrix.nginx-version}}.tgz" >> $GITHUB_OUTPUT # TODO cache the build result so we don't have to do this every time? - name: Download jansson @@ -104,11 +124,14 @@ jobs: uses: actions/upload-artifact@v4 with: if-no-files-found: error - name: ${{steps.meta.outputs.filename}} + name: ${{steps.meta.outputs.artifact}} + path: ${{steps.meta.outputs.filename}} release: name: Create/Update Release - needs: build + needs: + - meta + - build runs-on: ubuntu-latest permissions: contents: write @@ -119,25 +142,38 @@ jobs: run: | echo "date_now=$(date --rfc-3339=seconds)" >> "${GITHUB_OUTPUT}" - - name: Download Build Artifacts + - name: Download Artifacts uses: actions/download-artifact@v4 with: path: artifacts - - name: Upload Builds to Release + - name: Flatten Artifacts + run: | + set -eu + + cd artifacts + + for f in $(find . -type f); do + echo "Staging: ${f}" + mv "${f}" . + done + + find . -type d -mindepth 1 -exec rm -rf "{}" + + + - name: Create/Update Release uses: ncipollo/release-action@v1 with: - name: 'Development Build: ${{ github.ref_name }}@${{ github.sha }}' + tag: ${{needs.meta.outputs.tag}} + name: "Pre-release: ${{needs.meta.outputs.tag}}" body: | > [!WARNING] > This is an automatically generated pre-release version of the module, which includes the latest master branch changes. > Please report any bugs you find. - Build Date: `${{ steps.vars.outputs.date_now }}` - - Commit: ${{ github.sha }} + - Commit: `${{ github.sha }}` prerelease: true allowUpdates: true removeArtifacts: true artifactErrorsFailBuild: true artifacts: artifacts/* - tag: dev-build diff --git a/test/docker-compose-test.yml b/test/docker-compose-test.yml deleted file mode 100644 index 72ff710..0000000 --- a/test/docker-compose-test.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - - nginx: - container_name: ${TEST_CONTAINER_NAME_PREFIX:?required}-nginx - build: - context: . - dockerfile: test-nginx.dockerfile - args: - BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION:?required} - logging: - driver: ${LOG_DRIVER:-journald} - - runner: - container_name: ${TEST_CONTAINER_NAME_PREFIX:?required}-runner - build: - context: . - dockerfile: test-runner.dockerfile - depends_on: - - nginx \ No newline at end of file diff --git a/test/ec_key_256.pem b/test/ec_key_256.pem deleted file mode 100644 index 4206969..0000000 --- a/test/ec_key_256.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgOlEBGcZxxhv8FkN0 -YIvax6fnhJbMeotzIEBxIglkNu6hRANCAATP1NpDzvZmKd2Mw6hIrv4nzUfNu7OK -mT5VuL5LhvUgzTqVGuxwevA7DlFsNVSfCljIBG3geio3fcd4k0Z9SygL ------END PRIVATE KEY----- diff --git a/test/ec_key_384.pem b/test/ec_key_384.pem deleted file mode 100644 index 2aa5780..0000000 --- a/test/ec_key_384.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDADyrL6llSQoQOZ/PF/ -l761kAbrTwn4vu30Kr34ScW6bRKVXLq3cT3QssJ1nF9B63qhZANiAAQ48dOfIEd3 -0TCVE0JT4ZU0Db7Ftz+ex7lojP7uqTY9OI59yoMB01zUN4JK30BRXS9Yv0A9Bu1z -fgLu93FSn0kd0zIPMvuu5LUt60M/miSt2lA0OrqFhKjx6FFdN/lNh64= ------END PRIVATE KEY----- diff --git a/test/ec_key_521.pem b/test/ec_key_521.pem deleted file mode 100644 index 10471dc..0000000 --- a/test/ec_key_521.pem +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAKkag6aVn4XAbaALo -0b3pypdP5RBX7uKxHmKlkNCcpA0oVTdgjnM5NpJP8ZOM6NjVhEzsn6c/Tdn8hL8w -SI55hFWhgYkDgYYABABpTipSvbs8fq44u4fA+v7DTNYViA58sqbrxjxdzwWZ8eEj -CXsH7yzSGx3Y19NSyrX8HbjWmrj5uxiKeFCB8mGzTwDcFIKCMeMkHjZs/fmVOumR -a2XSpj7BP6wqcN6Pf+UqECivGAZGRHoabo/dm5zF9M3gO+G9eOrf3G1wgFFM7Vzb -Ow== ------END PRIVATE KEY----- diff --git a/test/etc/nginx/conf.d/test.conf b/test/etc/nginx/conf.d/test.conf deleted file mode 100644 index 4e5d764..0000000 --- a/test/etc/nginx/conf.d/test.conf +++ /dev/null @@ -1,448 +0,0 @@ -error_log /var/log/nginx/debug.log debug; -access_log /var/log/nginx/access.log; - -server { - listen %{PORT}; - listen %{SSL_PORT} ssl; - server_name localhost; - - ssl_certificate /etc/nginx/test.crt; - ssl_certificate_key /etc/nginx/test.key; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - - auth_jwt_key "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF"; - auth_jwt_loginurl "https://example.com/login"; - auth_jwt_enabled off; - - location / { - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/default { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location COOKIE=jwt; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/default/validate-sub { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_validate_sub on; - auth_jwt_location COOKIE=jwt; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/default/no-redirect { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location COOKIE=jwt; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/hs256 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location COOKIE=jwt; - auth_jwt_algorithm HS256; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/hs384 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location COOKIE=jwt; - auth_jwt_algorithm HS384; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/hs512 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location COOKIE=jwt; - auth_jwt_algorithm HS512; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/es256 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location COOKIE=jwt; - auth_jwt_algorithm ES256; - auth_jwt_key "-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz -ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== ------END PUBLIC KEY-----"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/es384 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location COOKIE=jwt; - auth_jwt_algorithm ES384; - auth_jwt_key "-----BEGIN PUBLIC KEY----- -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 -aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 -LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu ------END PUBLIC KEY-----"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/cookie/es512 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location COOKIE=jwt; - auth_jwt_algorithm ES512; - auth_jwt_key "-----BEGIN PUBLIC KEY----- -MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO -fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC -gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh -vXjq39xtcIBRTO1c2zs= ------END PUBLIC KEY-----"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/default { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/default/no-redirect { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/default/proxy-header { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - - add_header "Test-Authorization" "$http_authorization"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/rs256 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_key "-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh -uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+ -iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM -ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g -6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe -K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t -BwIDAQAB ------END PUBLIC KEY-----"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/es256 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_key "-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz -ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== ------END PUBLIC KEY-----"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/es384 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_key "-----BEGIN PUBLIC KEY----- -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 -aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 -LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu ------END PUBLIC KEY-----"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/es512 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_key "-----BEGIN PUBLIC KEY----- -MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO -fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC -gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh -vXjq39xtcIBRTO1c2zs= ------END PUBLIC KEY-----"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/rs256/file { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_algorithm RS256; - auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/rs384/file { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_algorithm RS384; - auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/rs512/file { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_algorithm RS512; - auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/es256/file { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_algorithm ES256; - auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/ec_key_256-pub.pem"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/es384/file { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_algorithm ES384; - auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/ec_key_384-pub.pem"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/auth-header/es512/file { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Authorization; - auth_jwt_algorithm ES512; - auth_jwt_use_keyfile on; - auth_jwt_keyfile_path "/etc/nginx/ec_key_521-pub.pem"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/custom-header/hs256 { - auth_jwt_enabled on; - auth_jwt_redirect on; - auth_jwt_location HEADER=Auth-Token; - auth_jwt_algorithm HS256; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/extract-claim/request/sub { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_request_claims sub; - - add_header "Test" "sub=$http_jwt_sub"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/extract-claim/request/name-1 { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_request_claims firstName lastName; - - add_header "Test" "firstName=$http_jwt_firstname; lastName=$http_jwt_lastname"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/extract-claim/request/name-2 { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_request_claims firstName; - auth_jwt_extract_request_claims lastName; - - add_header "Test" "firstName=$http_jwt_firstname; lastName=$http_jwt_lastname"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/extract-claim/request/nested { - location /secure/extract-claim/request/nested { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_request_claims username; - - add_header "Test" "username=$http_jwt_username"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - } - - location /secure/extract-claim/response/sub { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_response_claims sub; - - add_header "Test" "sub=$sent_http_jwt_sub"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/extract-claim/response/name-1 { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_response_claims firstName lastName; - - add_header "Test" "firstName=$sent_http_jwt_firstname; lastName=$sent_http_jwt_lastname"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/extract-claim/response/name-2 { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_response_claims firstName; - auth_jwt_extract_response_claims lastName; - - add_header "Test" "firstName=$sent_http_jwt_firstname; lastName=$sent_http_jwt_lastname"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - - location /secure/extract-claim/response/nested { - location /secure/extract-claim/response/nested { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_response_claims username; - - add_header "Test" "username=$sent_http_jwt_username"; - - alias /usr/share/nginx/html/; - try_files index.html =404; - } - } - - location /secure/extract-claim/if/sub { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_var_claims sub; - - if ($jwt_claim_sub = 'some-long-uuid') { - return 200; - } - - return 401; - } - - location /secure/extract-claim/body/sub { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_extract_var_claims sub; - - return 200 "sub: $jwt_claim_sub"; - } - - location /secure/extract-claim/body/multiple { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_validate_sub on; - auth_jwt_extract_var_claims firstName middleName lastName; - - return 200 "you are: $jwt_claim_firstName $jwt_claim_middleName $jwt_claim_lastName"; - } - - location /profile { - auth_jwt_enabled on; - auth_jwt_redirect off; - auth_jwt_location HEADER=Authorization; - auth_jwt_validate_sub on; - - location /profile/me { - auth_jwt_extract_var_claims sub; - - return 301 /profile/$jwt_claim_sub; - } - } - - location /return-url { - auth_jwt_enabled on; - auth_jwt_redirect on; - } -} diff --git a/test/etc/nginx/ec_key_256-pub.pem b/test/etc/nginx/ec_key_256-pub.pem deleted file mode 100644 index 3306ea0..0000000 --- a/test/etc/nginx/ec_key_256-pub.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz -ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== ------END PUBLIC KEY----- diff --git a/test/etc/nginx/ec_key_384-pub.pem b/test/etc/nginx/ec_key_384-pub.pem deleted file mode 100644 index e642ed1..0000000 --- a/test/etc/nginx/ec_key_384-pub.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PUBLIC KEY----- -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 -aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 -LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu ------END PUBLIC KEY----- diff --git a/test/etc/nginx/ec_key_521-pub.pem b/test/etc/nginx/ec_key_521-pub.pem deleted file mode 100644 index 0cb875c..0000000 --- a/test/etc/nginx/ec_key_521-pub.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO -fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC -gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh -vXjq39xtcIBRTO1c2zs= ------END PUBLIC KEY----- diff --git a/test/etc/nginx/rsa_key_2048-pub.pem b/test/etc/nginx/rsa_key_2048-pub.pem deleted file mode 100755 index 01f59bf..0000000 --- a/test/etc/nginx/rsa_key_2048-pub.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh -uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+ -iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM -ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g -6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe -K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t -BwIDAQAB ------END PUBLIC KEY----- diff --git a/test/etc/nginx/test.crt b/test/etc/nginx/test.crt deleted file mode 100644 index fb406ba..0000000 --- a/test/etc/nginx/test.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIUMG9M4Itu0cOyX0+La+7huiIoX6YwDQYJKoZIhvcNAQEL -BQAwcTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRUwEwYDVQQHDAxG -YWxscyBDaHVyY2gxHzAdBgNVBAoMFlRlc2xhIEdvdmVybm1lbnQsIEluYy4xFzAV -BgNVBAsMDk5HSU5YIEF1dGggSldUMB4XDTI0MDMxNTE4MTM1MloXDTM0MDMxMzE4 -MTM1MlowcTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRUwEwYDVQQH -DAxGYWxscyBDaHVyY2gxHzAdBgNVBAoMFlRlc2xhIEdvdmVybm1lbnQsIEluYy4x -FzAVBgNVBAsMDk5HSU5YIEF1dGggSldUMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAih41Ct5XgcSTz7ZVAjBb0t0z9Qae08aseoMEKJf7AmNqKtsvzeAw -/DJxOWJR5VPtUWhFAmXxPfG2B6aiSIVJVpG9yzcdQlCvyJG7Ub4QCm5GXwpU+zDC -qmD5ksz9QMdOzvRLypAU1ciZiCXjwpUnW+BZyZ9Tpmsxm6/gOzkd3rxoIbc9uXxp -5o4n6k02EPSzLzUhkZnhLQrOAGUB7+q11FAU5eNMlTWC9gQUsbNaTVtKmM2eV9BA -UHdX2GbkfFbN22l3Wey4oyNZWmye1ZFOPyBR+tyU3pofhb+R+hTFmeNBzrJq3i30 -Qi0B8AnulKdOjnTysPYjDTrN6xcVDWNmPQIDAQABo1MwUTAdBgNVHQ4EFgQUczdy -7s64NJHNGsQTf/zwFnQe6LMwHwYDVR0jBBgwFoAUczdy7s64NJHNGsQTf/zwFnQe -6LMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfcxCiz6ShHof -lXiE2j+s556SM2n8oW/S1BSjFC2wF1uKVeMJA1gAaWObC3ElqffFlqTdCorhgRS/ -knWa+Sqe/jWBSgwLG/e5DvxXWjD7b7kZdAZNy9evs5nhVfcLT+GyvB/z5GdAFY7s -xYmLrC07ubhHIL9h7lhNKbRr++o+BcClQBZKRO4fxBwXxqx/rHudjH87Wr61Ov52 -90xNjwcqvevY0skmPao5+oyxkURdKZualNxiOGMPpywkpJkfl8Az5xKAJhUMAtFR -smhQduejEkcxfxtsiYgVoulI29GAsMr9zHps9zb5k0+SWIiSixjQ0CpRhLcNYu4F -QPgLQLGwUQ== ------END CERTIFICATE----- diff --git a/test/etc/nginx/test.key b/test/etc/nginx/test.key deleted file mode 100644 index 13ec754..0000000 --- a/test/etc/nginx/test.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCKHjUK3leBxJPP -tlUCMFvS3TP1Bp7Txqx6gwQol/sCY2oq2y/N4DD8MnE5YlHlU+1RaEUCZfE98bYH -pqJIhUlWkb3LNx1CUK/IkbtRvhAKbkZfClT7MMKqYPmSzP1Ax07O9EvKkBTVyJmI -JePClSdb4FnJn1OmazGbr+A7OR3evGghtz25fGnmjifqTTYQ9LMvNSGRmeEtCs4A -ZQHv6rXUUBTl40yVNYL2BBSxs1pNW0qYzZ5X0EBQd1fYZuR8Vs3baXdZ7LijI1la -bJ7VkU4/IFH63JTemh+Fv5H6FMWZ40HOsmreLfRCLQHwCe6Up06OdPKw9iMNOs3r -FxUNY2Y9AgMBAAECggEAAkwEggGp/xb67FCyDJ8rdimTZFPi9U7coUCN8HNI/qrf -lTnfvox0oOUUqMMmIIQeS/HJ4ANvZe8GO3QkE8R5Sg7F0yjZL2tyTCNPgOMCMK8E -mmHS58brHdrbm658C1ILnfmssjNmNueNbuW00Koa8imCsY2ZEW+L7vTKuMFqg6c+ -BDJxC4yoCPwSTVfcajjzI6FVfphE0pd8Ho/sE8vTqdmovh23+vgfNUq1L9Smvf7R -YLM+hS1ouRP2BI5AN0sm04Kxd8MKPzuwCxteoZ9Y9YHyr1JeWGTTL0T24+LwUee/ -24zXZFrzpTgmtDYeEuVWsF5bP/fMS4Fctda3pdJMsQKBgQDCANjGDwwfSCCev2kl -WdrFJywhn5hWLWFwlo/FwLOsFJtejaBwIDRQCMPZ74H+KMHwUnO3vTanKJWqDRP9 -CdMh94C1BqobRV6rN4HgA4Opxim1EyRWHV6ui41zokk2mJrwUzKkR8t9lt9EZKrk -ZPyKER9A4hBqBmYvaYxodN8U1QKBgQC2QXUQq9j7niT7t4xMi0e9vnPLs0z1yUK9 -0nzKwTHDPflk3o2sKvH7199qVkc15JQ9DQ7NuYD7ezLbE3DJuVzpNDAfNXmfWHmp -7ukdnxyn6ZCmzQY7/fTpJTEGKVQMVCgf2f5ANgxm5EmN0yWRMcEt1VXIwCisY56p -o6nwv/1fyQKBgQCJBnIVyjEEszwfBBEvCX0kvVtFUGUXkSv+isl3onkFNPTcXuoP -6B8q3FYAy1MkggMhTAthnqpIfLjhCCWzFspidl8Y/WEOq/uGsUjxQWowcr+onqGO -lWX3oKfDIb/WaQkeb5UYRYFr7jE6LGQrt0xL9HX/rOxtBqIMIN/EM7ARFQKBgDAJ -zMtaIFUh9+mJFafPRleS7X6RggV+yOKzqkTe6zjlCuk1Z+4rW6Df43lpyFdCKnh1 -CqPa805VyK/Jzf69pumo4c44EBiZ/2d1G2i9WZZAj+oHPE9vvq/9J5DSL98YB4Nt -uABAvsAYB/Mj5lEA5kQoaPYDADWABH/+LXrRf/1RAoGAUvxPvmpkGMC+KdmjLam7 -CPC3+y4MZOyZ11BhOxLhd1K2qcQd9K7tkjUhNxRn5GVzpzOKeFJFtiih2uN+PBNJ -oylPR03uk/7D52b1OYaJhs9bQkth//Qk935nyRM26C2vG4tQLfT/cFi5F53n0ZCQ -7e8O6+QY0lZnpvsfnt8YIsM= ------END PRIVATE KEY----- diff --git a/test/rsa_key_2048.pem b/test/rsa_key_2048.pem deleted file mode 100755 index 0f58120..0000000 --- a/test/rsa_key_2048.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC2kwAziXUf33m -iqWp0yG6o259+nj7hpQLC4UT0Hmz0wmvreDJ/yNbSgOvsxvVdvzL2IaRZ+Gi5mo0 -lswWvL6IGz7PZO0kXTq9sdBnNqMOx27HddV9e/2/p0MgibJTbgywY2Sk23QYhJpq -Kq/nU0xlBfSaI5ddZ2RC9ZNkVeGawUKYksTruhAVJqviHN8BoK6VowP5vcxyyOWH -TK9KruDqzCIhqwRTeo0spokBkTN/LCuhVivcHAzUiJVtB4qAiTI9L/zkzhjpKz9P -45aLU54rj011gG8U/6E1USh5nMnPkr+d3oLfkhfS3Zs3kJVdyFQWZpQxiTaI92Fd -2wLvbS0HAgMBAAECggEAD8dTnkETSSjlzhRuI9loAtAXM3Zj86JLPLW7GgaoxEoT -n7lJ2bGicFMHB2ROnbOb9vnas82gtOtJsGaBslmoaCckp/C5T1eJWTEb+i+vdpPp -wZcmKZovyyRFSE4+NYlU17fEv6DRvuaGBpDcW7QgHJIl45F8QWEM+msee2KE+V4G -z/9vAQ+sOlvsb4mJP1tJIBx9Lb5loVREwCRy2Ha9tnWdDNar8EYkOn8si4snPT+E -3ZCy8mlcZyUkZeiS/HdtydxZfoiwrSRYamd1diQpPhWCeRteQ802a7ds0Y2YzgfF -UaYjNuRQm7zA//hwbXS7ELPyNMU15N00bajlG0tUOQKBgQDnLy01l20OneW6A2cI -DIDyYhy5O7uulsaEtJReUlcjEDMkin8b767q2VZHb//3ZH+ipnRYByUUyYUhdOs2 -DYRGGeAebnH8wpTT4FCYxUsIUpDfB7RwfdBONgaKewTJz/FPswy1Ye0b5H2c6vVi -m2FZ33HQcoZ3wvFFqyGVnMzpOwKBgQDXxL95yoxUGKa8vMzcE3Cn01szh0dFq0sq -cFpM+HWLVr84CItuG9H6L0KaStEEIOiJsxOVpcXfFFhsJvOGhMA4DQTwH4WuXmXp -1PoVMDlV65PYqvhzwL4+QhvZO2bsrEunITXOmU7CI6kilnAN3LuP4HbqZgoX9lqP -I31VYzLupQKBgGEYck9w0s/xxxtR9ILv5XRnepLdoJzaHHR991aKFKjYU/KD7JDK -INfoAhGs23+HCQhCCtkx3wQVA0Ii/erM0II0ueluD5fODX3TV2ZibnoHW2sgrEsW -vFcs36BnvIIaQMptc+f2QgSV+Z/fGsKYadG6Q+39O7au/HB7SHayzWkjAoGBAMgt -Fzslp9TpXd9iBWjzfCOnGUiP65Z+GWkQ/SXFqD+SRir0+m43zzGdoNvGJ23+Hd6K -TdQbDJ0uoe4MoQeepzoZEgi4JeykVUZ/uVfo+nh06yArVf8FxTm7WVzLGGzgV/uA -+wtl/cRtEyAsk1649yW/KHPEIP8kJdYAJeoO8xSlAoGAERMrkFR7KGYZG1eFNRdV -mJMq+Ibxyw8ks/CbiI+n3yUyk1U8962ol2Q0T4qjBmb26L5rrhNQhneM4e8mo9FX -LlQapYkPvkdrqW0Bp72A/UNAvcGTmN7z5OCJGMUutx2hmEAlrYmpLKS8pM/p9zpK -tEOtzsP5GMDYVlEp1jYSjzQ= ------END PRIVATE KEY----- diff --git a/test/test-nginx.dockerfile b/test/test-nginx.dockerfile deleted file mode 100644 index 1065558..0000000 --- a/test/test-nginx.dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -ARG BASE_IMAGE - -FROM ${BASE_IMAGE:?required} AS NGINX -ARG PORT -ARG SSL_PORT - -COPY etc/ /etc/ - -COPY <<` /usr/share/nginx/html/index.html - - Test - -

NGINX Auth-JWT Module Test

- - -` - -RUN sed -i "s|%{PORT}|${PORT:?required}|" /etc/nginx/conf.d/test.conf -RUN sed -i "s|%{SSL_PORT}|${SSL_PORT:?required}|" /etc/nginx/conf.d/test.conf diff --git a/test/test-runner.dockerfile b/test/test-runner.dockerfile deleted file mode 100644 index 18fc3d3..0000000 --- a/test/test-runner.dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -ARG RUNNER_BASE_IMAGE - -FROM ${RUNNER_BASE_IMAGE:?required} -ARG PORT -ARG SSL_PORT - -ENV PORT=${PORT:?required} -ENV SSL_PORT=${SSL_PORT:?required} - -RUN <<` - set -e - apt-get update - apt-get install -y curl bash -` - -COPY test.sh . - -CMD ./test.sh diff --git a/test/test.sh b/test/test.sh deleted file mode 100755 index c726a75..0000000 --- a/test/test.sh +++ /dev/null @@ -1,387 +0,0 @@ -#!/bin/bash -eu - -# set a test # here to execute only that test and output additional info -DEBUG= - -RED='\e[31m' -GREEN='\e[32m' -GRAY='\e[90m' -NC='\e[00m' - -NUM_TESTS=0; -NUM_SKIPPED=0; -NUM_FAILED=0; - -run_test () { - NUM_TESTS=$((${NUM_TESTS} + 1)); - - if [ "${DEBUG}" == '' ] || [ ${DEBUG} == ${NUM_TESTS} ]; then - local OPTIND; - local name= - local path= - local expectedCode= - local expectedResponseRegex= - local extraCurlOpts= - local scheme='http' - local port=${PORT} - local curlCommand= - local exitCode= - local response= - local testNum="${GRAY}${NUM_TESTS}${NC}\t" - - while getopts "n:asp:r:c:x:" option; do - case $option in - n) - name=$OPTARG;; - s) - scheme='https' - port=${SSL_PORT};; - p) - path=$OPTARG;; - c) - expectedCode=$OPTARG;; - r) - expectedResponseRegex=$OPTARG;; - x) - extraCurlOpts=$OPTARG;; - \?) # Invalid option - printf "Error: Invalid option\n"; - exit;; - esac - done - - curlCommand="curl -skv ${scheme}://nginx:${port}${path} -H 'Cache-Control: no-cache' ${extraCurlOpts} 2>&1" - response=$(eval "${curlCommand}") - exitCode=$? - - printf "\n${testNum}" - - if [ "${exitCode}" -ne "0" ]; then - printf "${RED}${name} -- unexpected exit code from cURL\n\tcURL Exit Code: ${exitCode}"; - NUM_FAILED=$((${NUM_FAILED} + 1)); - else - local okay=1 - - if [ "${expectedCode}" != "" ]; then - local responseCode=$(echo "${response}" | grep -Eo 'HTTP/1.1 ([0-9]{3})' | awk '{print $2}') - - if [ "${expectedCode}" != "${responseCode}" ]; then - printf "${RED}${name} -- unexpected status code\n\tExpected: ${expectedCode}\n\tActual: ${responseCode}\n\tPath: ${path}" - NUM_FAILED=$((${NUM_FAILED} + 1)) - okay=0 - fi - fi - - if [ "${okay}" == '1' ] && [ "${expectedResponseRegex}" != "" ] && ! [[ "${response}" =~ ${expectedResponseRegex} ]]; then - printf "${RED}${name} -- regex not found in response\n\tPath: ${path}\n\tRegEx: ${expectedResponseRegex//%/%%}" - NUM_FAILED=$((${NUM_FAILED} + 1)) - okay=0 - fi - - if [ "${okay}" == '1' ]; then - printf "${GREEN}${name}"; - fi - fi - - if [ "${DEBUG}" == "${NUM_TESTS}" ]; then - printf '\n\tcURL Command: %s' "${curlCommand:---}" - printf '\n\tResponse: %s' "${response:---}" - fi - - printf "${NC}\n" - else - NUM_SKIPPED=$((${NUM_SKIPPED} + 1)) - fi -} - -main() { - local JWT_HS256_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.r8tG8IZheiQ-i6HqUYyJj9V6dipgcQ4ZIdxau6QCZDo - local JWT_HS256_MISSING_SUB=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaXJzdE5hbWUiOiJoZWxsbyIsImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwicm9sZXMiOlsidGhpcyIsInRoYXQiLCJ0aGVvdGhlciJdLCJpc3MiOiJpc3N1ZXIiLCJwZXJzb25JZCI6Ijc1YmIzY2M3LWI5MzMtNDRmMC05M2M2LTE0N2IwODJmYWRiNSIsImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.lD6jUsazVtzeGhRTNeP_b2Zs6O798V2FQql11QOEI1Q - local JWT_HS256_MISSING_EMAIL=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.tJoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM - local JWT_HS384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.SS57j7PEybjbsp3g5W-IhhJHBmG5K-97qvgBKL16xj9ey-uMeEenWjGbB2vVp0kq - local JWT_HS512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.xtSU6EWN2LILVsYzJFJpKnRkqjn_3qjz-J2ttNKnhZ60_5YjFeC8io4k8k1u77zlohSWvWMdugD9ZaB3vjJo-w - local JWT_RS256_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwgImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwgInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwgImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwgImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.cn5Gb75XL-r7TMsPuqzWoKZ06ZsyF_VZIG0Ohn8uZZFeF8dFUhSrEOYe8WFN6Eon8a8LC0OCI9eNdGiD4m_e9TD1Iz2juqaeos-6yd7SWuODr4YS8KD3cqfXndnLRPzp9PC_UIpATsbqOmxGDrRKvHsQq0TuIXImU3rM_m3kFJFgtoJFHx3KmZUo_Ozkyhhc6Pukikhy6odNAtEyLHP5_tabMXtkeAuIlG8dhjAxef4mJLexYFclG-vl7No5VBU4JrMbfgyxtobcYoE-bDIpmQHywrwo6Li7X0hgHJ17sfS3G2YMHmE-Ij_W2Lf9kf5r2r12DUvg44SLIfM58pCINQ - local JWT_RS256_INVALID=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._aQmIBL4CVBxU1fNMOHp0kkagFaaX2TvAEenizytwd0 - local JWT_RS384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.H35bTcZRhepWIoa8pKCbUMRuAOkVX9K5hJjc6tPmQwWmTw8lrktsvmMzJg_rgqnJLnAkciSIQw5EDj7fngS5zX2ThyRxrkPuE2Uiyw2Ect-mo9Kg1lrWgnyZCuCgq-Up9HQRAv0160mePlm8Gs4TOY6CPr38zwTcDZsy_Keq93igDQV8WuuWAGICaGd5ZyUOPjjzGShRjTU8Szz7fnpZpTtYRCYVo0pc5yfRWYm0fdn-4AseyGvd8JJ2xfnAEe4kZOkz7X1MLKtL0slKg3m2PH1lD7HwxIawXRTPWxArhJ9dcTNiDUrqtde2juGwOuMD_zTsb2Jj0_rmRb0Q6aljNw - local JWT_RS512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.iUupyKypfXJ5aZWfItSW-mOmx9a4C4X7Yr5p5Fk8W75ZhkOq0EeNfstTxx870brhkdPovBhO2LYI44_HoH9XicQNL6JnFprE0r61eJFngbuzlhRQiWpq0xYrazJWc9zB7_GgL2ZCwtw-Ts3G23Q0632wVm6-d7MKvG7RS8aEjN-MuVGdtLglH3forpItmFxw-if40EQsBL7hncN_XNcQTO4KPHkqmlpac_oKXRrLFDIIt2tB6OOpvY4QcpERoxexp4pi2f-JoINnWX_dU5JnIs3ypVJLQPfoJvxg8fsg3zYrOvMYnfsqOCYoHtZGK0O7jyfFmcGo5v2hLT-CpoF3Zw - local JWT_ES256_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.WFfJXGr5whKHB7arjsTXPTJ6TAsS1LoRxu7Vj2_HrLaIQphWJM6BICf-M3cv52tFzt-XTZb6GxlDgAbHo8z9Zg - local JWT_ES256_INVALID=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._aQmIBL4CVBxU1fNMOHp0kkagFaaX2TvAEenizytwd0 - local JWT_ES384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._EFxXYOTAfT3gB3xUfgGR2UyXHeRTlDWqA94oZbB0DDa7YPZTEX9T4C_0ylnOFKZ6irGHZA8vxjgXDH3DZKWwBWcZ-XaQ_Q4Ws2J-AEeLqcl7_CS6q9mFo0Y7vUNEn-W - local JWT_ES512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.AFY4gNCtZNYkrTiijDkV4eKIt2UPMIuJBfZIk69jgI8FSGCQyUIMmIVg0fTvbaSiaryXzcjbG5TCm8a9Vu3KFJutAHGrgvZqcdklxx6Fbk3an3r_CH68n_ncwS3SUV58mDjf0OX8jRuNdudU1L5xYNQdodo-fxPIb1oHXfMJ0CmULDR9 - - run_test -n 'when auth disabled, should return 200' \ - -p '/' \ - -c '200' - - run_test -n '[SSL] when auth disabled, should return 200' \ - -s \ - -p '/' \ - -c '200' - - run_test -n 'when auth enabled with default algorithm and no JWT in Authorization header, returns 302' \ - -p '/secure/auth-header/default' \ - -c '302' - - run_test -n '[SSL] when auth enabled with default algorithm and no JWT in Authorization header, returns 302' \ - -s \ - -p '/secure/auth-header/default' \ - -c '302' - - run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header missing Bearer, should return 200' \ - -p '/secure/auth-header/default/no-redirect' \ - -c '200' \ - -x "--header \"Authorization: ${JWT_HS256_VALID}\"" - - run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header with Bearer, should return 200' \ - -p '/secure/auth-header/default/no-redirect' \ - -c '200' \ - -x "--header \"Authorization: Bearer ${JWT_HS256_VALID}\"" - - run_test -n 'when auth enabled with Authorization header with Bearer, should keep header intact' \ - -p '/secure/auth-header/default/proxy-header' \ - -c '200' \ - -r "< Test-Authorization: Bearer ${JWT_HS256_VALID}" \ - -x "--header \"Authorization: Bearer ${JWT_HS256_VALID}\"" - - run_test -n 'when auth enabled with Authorization header with Bearer, lower-case "bearer" should be accepted' \ - -p '/secure/auth-header/default/proxy-header' \ - -c '200' \ - -r "< Test-Authorization: bearer ${JWT_HS256_VALID}" \ - -x "--header \"Authorization: bearer ${JWT_HS256_VALID}\"" - - run_test -n 'when auth enabled with default algorithm and no JWT cookie, returns 302' \ - -p '/secure/cookie/default' \ - -c '302' - - run_test -n 'when auth enabled with default algorithm with no redirect and no JWT cookie, should return 401' \ - -p '/secure/cookie/default/no-redirect' \ - -c '401' - - run_test -n 'when auth enabled with default algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/default' \ - -c '200' \ - -x "--cookie jwt=${JWT_HS256_VALID}" - - run_test -n 'when auth enabled with default algorithm and valid JWT cookie with no sub, returns 200' \ - -p '/secure/cookie/default' \ - -c '200' \ - -x ' --cookie "jwt=${JWT_HS256_MISSING_SUB}"' - - run_test -n 'when auth enabled with default algorithm and valid JWT cookie with no sub when sub validated, returns 302' \ - -p '/secure/cookie/default/validate-sub' \ - -c '302' \ - -x ' --cookie "jwt=${JWT_HS256_MISSING_SUB}"' - - run_test -n 'when auth enabled with default algorithm and valid JWT cookie with no email, returns 200' \ - -p '/secure/cookie/default' \ - -c '200' \ - -x ' --cookie "jwt=${JWT_HS256_MISSING_EMAIL}"' - - run_test -n 'when auth enabled with HS256 algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/hs256' \ - -c '200' \ - -x '--cookie "jwt=${JWT_HS256_VALID}"' - - run_test -n 'when auth enabled with HS384 algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/hs384' \ - -c '200' \ - -x '--cookie "jwt=${JWT_HS384_VALID}"' - - run_test -n 'when auth enabled with HS512 algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/hs512' \ - -c '200' \ - -x '--cookie "jwt=${JWT_HS512_VALID}"' - - run_test -n 'when auth enabled with RS256 algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/rs256' \ - -c '200' \ - -x ' --cookie "jwt=${JWT_RS256_VALID}"' - - run_test -n 'when auth enabled with ES256 algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/es256' \ - -c '200' \ - -x ' --cookie "jwt=${JWT_ES256_VALID}"' - - run_test -n 'when auth enabled with ES384 algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/es384' \ - -c '200' \ - -x ' --cookie "jwt=${JWT_ES384_VALID}"' - - run_test -n 'when auth enabled with ES512 algorithm and valid JWT cookie, returns 200' \ - -p '/secure/cookie/es512' \ - -c '200' \ - -x ' --cookie "jwt=${JWT_ES512_VALID}"' - - run_test -n 'when auth enabled with RS256 algorithm via file and valid JWT in Authorization header, returns 200' \ - -p '/secure/auth-header/rs256/file' \ - -c '200' \ - -x '--header "Authorization: Bearer ${JWT_RS256_VALID}"' - - run_test -n 'when auth enabled with RS256 algorithm via file and invalid JWT in Authorization header, returns 401' \ - -p '/secure/auth-header/rs256/file' \ - -c '302' \ - -x '--header "Authorization: Bearer ${JWT_RS256_INVALID}"' - - run_test -n 'when auth enabled with RS384 algorithm via file and valid JWT in Authorization header, returns 200' \ - -p '/secure/auth-header/rs384/file' \ - -c '200' \ - -x '--header "Authorization: Bearer ${JWT_RS256_VALID}"' - - run_test -n 'when auth enabled with RS512 algorithm via file and valid JWT in Authorization header, returns 200' \ - -p '/secure/auth-header/rs512/file' \ - -c '200' \ - -x '--header "Authorization: Bearer ${JWT_RS256_VALID}"' - - run_test -n 'when auth enabled with ES256 algorithm via file and valid JWT in Authorization header, returns 200' \ - -p '/secure/auth-header/es256/file' \ - -c '200' \ - -x '--header "Authorization: Bearer ${JWT_ES256_VALID}"' - - run_test -n 'when auth enabled with ES256 algorithm via file and invalid JWT in Authorization header, returns 401' \ - -p '/secure/auth-header/es256/file' \ - -c '302' \ - -x '--header "Authorization: Bearer ${JWT_ES256_INVALID}"' - - run_test -n 'when auth enabled with ES384 algorithm via file and valid JWT in Authorization header, returns 200' \ - -p '/secure/auth-header/es384/file' \ - -c '200' \ - -x '--header "Authorization: Bearer ${JWT_ES384_VALID}"' - - run_test -n 'when auth enabled with ES512 algorithm via file and valid JWT in Authorization header, returns 200' \ - -p '/secure/auth-header/es512/file' \ - -c '200' \ - -x '--header "Authorization: Bearer ${JWT_ES512_VALID}"' - - run_test -n 'when auth enabled with HS256 algorithm and valid JWT in custom header without bearer, returns 200' \ - -p '/secure/custom-header/hs256/' \ - -c '200' \ - -x '--header "Auth-Token: ${JWT_HS256_VALID}"' - - run_test -n 'when auth enabled with HS256 algorithm and valid JWT in custom header with bearer, returns 200' \ - -p '/secure/custom-header/hs256/' \ - -c '200' \ - -x '--header "Auth-Token: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts single claim to request variable' \ - -p '/secure/extract-claim/request/sub' \ - -r '< Test: sub=some-long-uuid' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts multiple claims (single directive) to request variable' \ - -p '/secure/extract-claim/request/name-1' \ - -r '< Test: firstName=hello; lastName=world' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts multiple claims (multiple directives) to request variable' \ - -p '/secure/extract-claim/request/name-2' \ - -r '< Test: firstName=hello; lastName=world' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts nested claim to request variable' \ - -p '/secure/extract-claim/request/nested' \ - -r '< Test: username=hello\.world' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts single claim to response variable' \ - -p '/secure/extract-claim/response/sub' \ - -r '< Test: sub=some-long-uuid' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts multiple claims (single directive) to response variable' \ - -p '/secure/extract-claim/response/name-1' \ - -r '< Test: firstName=hello; lastName=world' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts multiple claims (multiple directives) to response variable' \ - -p '/secure/extract-claim/response/name-2' \ - -r '< Test: firstName=hello; lastName=world' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts nested claim to response variable' \ - -p '/secure/extract-claim/response/nested' \ - -r '< Test: username=hello.world' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts single claim to response header' \ - -p '/secure/extract-claim/response/sub' \ - -r '< JWT-sub: some-long-uuid' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts multiple claims (single directive) to response header' \ - -p '/secure/extract-claim/response/name-1' \ - -r '< JWT-firstName: hello' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts multiple claims (multiple directives) to response header' \ - -p '/secure/extract-claim/response/name-2' \ - -r '< JWT-firstName: hello' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts nested claim to response header' \ - -p '/secure/extract-claim/response/nested' \ - -r '< JWT-username: hello\.world' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'tests single claim with if statement' \ - -p '/secure/extract-claim/if/sub' \ - -c 200 \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'tests absence of single claim with if statement' \ - -p '/secure/extract-claim/if/sub' \ - -c 401 \ - -x '--header "Authorization: Bearer ${JWT_HS256_MISSING_SUB}"' - - run_test -n 'extracts single claim to response body' \ - -p '/secure/extract-claim/body/sub' \ - -c 200 \ - -r 'sub: some-long-uuid$' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'extracts multiple claims to response body' \ - -p '/secure/extract-claim/body/multiple' \ - -c 200 \ - -r 'you are: hello world$' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'redirect based on claim' \ - -p '/profile/me' \ - -c 301 \ - -r '< Location: http://nginx:8000/profile/some-long-uuid' \ - -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' - - run_test -n 'returns 302 if auth enabled and no JWT provided' \ - -p '/return-url' \ - -c '302' - - run_test -n 'redirects to login if auth enabled and no JWT provided' \ - -p '/return-url' \ - -r '< Location: https://example\.com/login.*' - - run_test -n 'adds return_url to login URL when redirected to login' \ - -p '/return-url' \ - -r '< Location: https://example\.com/login\?return_url=http://nginx.*' - - run_test -n 'return_url includes port when redirected to login' \ - -p '/return-url' \ - -r "< Location: https://example\.com/login\?return_url=http://nginx:${PORT}/return-url" - - run_test -n 'return_url includes query when redirected to login' \ - -p '/return-url?test=123' \ - -r '< Location: https://example\.com/login\?return_url=http://nginx.*/return-url%3Ftest=123' - - if [[ "${NUM_FAILED}" = '0' ]]; then - printf "\nRan ${NUM_TESTS} tests successfully (skipped ${NUM_SKIPPED}).\n" - return 0 - else - printf "\nRan ${NUM_TESTS} tests: ${GREEN}$((${NUM_TESTS} - ${NUM_FAILED})) passed${NC}; ${RED}${NUM_FAILED} failed${NC}; ${NUM_SKIPPED} skipped\n" - return 1 - fi -} - -if [ "${DEBUG}" != '' ]; then - printf "\n${RED}Some tests will be skipped since DEBUG is set.${NC}\n" -fi - -printf "\n${GRAY}Starting tests using port ${PORT}...${NC}\n" -main From c415b81e28b5fdacddc5716fb7e21728a4c40096 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Wed, 5 Feb 2025 16:07:30 -0500 Subject: [PATCH 22/29] fix incorrect function name --- .bin/git/hooks/pre-push-build-and-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bin/git/hooks/pre-push-build-and-test b/.bin/git/hooks/pre-push-build-and-test index 9cf4682..876ca32 100755 --- a/.bin/git/hooks/pre-push-build-and-test +++ b/.bin/git/hooks/pre-push-build-and-test @@ -4,7 +4,7 @@ REPO_ROOT_DIR=$(git rev-parse --show-toplevel) CHANGE_COUNT=$(cd ${REPO_ROOT_DIR}; git diff --name-only origin/HEAD..HEAD -- resources/ src/ test/ Dockerfile scripts.sh |wc -l) if [[ "0" -ne "${CHANGE_COUNT}" ]]; then - (cd ${REPO_ROOT_DIR}; ./scripts.sh rebuild_nginx rebuild_test_runner test) + (cd ${REPO_ROOT_DIR}; ./scripts.sh rebuild_nginx rebuild_test test) else HOOK_NAME=$(basename $0) From a1662419010034a8230892489b083365bed4232c Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 18 Feb 2025 11:15:25 -0500 Subject: [PATCH 23/29] restore accidentally-deleted test dir :-| --- test/docker-compose-test.yml | 19 ++ test/ec_key_256.pem | 5 + test/ec_key_384.pem | 6 + test/ec_key_521.pem | 8 + test/etc/nginx/conf.d/test.conf | 448 ++++++++++++++++++++++++++++ test/etc/nginx/ec_key_256-pub.pem | 4 + test/etc/nginx/ec_key_384-pub.pem | 5 + test/etc/nginx/ec_key_521-pub.pem | 6 + test/etc/nginx/rsa_key_2048-pub.pem | 9 + test/etc/nginx/test.crt | 23 ++ test/etc/nginx/test.key | 28 ++ test/rsa_key_2048.pem | 28 ++ test/test-nginx.dockerfile | 19 ++ test/test-runner.dockerfile | 18 ++ test/test.sh | 387 ++++++++++++++++++++++++ 15 files changed, 1013 insertions(+) create mode 100644 test/docker-compose-test.yml create mode 100644 test/ec_key_256.pem create mode 100644 test/ec_key_384.pem create mode 100644 test/ec_key_521.pem create mode 100644 test/etc/nginx/conf.d/test.conf create mode 100644 test/etc/nginx/ec_key_256-pub.pem create mode 100644 test/etc/nginx/ec_key_384-pub.pem create mode 100644 test/etc/nginx/ec_key_521-pub.pem create mode 100755 test/etc/nginx/rsa_key_2048-pub.pem create mode 100644 test/etc/nginx/test.crt create mode 100644 test/etc/nginx/test.key create mode 100755 test/rsa_key_2048.pem create mode 100644 test/test-nginx.dockerfile create mode 100644 test/test-runner.dockerfile create mode 100755 test/test.sh diff --git a/test/docker-compose-test.yml b/test/docker-compose-test.yml new file mode 100644 index 0000000..72ff710 --- /dev/null +++ b/test/docker-compose-test.yml @@ -0,0 +1,19 @@ +services: + + nginx: + container_name: ${TEST_CONTAINER_NAME_PREFIX:?required}-nginx + build: + context: . + dockerfile: test-nginx.dockerfile + args: + BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION:?required} + logging: + driver: ${LOG_DRIVER:-journald} + + runner: + container_name: ${TEST_CONTAINER_NAME_PREFIX:?required}-runner + build: + context: . + dockerfile: test-runner.dockerfile + depends_on: + - nginx \ No newline at end of file diff --git a/test/ec_key_256.pem b/test/ec_key_256.pem new file mode 100644 index 0000000..4206969 --- /dev/null +++ b/test/ec_key_256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgOlEBGcZxxhv8FkN0 +YIvax6fnhJbMeotzIEBxIglkNu6hRANCAATP1NpDzvZmKd2Mw6hIrv4nzUfNu7OK +mT5VuL5LhvUgzTqVGuxwevA7DlFsNVSfCljIBG3geio3fcd4k0Z9SygL +-----END PRIVATE KEY----- diff --git a/test/ec_key_384.pem b/test/ec_key_384.pem new file mode 100644 index 0000000..2aa5780 --- /dev/null +++ b/test/ec_key_384.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDADyrL6llSQoQOZ/PF/ +l761kAbrTwn4vu30Kr34ScW6bRKVXLq3cT3QssJ1nF9B63qhZANiAAQ48dOfIEd3 +0TCVE0JT4ZU0Db7Ftz+ex7lojP7uqTY9OI59yoMB01zUN4JK30BRXS9Yv0A9Bu1z +fgLu93FSn0kd0zIPMvuu5LUt60M/miSt2lA0OrqFhKjx6FFdN/lNh64= +-----END PRIVATE KEY----- diff --git a/test/ec_key_521.pem b/test/ec_key_521.pem new file mode 100644 index 0000000..10471dc --- /dev/null +++ b/test/ec_key_521.pem @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAKkag6aVn4XAbaALo +0b3pypdP5RBX7uKxHmKlkNCcpA0oVTdgjnM5NpJP8ZOM6NjVhEzsn6c/Tdn8hL8w +SI55hFWhgYkDgYYABABpTipSvbs8fq44u4fA+v7DTNYViA58sqbrxjxdzwWZ8eEj +CXsH7yzSGx3Y19NSyrX8HbjWmrj5uxiKeFCB8mGzTwDcFIKCMeMkHjZs/fmVOumR +a2XSpj7BP6wqcN6Pf+UqECivGAZGRHoabo/dm5zF9M3gO+G9eOrf3G1wgFFM7Vzb +Ow== +-----END PRIVATE KEY----- diff --git a/test/etc/nginx/conf.d/test.conf b/test/etc/nginx/conf.d/test.conf new file mode 100644 index 0000000..4e5d764 --- /dev/null +++ b/test/etc/nginx/conf.d/test.conf @@ -0,0 +1,448 @@ +error_log /var/log/nginx/debug.log debug; +access_log /var/log/nginx/access.log; + +server { + listen %{PORT}; + listen %{SSL_PORT} ssl; + server_name localhost; + + ssl_certificate /etc/nginx/test.crt; + ssl_certificate_key /etc/nginx/test.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + auth_jwt_key "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF"; + auth_jwt_loginurl "https://example.com/login"; + auth_jwt_enabled off; + + location / { + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/default { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/default/validate-sub { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_validate_sub on; + auth_jwt_location COOKIE=jwt; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/default/no-redirect { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location COOKIE=jwt; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/hs256 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm HS256; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/hs384 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm HS384; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/hs512 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm HS512; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/es256 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm ES256; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz +ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/es384 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm ES384; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 +aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 +LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/cookie/es512 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location COOKIE=jwt; + auth_jwt_algorithm ES512; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO +fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC +gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh +vXjq39xtcIBRTO1c2zs= +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/default { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/default/no-redirect { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/default/proxy-header { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + + add_header "Test-Authorization" "$http_authorization"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/rs256 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh +uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+ +iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM +ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g +6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe +K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t +BwIDAQAB +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es256 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz +ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es384 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 +aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 +LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es512 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_key "-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO +fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC +gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh +vXjq39xtcIBRTO1c2zs= +-----END PUBLIC KEY-----"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/rs256/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm RS256; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/rs384/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm RS384; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/rs512/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm RS512; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/rsa_key_2048-pub.pem"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es256/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm ES256; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/ec_key_256-pub.pem"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es384/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm ES384; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/ec_key_384-pub.pem"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/auth-header/es512/file { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Authorization; + auth_jwt_algorithm ES512; + auth_jwt_use_keyfile on; + auth_jwt_keyfile_path "/etc/nginx/ec_key_521-pub.pem"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/custom-header/hs256 { + auth_jwt_enabled on; + auth_jwt_redirect on; + auth_jwt_location HEADER=Auth-Token; + auth_jwt_algorithm HS256; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/extract-claim/request/sub { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_request_claims sub; + + add_header "Test" "sub=$http_jwt_sub"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/extract-claim/request/name-1 { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_request_claims firstName lastName; + + add_header "Test" "firstName=$http_jwt_firstname; lastName=$http_jwt_lastname"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/extract-claim/request/name-2 { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_request_claims firstName; + auth_jwt_extract_request_claims lastName; + + add_header "Test" "firstName=$http_jwt_firstname; lastName=$http_jwt_lastname"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/extract-claim/request/nested { + location /secure/extract-claim/request/nested { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_request_claims username; + + add_header "Test" "username=$http_jwt_username"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + } + + location /secure/extract-claim/response/sub { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_response_claims sub; + + add_header "Test" "sub=$sent_http_jwt_sub"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/extract-claim/response/name-1 { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_response_claims firstName lastName; + + add_header "Test" "firstName=$sent_http_jwt_firstname; lastName=$sent_http_jwt_lastname"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/extract-claim/response/name-2 { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_response_claims firstName; + auth_jwt_extract_response_claims lastName; + + add_header "Test" "firstName=$sent_http_jwt_firstname; lastName=$sent_http_jwt_lastname"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + + location /secure/extract-claim/response/nested { + location /secure/extract-claim/response/nested { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_response_claims username; + + add_header "Test" "username=$sent_http_jwt_username"; + + alias /usr/share/nginx/html/; + try_files index.html =404; + } + } + + location /secure/extract-claim/if/sub { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_var_claims sub; + + if ($jwt_claim_sub = 'some-long-uuid') { + return 200; + } + + return 401; + } + + location /secure/extract-claim/body/sub { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_extract_var_claims sub; + + return 200 "sub: $jwt_claim_sub"; + } + + location /secure/extract-claim/body/multiple { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_validate_sub on; + auth_jwt_extract_var_claims firstName middleName lastName; + + return 200 "you are: $jwt_claim_firstName $jwt_claim_middleName $jwt_claim_lastName"; + } + + location /profile { + auth_jwt_enabled on; + auth_jwt_redirect off; + auth_jwt_location HEADER=Authorization; + auth_jwt_validate_sub on; + + location /profile/me { + auth_jwt_extract_var_claims sub; + + return 301 /profile/$jwt_claim_sub; + } + } + + location /return-url { + auth_jwt_enabled on; + auth_jwt_redirect on; + } +} diff --git a/test/etc/nginx/ec_key_256-pub.pem b/test/etc/nginx/ec_key_256-pub.pem new file mode 100644 index 0000000..3306ea0 --- /dev/null +++ b/test/etc/nginx/ec_key_256-pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9TaQ872ZindjMOoSK7+J81Hzbuz +ipk+Vbi+S4b1IM06lRrscHrwOw5RbDVUnwpYyARt4HoqN33HeJNGfUsoCw== +-----END PUBLIC KEY----- diff --git a/test/etc/nginx/ec_key_384-pub.pem b/test/etc/nginx/ec_key_384-pub.pem new file mode 100644 index 0000000..e642ed1 --- /dev/null +++ b/test/etc/nginx/ec_key_384-pub.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOPHTnyBHd9EwlRNCU+GVNA2+xbc/nse5 +aIz+7qk2PTiOfcqDAdNc1DeCSt9AUV0vWL9APQbtc34C7vdxUp9JHdMyDzL7ruS1 +LetDP5okrdpQNDq6hYSo8ehRXTf5TYeu +-----END PUBLIC KEY----- diff --git a/test/etc/nginx/ec_key_521-pub.pem b/test/etc/nginx/ec_key_521-pub.pem new file mode 100644 index 0000000..0cb875c --- /dev/null +++ b/test/etc/nginx/ec_key_521-pub.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAaU4qUr27PH6uOLuHwPr+w0zWFYgO +fLKm68Y8Xc8FmfHhIwl7B+8s0hsd2NfTUsq1/B241pq4+bsYinhQgfJhs08A3BSC +gjHjJB42bP35lTrpkWtl0qY+wT+sKnDej3/lKhAorxgGRkR6Gm6P3ZucxfTN4Dvh +vXjq39xtcIBRTO1c2zs= +-----END PUBLIC KEY----- diff --git a/test/etc/nginx/rsa_key_2048-pub.pem b/test/etc/nginx/rsa_key_2048-pub.pem new file mode 100755 index 0000000..01f59bf --- /dev/null +++ b/test/etc/nginx/rsa_key_2048-pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh +uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+ +iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM +ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g +6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe +K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t +BwIDAQAB +-----END PUBLIC KEY----- diff --git a/test/etc/nginx/test.crt b/test/etc/nginx/test.crt new file mode 100644 index 0000000..fb406ba --- /dev/null +++ b/test/etc/nginx/test.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUMG9M4Itu0cOyX0+La+7huiIoX6YwDQYJKoZIhvcNAQEL +BQAwcTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRUwEwYDVQQHDAxG +YWxscyBDaHVyY2gxHzAdBgNVBAoMFlRlc2xhIEdvdmVybm1lbnQsIEluYy4xFzAV +BgNVBAsMDk5HSU5YIEF1dGggSldUMB4XDTI0MDMxNTE4MTM1MloXDTM0MDMxMzE4 +MTM1MlowcTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRUwEwYDVQQH +DAxGYWxscyBDaHVyY2gxHzAdBgNVBAoMFlRlc2xhIEdvdmVybm1lbnQsIEluYy4x +FzAVBgNVBAsMDk5HSU5YIEF1dGggSldUMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAih41Ct5XgcSTz7ZVAjBb0t0z9Qae08aseoMEKJf7AmNqKtsvzeAw +/DJxOWJR5VPtUWhFAmXxPfG2B6aiSIVJVpG9yzcdQlCvyJG7Ub4QCm5GXwpU+zDC +qmD5ksz9QMdOzvRLypAU1ciZiCXjwpUnW+BZyZ9Tpmsxm6/gOzkd3rxoIbc9uXxp +5o4n6k02EPSzLzUhkZnhLQrOAGUB7+q11FAU5eNMlTWC9gQUsbNaTVtKmM2eV9BA +UHdX2GbkfFbN22l3Wey4oyNZWmye1ZFOPyBR+tyU3pofhb+R+hTFmeNBzrJq3i30 +Qi0B8AnulKdOjnTysPYjDTrN6xcVDWNmPQIDAQABo1MwUTAdBgNVHQ4EFgQUczdy +7s64NJHNGsQTf/zwFnQe6LMwHwYDVR0jBBgwFoAUczdy7s64NJHNGsQTf/zwFnQe +6LMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfcxCiz6ShHof +lXiE2j+s556SM2n8oW/S1BSjFC2wF1uKVeMJA1gAaWObC3ElqffFlqTdCorhgRS/ +knWa+Sqe/jWBSgwLG/e5DvxXWjD7b7kZdAZNy9evs5nhVfcLT+GyvB/z5GdAFY7s +xYmLrC07ubhHIL9h7lhNKbRr++o+BcClQBZKRO4fxBwXxqx/rHudjH87Wr61Ov52 +90xNjwcqvevY0skmPao5+oyxkURdKZualNxiOGMPpywkpJkfl8Az5xKAJhUMAtFR +smhQduejEkcxfxtsiYgVoulI29GAsMr9zHps9zb5k0+SWIiSixjQ0CpRhLcNYu4F +QPgLQLGwUQ== +-----END CERTIFICATE----- diff --git a/test/etc/nginx/test.key b/test/etc/nginx/test.key new file mode 100644 index 0000000..13ec754 --- /dev/null +++ b/test/etc/nginx/test.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCKHjUK3leBxJPP +tlUCMFvS3TP1Bp7Txqx6gwQol/sCY2oq2y/N4DD8MnE5YlHlU+1RaEUCZfE98bYH +pqJIhUlWkb3LNx1CUK/IkbtRvhAKbkZfClT7MMKqYPmSzP1Ax07O9EvKkBTVyJmI +JePClSdb4FnJn1OmazGbr+A7OR3evGghtz25fGnmjifqTTYQ9LMvNSGRmeEtCs4A +ZQHv6rXUUBTl40yVNYL2BBSxs1pNW0qYzZ5X0EBQd1fYZuR8Vs3baXdZ7LijI1la +bJ7VkU4/IFH63JTemh+Fv5H6FMWZ40HOsmreLfRCLQHwCe6Up06OdPKw9iMNOs3r +FxUNY2Y9AgMBAAECggEAAkwEggGp/xb67FCyDJ8rdimTZFPi9U7coUCN8HNI/qrf +lTnfvox0oOUUqMMmIIQeS/HJ4ANvZe8GO3QkE8R5Sg7F0yjZL2tyTCNPgOMCMK8E +mmHS58brHdrbm658C1ILnfmssjNmNueNbuW00Koa8imCsY2ZEW+L7vTKuMFqg6c+ +BDJxC4yoCPwSTVfcajjzI6FVfphE0pd8Ho/sE8vTqdmovh23+vgfNUq1L9Smvf7R +YLM+hS1ouRP2BI5AN0sm04Kxd8MKPzuwCxteoZ9Y9YHyr1JeWGTTL0T24+LwUee/ +24zXZFrzpTgmtDYeEuVWsF5bP/fMS4Fctda3pdJMsQKBgQDCANjGDwwfSCCev2kl +WdrFJywhn5hWLWFwlo/FwLOsFJtejaBwIDRQCMPZ74H+KMHwUnO3vTanKJWqDRP9 +CdMh94C1BqobRV6rN4HgA4Opxim1EyRWHV6ui41zokk2mJrwUzKkR8t9lt9EZKrk +ZPyKER9A4hBqBmYvaYxodN8U1QKBgQC2QXUQq9j7niT7t4xMi0e9vnPLs0z1yUK9 +0nzKwTHDPflk3o2sKvH7199qVkc15JQ9DQ7NuYD7ezLbE3DJuVzpNDAfNXmfWHmp +7ukdnxyn6ZCmzQY7/fTpJTEGKVQMVCgf2f5ANgxm5EmN0yWRMcEt1VXIwCisY56p +o6nwv/1fyQKBgQCJBnIVyjEEszwfBBEvCX0kvVtFUGUXkSv+isl3onkFNPTcXuoP +6B8q3FYAy1MkggMhTAthnqpIfLjhCCWzFspidl8Y/WEOq/uGsUjxQWowcr+onqGO +lWX3oKfDIb/WaQkeb5UYRYFr7jE6LGQrt0xL9HX/rOxtBqIMIN/EM7ARFQKBgDAJ +zMtaIFUh9+mJFafPRleS7X6RggV+yOKzqkTe6zjlCuk1Z+4rW6Df43lpyFdCKnh1 +CqPa805VyK/Jzf69pumo4c44EBiZ/2d1G2i9WZZAj+oHPE9vvq/9J5DSL98YB4Nt +uABAvsAYB/Mj5lEA5kQoaPYDADWABH/+LXrRf/1RAoGAUvxPvmpkGMC+KdmjLam7 +CPC3+y4MZOyZ11BhOxLhd1K2qcQd9K7tkjUhNxRn5GVzpzOKeFJFtiih2uN+PBNJ +oylPR03uk/7D52b1OYaJhs9bQkth//Qk935nyRM26C2vG4tQLfT/cFi5F53n0ZCQ +7e8O6+QY0lZnpvsfnt8YIsM= +-----END PRIVATE KEY----- diff --git a/test/rsa_key_2048.pem b/test/rsa_key_2048.pem new file mode 100755 index 0000000..0f58120 --- /dev/null +++ b/test/rsa_key_2048.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC2kwAziXUf33m +iqWp0yG6o259+nj7hpQLC4UT0Hmz0wmvreDJ/yNbSgOvsxvVdvzL2IaRZ+Gi5mo0 +lswWvL6IGz7PZO0kXTq9sdBnNqMOx27HddV9e/2/p0MgibJTbgywY2Sk23QYhJpq +Kq/nU0xlBfSaI5ddZ2RC9ZNkVeGawUKYksTruhAVJqviHN8BoK6VowP5vcxyyOWH +TK9KruDqzCIhqwRTeo0spokBkTN/LCuhVivcHAzUiJVtB4qAiTI9L/zkzhjpKz9P +45aLU54rj011gG8U/6E1USh5nMnPkr+d3oLfkhfS3Zs3kJVdyFQWZpQxiTaI92Fd +2wLvbS0HAgMBAAECggEAD8dTnkETSSjlzhRuI9loAtAXM3Zj86JLPLW7GgaoxEoT +n7lJ2bGicFMHB2ROnbOb9vnas82gtOtJsGaBslmoaCckp/C5T1eJWTEb+i+vdpPp +wZcmKZovyyRFSE4+NYlU17fEv6DRvuaGBpDcW7QgHJIl45F8QWEM+msee2KE+V4G +z/9vAQ+sOlvsb4mJP1tJIBx9Lb5loVREwCRy2Ha9tnWdDNar8EYkOn8si4snPT+E +3ZCy8mlcZyUkZeiS/HdtydxZfoiwrSRYamd1diQpPhWCeRteQ802a7ds0Y2YzgfF +UaYjNuRQm7zA//hwbXS7ELPyNMU15N00bajlG0tUOQKBgQDnLy01l20OneW6A2cI +DIDyYhy5O7uulsaEtJReUlcjEDMkin8b767q2VZHb//3ZH+ipnRYByUUyYUhdOs2 +DYRGGeAebnH8wpTT4FCYxUsIUpDfB7RwfdBONgaKewTJz/FPswy1Ye0b5H2c6vVi +m2FZ33HQcoZ3wvFFqyGVnMzpOwKBgQDXxL95yoxUGKa8vMzcE3Cn01szh0dFq0sq +cFpM+HWLVr84CItuG9H6L0KaStEEIOiJsxOVpcXfFFhsJvOGhMA4DQTwH4WuXmXp +1PoVMDlV65PYqvhzwL4+QhvZO2bsrEunITXOmU7CI6kilnAN3LuP4HbqZgoX9lqP +I31VYzLupQKBgGEYck9w0s/xxxtR9ILv5XRnepLdoJzaHHR991aKFKjYU/KD7JDK +INfoAhGs23+HCQhCCtkx3wQVA0Ii/erM0II0ueluD5fODX3TV2ZibnoHW2sgrEsW +vFcs36BnvIIaQMptc+f2QgSV+Z/fGsKYadG6Q+39O7au/HB7SHayzWkjAoGBAMgt +Fzslp9TpXd9iBWjzfCOnGUiP65Z+GWkQ/SXFqD+SRir0+m43zzGdoNvGJ23+Hd6K +TdQbDJ0uoe4MoQeepzoZEgi4JeykVUZ/uVfo+nh06yArVf8FxTm7WVzLGGzgV/uA ++wtl/cRtEyAsk1649yW/KHPEIP8kJdYAJeoO8xSlAoGAERMrkFR7KGYZG1eFNRdV +mJMq+Ibxyw8ks/CbiI+n3yUyk1U8962ol2Q0T4qjBmb26L5rrhNQhneM4e8mo9FX +LlQapYkPvkdrqW0Bp72A/UNAvcGTmN7z5OCJGMUutx2hmEAlrYmpLKS8pM/p9zpK +tEOtzsP5GMDYVlEp1jYSjzQ= +-----END PRIVATE KEY----- diff --git a/test/test-nginx.dockerfile b/test/test-nginx.dockerfile new file mode 100644 index 0000000..1065558 --- /dev/null +++ b/test/test-nginx.dockerfile @@ -0,0 +1,19 @@ +ARG BASE_IMAGE + +FROM ${BASE_IMAGE:?required} AS NGINX +ARG PORT +ARG SSL_PORT + +COPY etc/ /etc/ + +COPY <<` /usr/share/nginx/html/index.html + + Test + +

NGINX Auth-JWT Module Test

+ + +` + +RUN sed -i "s|%{PORT}|${PORT:?required}|" /etc/nginx/conf.d/test.conf +RUN sed -i "s|%{SSL_PORT}|${SSL_PORT:?required}|" /etc/nginx/conf.d/test.conf diff --git a/test/test-runner.dockerfile b/test/test-runner.dockerfile new file mode 100644 index 0000000..18fc3d3 --- /dev/null +++ b/test/test-runner.dockerfile @@ -0,0 +1,18 @@ +ARG RUNNER_BASE_IMAGE + +FROM ${RUNNER_BASE_IMAGE:?required} +ARG PORT +ARG SSL_PORT + +ENV PORT=${PORT:?required} +ENV SSL_PORT=${SSL_PORT:?required} + +RUN <<` + set -e + apt-get update + apt-get install -y curl bash +` + +COPY test.sh . + +CMD ./test.sh diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 0000000..c726a75 --- /dev/null +++ b/test/test.sh @@ -0,0 +1,387 @@ +#!/bin/bash -eu + +# set a test # here to execute only that test and output additional info +DEBUG= + +RED='\e[31m' +GREEN='\e[32m' +GRAY='\e[90m' +NC='\e[00m' + +NUM_TESTS=0; +NUM_SKIPPED=0; +NUM_FAILED=0; + +run_test () { + NUM_TESTS=$((${NUM_TESTS} + 1)); + + if [ "${DEBUG}" == '' ] || [ ${DEBUG} == ${NUM_TESTS} ]; then + local OPTIND; + local name= + local path= + local expectedCode= + local expectedResponseRegex= + local extraCurlOpts= + local scheme='http' + local port=${PORT} + local curlCommand= + local exitCode= + local response= + local testNum="${GRAY}${NUM_TESTS}${NC}\t" + + while getopts "n:asp:r:c:x:" option; do + case $option in + n) + name=$OPTARG;; + s) + scheme='https' + port=${SSL_PORT};; + p) + path=$OPTARG;; + c) + expectedCode=$OPTARG;; + r) + expectedResponseRegex=$OPTARG;; + x) + extraCurlOpts=$OPTARG;; + \?) # Invalid option + printf "Error: Invalid option\n"; + exit;; + esac + done + + curlCommand="curl -skv ${scheme}://nginx:${port}${path} -H 'Cache-Control: no-cache' ${extraCurlOpts} 2>&1" + response=$(eval "${curlCommand}") + exitCode=$? + + printf "\n${testNum}" + + if [ "${exitCode}" -ne "0" ]; then + printf "${RED}${name} -- unexpected exit code from cURL\n\tcURL Exit Code: ${exitCode}"; + NUM_FAILED=$((${NUM_FAILED} + 1)); + else + local okay=1 + + if [ "${expectedCode}" != "" ]; then + local responseCode=$(echo "${response}" | grep -Eo 'HTTP/1.1 ([0-9]{3})' | awk '{print $2}') + + if [ "${expectedCode}" != "${responseCode}" ]; then + printf "${RED}${name} -- unexpected status code\n\tExpected: ${expectedCode}\n\tActual: ${responseCode}\n\tPath: ${path}" + NUM_FAILED=$((${NUM_FAILED} + 1)) + okay=0 + fi + fi + + if [ "${okay}" == '1' ] && [ "${expectedResponseRegex}" != "" ] && ! [[ "${response}" =~ ${expectedResponseRegex} ]]; then + printf "${RED}${name} -- regex not found in response\n\tPath: ${path}\n\tRegEx: ${expectedResponseRegex//%/%%}" + NUM_FAILED=$((${NUM_FAILED} + 1)) + okay=0 + fi + + if [ "${okay}" == '1' ]; then + printf "${GREEN}${name}"; + fi + fi + + if [ "${DEBUG}" == "${NUM_TESTS}" ]; then + printf '\n\tcURL Command: %s' "${curlCommand:---}" + printf '\n\tResponse: %s' "${response:---}" + fi + + printf "${NC}\n" + else + NUM_SKIPPED=$((${NUM_SKIPPED} + 1)) + fi +} + +main() { + local JWT_HS256_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.r8tG8IZheiQ-i6HqUYyJj9V6dipgcQ4ZIdxau6QCZDo + local JWT_HS256_MISSING_SUB=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaXJzdE5hbWUiOiJoZWxsbyIsImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwicm9sZXMiOlsidGhpcyIsInRoYXQiLCJ0aGVvdGhlciJdLCJpc3MiOiJpc3N1ZXIiLCJwZXJzb25JZCI6Ijc1YmIzY2M3LWI5MzMtNDRmMC05M2M2LTE0N2IwODJmYWRiNSIsImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.lD6jUsazVtzeGhRTNeP_b2Zs6O798V2FQql11QOEI1Q + local JWT_HS256_MISSING_EMAIL=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.tJoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM + local JWT_HS384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.SS57j7PEybjbsp3g5W-IhhJHBmG5K-97qvgBKL16xj9ey-uMeEenWjGbB2vVp0kq + local JWT_HS512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.xtSU6EWN2LILVsYzJFJpKnRkqjn_3qjz-J2ttNKnhZ60_5YjFeC8io4k8k1u77zlohSWvWMdugD9ZaB3vjJo-w + local JWT_RS256_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwgImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwgInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwgImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwgImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.cn5Gb75XL-r7TMsPuqzWoKZ06ZsyF_VZIG0Ohn8uZZFeF8dFUhSrEOYe8WFN6Eon8a8LC0OCI9eNdGiD4m_e9TD1Iz2juqaeos-6yd7SWuODr4YS8KD3cqfXndnLRPzp9PC_UIpATsbqOmxGDrRKvHsQq0TuIXImU3rM_m3kFJFgtoJFHx3KmZUo_Ozkyhhc6Pukikhy6odNAtEyLHP5_tabMXtkeAuIlG8dhjAxef4mJLexYFclG-vl7No5VBU4JrMbfgyxtobcYoE-bDIpmQHywrwo6Li7X0hgHJ17sfS3G2YMHmE-Ij_W2Lf9kf5r2r12DUvg44SLIfM58pCINQ + local JWT_RS256_INVALID=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._aQmIBL4CVBxU1fNMOHp0kkagFaaX2TvAEenizytwd0 + local JWT_RS384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.H35bTcZRhepWIoa8pKCbUMRuAOkVX9K5hJjc6tPmQwWmTw8lrktsvmMzJg_rgqnJLnAkciSIQw5EDj7fngS5zX2ThyRxrkPuE2Uiyw2Ect-mo9Kg1lrWgnyZCuCgq-Up9HQRAv0160mePlm8Gs4TOY6CPr38zwTcDZsy_Keq93igDQV8WuuWAGICaGd5ZyUOPjjzGShRjTU8Szz7fnpZpTtYRCYVo0pc5yfRWYm0fdn-4AseyGvd8JJ2xfnAEe4kZOkz7X1MLKtL0slKg3m2PH1lD7HwxIawXRTPWxArhJ9dcTNiDUrqtde2juGwOuMD_zTsb2Jj0_rmRb0Q6aljNw + local JWT_RS512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.iUupyKypfXJ5aZWfItSW-mOmx9a4C4X7Yr5p5Fk8W75ZhkOq0EeNfstTxx870brhkdPovBhO2LYI44_HoH9XicQNL6JnFprE0r61eJFngbuzlhRQiWpq0xYrazJWc9zB7_GgL2ZCwtw-Ts3G23Q0632wVm6-d7MKvG7RS8aEjN-MuVGdtLglH3forpItmFxw-if40EQsBL7hncN_XNcQTO4KPHkqmlpac_oKXRrLFDIIt2tB6OOpvY4QcpERoxexp4pi2f-JoINnWX_dU5JnIs3ypVJLQPfoJvxg8fsg3zYrOvMYnfsqOCYoHtZGK0O7jyfFmcGo5v2hLT-CpoF3Zw + local JWT_ES256_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.WFfJXGr5whKHB7arjsTXPTJ6TAsS1LoRxu7Vj2_HrLaIQphWJM6BICf-M3cv52tFzt-XTZb6GxlDgAbHo8z9Zg + local JWT_ES256_INVALID=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._aQmIBL4CVBxU1fNMOHp0kkagFaaX2TvAEenizytwd0 + local JWT_ES384_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ._EFxXYOTAfT3gB3xUfgGR2UyXHeRTlDWqA94oZbB0DDa7YPZTEX9T4C_0ylnOFKZ6irGHZA8vxjgXDH3DZKWwBWcZ-XaQ_Q4Ws2J-AEeLqcl7_CS6q9mFo0Y7vUNEn-W + local JWT_ES512_VALID=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsImVtYWlsQWRkcmVzcyI6ImhlbGxvd29ybGRAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ0aGlzIiwidGhhdCIsInRoZW90aGVyIl0sImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwiZXhwIjoxOTA4ODM1MjAwLCJpYXQiOjE0ODg4MTk2MDAsInVzZXJuYW1lIjoiaGVsbG8ud29ybGQifQ.AFY4gNCtZNYkrTiijDkV4eKIt2UPMIuJBfZIk69jgI8FSGCQyUIMmIVg0fTvbaSiaryXzcjbG5TCm8a9Vu3KFJutAHGrgvZqcdklxx6Fbk3an3r_CH68n_ncwS3SUV58mDjf0OX8jRuNdudU1L5xYNQdodo-fxPIb1oHXfMJ0CmULDR9 + + run_test -n 'when auth disabled, should return 200' \ + -p '/' \ + -c '200' + + run_test -n '[SSL] when auth disabled, should return 200' \ + -s \ + -p '/' \ + -c '200' + + run_test -n 'when auth enabled with default algorithm and no JWT in Authorization header, returns 302' \ + -p '/secure/auth-header/default' \ + -c '302' + + run_test -n '[SSL] when auth enabled with default algorithm and no JWT in Authorization header, returns 302' \ + -s \ + -p '/secure/auth-header/default' \ + -c '302' + + run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header missing Bearer, should return 200' \ + -p '/secure/auth-header/default/no-redirect' \ + -c '200' \ + -x "--header \"Authorization: ${JWT_HS256_VALID}\"" + + run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header with Bearer, should return 200' \ + -p '/secure/auth-header/default/no-redirect' \ + -c '200' \ + -x "--header \"Authorization: Bearer ${JWT_HS256_VALID}\"" + + run_test -n 'when auth enabled with Authorization header with Bearer, should keep header intact' \ + -p '/secure/auth-header/default/proxy-header' \ + -c '200' \ + -r "< Test-Authorization: Bearer ${JWT_HS256_VALID}" \ + -x "--header \"Authorization: Bearer ${JWT_HS256_VALID}\"" + + run_test -n 'when auth enabled with Authorization header with Bearer, lower-case "bearer" should be accepted' \ + -p '/secure/auth-header/default/proxy-header' \ + -c '200' \ + -r "< Test-Authorization: bearer ${JWT_HS256_VALID}" \ + -x "--header \"Authorization: bearer ${JWT_HS256_VALID}\"" + + run_test -n 'when auth enabled with default algorithm and no JWT cookie, returns 302' \ + -p '/secure/cookie/default' \ + -c '302' + + run_test -n 'when auth enabled with default algorithm with no redirect and no JWT cookie, should return 401' \ + -p '/secure/cookie/default/no-redirect' \ + -c '401' + + run_test -n 'when auth enabled with default algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/default' \ + -c '200' \ + -x "--cookie jwt=${JWT_HS256_VALID}" + + run_test -n 'when auth enabled with default algorithm and valid JWT cookie with no sub, returns 200' \ + -p '/secure/cookie/default' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_HS256_MISSING_SUB}"' + + run_test -n 'when auth enabled with default algorithm and valid JWT cookie with no sub when sub validated, returns 302' \ + -p '/secure/cookie/default/validate-sub' \ + -c '302' \ + -x ' --cookie "jwt=${JWT_HS256_MISSING_SUB}"' + + run_test -n 'when auth enabled with default algorithm and valid JWT cookie with no email, returns 200' \ + -p '/secure/cookie/default' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_HS256_MISSING_EMAIL}"' + + run_test -n 'when auth enabled with HS256 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/hs256' \ + -c '200' \ + -x '--cookie "jwt=${JWT_HS256_VALID}"' + + run_test -n 'when auth enabled with HS384 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/hs384' \ + -c '200' \ + -x '--cookie "jwt=${JWT_HS384_VALID}"' + + run_test -n 'when auth enabled with HS512 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/hs512' \ + -c '200' \ + -x '--cookie "jwt=${JWT_HS512_VALID}"' + + run_test -n 'when auth enabled with RS256 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/rs256' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_RS256_VALID}"' + + run_test -n 'when auth enabled with ES256 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/es256' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_ES256_VALID}"' + + run_test -n 'when auth enabled with ES384 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/es384' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_ES384_VALID}"' + + run_test -n 'when auth enabled with ES512 algorithm and valid JWT cookie, returns 200' \ + -p '/secure/cookie/es512' \ + -c '200' \ + -x ' --cookie "jwt=${JWT_ES512_VALID}"' + + run_test -n 'when auth enabled with RS256 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/rs256/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_RS256_VALID}"' + + run_test -n 'when auth enabled with RS256 algorithm via file and invalid JWT in Authorization header, returns 401' \ + -p '/secure/auth-header/rs256/file' \ + -c '302' \ + -x '--header "Authorization: Bearer ${JWT_RS256_INVALID}"' + + run_test -n 'when auth enabled with RS384 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/rs384/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_RS256_VALID}"' + + run_test -n 'when auth enabled with RS512 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/rs512/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_RS256_VALID}"' + + run_test -n 'when auth enabled with ES256 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/es256/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_ES256_VALID}"' + + run_test -n 'when auth enabled with ES256 algorithm via file and invalid JWT in Authorization header, returns 401' \ + -p '/secure/auth-header/es256/file' \ + -c '302' \ + -x '--header "Authorization: Bearer ${JWT_ES256_INVALID}"' + + run_test -n 'when auth enabled with ES384 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/es384/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_ES384_VALID}"' + + run_test -n 'when auth enabled with ES512 algorithm via file and valid JWT in Authorization header, returns 200' \ + -p '/secure/auth-header/es512/file' \ + -c '200' \ + -x '--header "Authorization: Bearer ${JWT_ES512_VALID}"' + + run_test -n 'when auth enabled with HS256 algorithm and valid JWT in custom header without bearer, returns 200' \ + -p '/secure/custom-header/hs256/' \ + -c '200' \ + -x '--header "Auth-Token: ${JWT_HS256_VALID}"' + + run_test -n 'when auth enabled with HS256 algorithm and valid JWT in custom header with bearer, returns 200' \ + -p '/secure/custom-header/hs256/' \ + -c '200' \ + -x '--header "Auth-Token: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts single claim to request variable' \ + -p '/secure/extract-claim/request/sub' \ + -r '< Test: sub=some-long-uuid' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims (single directive) to request variable' \ + -p '/secure/extract-claim/request/name-1' \ + -r '< Test: firstName=hello; lastName=world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims (multiple directives) to request variable' \ + -p '/secure/extract-claim/request/name-2' \ + -r '< Test: firstName=hello; lastName=world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts nested claim to request variable' \ + -p '/secure/extract-claim/request/nested' \ + -r '< Test: username=hello\.world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts single claim to response variable' \ + -p '/secure/extract-claim/response/sub' \ + -r '< Test: sub=some-long-uuid' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims (single directive) to response variable' \ + -p '/secure/extract-claim/response/name-1' \ + -r '< Test: firstName=hello; lastName=world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims (multiple directives) to response variable' \ + -p '/secure/extract-claim/response/name-2' \ + -r '< Test: firstName=hello; lastName=world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts nested claim to response variable' \ + -p '/secure/extract-claim/response/nested' \ + -r '< Test: username=hello.world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts single claim to response header' \ + -p '/secure/extract-claim/response/sub' \ + -r '< JWT-sub: some-long-uuid' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims (single directive) to response header' \ + -p '/secure/extract-claim/response/name-1' \ + -r '< JWT-firstName: hello' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims (multiple directives) to response header' \ + -p '/secure/extract-claim/response/name-2' \ + -r '< JWT-firstName: hello' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts nested claim to response header' \ + -p '/secure/extract-claim/response/nested' \ + -r '< JWT-username: hello\.world' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'tests single claim with if statement' \ + -p '/secure/extract-claim/if/sub' \ + -c 200 \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'tests absence of single claim with if statement' \ + -p '/secure/extract-claim/if/sub' \ + -c 401 \ + -x '--header "Authorization: Bearer ${JWT_HS256_MISSING_SUB}"' + + run_test -n 'extracts single claim to response body' \ + -p '/secure/extract-claim/body/sub' \ + -c 200 \ + -r 'sub: some-long-uuid$' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'extracts multiple claims to response body' \ + -p '/secure/extract-claim/body/multiple' \ + -c 200 \ + -r 'you are: hello world$' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'redirect based on claim' \ + -p '/profile/me' \ + -c 301 \ + -r '< Location: http://nginx:8000/profile/some-long-uuid' \ + -x '--header "Authorization: Bearer ${JWT_HS256_VALID}"' + + run_test -n 'returns 302 if auth enabled and no JWT provided' \ + -p '/return-url' \ + -c '302' + + run_test -n 'redirects to login if auth enabled and no JWT provided' \ + -p '/return-url' \ + -r '< Location: https://example\.com/login.*' + + run_test -n 'adds return_url to login URL when redirected to login' \ + -p '/return-url' \ + -r '< Location: https://example\.com/login\?return_url=http://nginx.*' + + run_test -n 'return_url includes port when redirected to login' \ + -p '/return-url' \ + -r "< Location: https://example\.com/login\?return_url=http://nginx:${PORT}/return-url" + + run_test -n 'return_url includes query when redirected to login' \ + -p '/return-url?test=123' \ + -r '< Location: https://example\.com/login\?return_url=http://nginx.*/return-url%3Ftest=123' + + if [[ "${NUM_FAILED}" = '0' ]]; then + printf "\nRan ${NUM_TESTS} tests successfully (skipped ${NUM_SKIPPED}).\n" + return 0 + else + printf "\nRan ${NUM_TESTS} tests: ${GREEN}$((${NUM_TESTS} - ${NUM_FAILED})) passed${NC}; ${RED}${NUM_FAILED} failed${NC}; ${NUM_SKIPPED} skipped\n" + return 1 + fi +} + +if [ "${DEBUG}" != '' ]; then + printf "\n${RED}Some tests will be skipped since DEBUG is set.${NC}\n" +fi + +printf "\n${GRAY}Starting tests using port ${PORT}...${NC}\n" +main From 7085f5c91503a92861797a266724be5bf2f3df5f Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 18 Feb 2025 11:23:46 -0500 Subject: [PATCH 24/29] update example config --- README.md | 2 +- {resources => examples}/nginx.conf | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) rename {resources => examples}/nginx.conf (70%) diff --git a/README.md b/README.md index f6b09d6..0fb2ea8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This module depends on the [JWT C Library](https://github.com/benmcollins/libjwt ## Directives -This module requires several new `nginx.conf` directives, which can be specified at the `http`, `server`, or `location` levels. +This module requires several new `nginx.conf` directives, which can be specified at the `http`, `server`, or `location` levels. See the [example NGINX config file](examples/nginx.conf) for more info. | Directive | Description | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/resources/nginx.conf b/examples/nginx.conf similarity index 70% rename from resources/nginx.conf rename to examples/nginx.conf index 9b8feab..9795363 100644 --- a/resources/nginx.conf +++ b/examples/nginx.conf @@ -18,6 +18,15 @@ http { '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; + auth_jwt_enabled on; + auth_jwt_algorithm 'put_algo_here'; + auth_jwt_key 'put_key_here'; + auth_jwt_location 'COOKIE=auth-token'; + auth_jwt_redirect on; + auth_jwt_loginurl 'put_login_url_here'; + + # Include other auth_jwt_* directives as needed. + sendfile on; keepalive_timeout 65; From a110d0c34924d6af8b8602c37b64e41ee1bd2910 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 18 Feb 2025 12:35:01 -0500 Subject: [PATCH 25/29] update NGINX versions to build against --- .github/workflows/make-releases.yml | 9 ++++++++- scripts | 19 ++++++++++--------- test/test-nginx.dockerfile | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index 487c4bf..d285bd8 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -30,7 +30,14 @@ jobs: strategy: matrix: # NGINX versions to build/test against - nginx-version: ['1.20.2', '1.22.1', '1.24.0', '1.26.2', '1.27.3'] + nginx-version: + - '1.20.2' # legacy + - '1.22.1' # legacy + - '1.24.0' # legacy + - '1.26.2' # stable + - '1.26.3' # stable + - '1.27.3' # mainline + - '1.27.4' # mainline # The following versions of libjwt are compatible: # * v1.0 - v1.12.0 diff --git a/scripts b/scripts index 59fc5c3..0d80ed5 100755 --- a/scripts +++ b/scripts @@ -19,17 +19,18 @@ SSL_IMAGE_MAP[$SSL_VERSION_3_0_11]="bookworm-slim:openssl-${SSL_VERSION_3_0_11}" SSL_IMAGE_MAP[$SSL_VERSION_3_2_1]="bookworm-slim:openssl-${SSL_VERSION_3_2_1}" # supported NGINX versions -- for binary distribution -NGINX_VERSION_LEGACY_1='1.20.2' -NGINX_VERSION_LEGACY_2='1.22.1' -NGINX_VERSION_LEGACY_3='1.24.0' -NGINX_VERSION_STABLE='1.26.2' -NGINX_VERSION_MAINLINE='1.27.3' -NGINX_VERSIONS=(${NGINX_VERSION_LEGACY_1} ${NGINX_VERSION_LEGACY_2} ${NGINX_VERSION_LEGACY_3} ${NGINX_VERSION_STABLE} ${NGINX_VERSION_MAINLINE}) -NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSION_STABLE}} - +NGINX_VERSIONS=( + '1.20.2' # legacy + '1.22.1' # legacy + '1.24.0' # legacy + '1.26.2' # stable + '1.26.3' # stable + '1.27.3' # mainline + '1.27.4' # mainline +) +NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSIONS[-1]}} IMAGE_NAME=${IMAGE_NAME:-nginx-auth-jwt} FULL_IMAGE_NAME=${ORG_NAME:-teslagov}/${IMAGE_NAME} - TEST_CONTAINER_NAME_PREFIX="${IMAGE_NAME}-test" TEST_COMPOSE_FILE='test/docker-compose-test.yml' diff --git a/test/test-nginx.dockerfile b/test/test-nginx.dockerfile index 1065558..f8c323f 100644 --- a/test/test-nginx.dockerfile +++ b/test/test-nginx.dockerfile @@ -1,6 +1,6 @@ ARG BASE_IMAGE -FROM ${BASE_IMAGE:?required} AS NGINX +FROM ${BASE_IMAGE:?required} ARG PORT ARG SSL_PORT From 8e4f2af67c5bfd91bb0820d63443cf8a6a33bd48 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 18 Feb 2025 12:48:54 -0500 Subject: [PATCH 26/29] update array style --- .github/workflows/make-releases.yml | 19 +++++++++++-------- scripts | 17 ++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index d285bd8..ab0aa87 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -31,13 +31,13 @@ jobs: matrix: # NGINX versions to build/test against nginx-version: - - '1.20.2' # legacy - - '1.22.1' # legacy - - '1.24.0' # legacy - - '1.26.2' # stable - - '1.26.3' # stable - - '1.27.3' # mainline - - '1.27.4' # mainline + - 1.20.2 # legacy + - 1.22.1 # legacy + - 1.24.0 # legacy + - 1.26.2 # stable + - 1.26.3 # stable + - 1.27.3 # mainline + - 1.27.4 # mainline # The following versions of libjwt are compatible: # * v1.0 - v1.12.0 @@ -47,7 +47,10 @@ jobs: # * Debian and Ubuntu's repos have v1.10.2 # * EPEL has v1.12.1 # This compiles against each version prior to a breaking change and the latest release - libjwt-version: ['1.12.0', '1.14.0', '1.15.3'] + libjwt-version: + - 1.12.0 + - 1.14.0 + - 1.15.3 runs-on: ubuntu-latest steps: diff --git a/scripts b/scripts index 0d80ed5..7ed7557 100755 --- a/scripts +++ b/scripts @@ -20,13 +20,13 @@ SSL_IMAGE_MAP[$SSL_VERSION_3_2_1]="bookworm-slim:openssl-${SSL_VERSION_3_2_1}" # supported NGINX versions -- for binary distribution NGINX_VERSIONS=( - '1.20.2' # legacy - '1.22.1' # legacy - '1.24.0' # legacy - '1.26.2' # stable - '1.26.3' # stable - '1.27.3' # mainline - '1.27.4' # mainline + 1.20.2 # legacy + 1.22.1 # legacy + 1.24.0 # legacy + 1.26.2 # stable + 1.26.3 # stable + 1.27.3 # mainline + 1.27.4 # mainline ) NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSIONS[-1]}} IMAGE_NAME=${IMAGE_NAME:-nginx-auth-jwt} @@ -139,8 +139,7 @@ make_release() { -C bin/usr/lib64/nginx/modules ngx_http_auth_jwt_module.so > /dev/null } -# Create releases for the current mainline and stable version, as well as the 2 most recent "legacy" versions. -# See: https://nginx.org/en/download.html +# Create releases for all NGINX versions defined in `NGINX_VERSIONS`. make_releases() { local moduleVersion=$(git describe --tags --abbrev=0) From df75972a5554790aac25899e6ec654a8154bd995 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 18 Feb 2025 13:43:23 -0500 Subject: [PATCH 27/29] fix script function calls --- scripts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts b/scripts index 7ed7557..943bebc 100755 --- a/scripts +++ b/scripts @@ -103,7 +103,7 @@ cp_bin() { local stopContainer=0; if [ "$(docker container inspect -f '{{.State.Running}}' ${IMAGE_NAME} | true)" != "true" ]; then - start_nginx + start stopContainer=1 fi @@ -117,7 +117,7 @@ cp_bin() { if [ $stopContainer ]; then printf "${MAGENTA}Stopping NGINX container (${IMAGE_NAME})...${NC}\n" - stop_nginx + stop fi } From e019e2039ce9d1f6abb369ed8aa7179aaba95012 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 18 Feb 2025 17:25:06 -0500 Subject: [PATCH 28/29] clean up & fix build process --- .github/workflows/make-releases.yml | 23 +++--- nginx.dockerfile | 96 +++++++++++++++++-------- scripts | 106 +++++++++++++++++----------- test/test-runner.dockerfile | 2 +- 4 files changed, 143 insertions(+), 84 deletions(-) diff --git a/.github/workflows/make-releases.yml b/.github/workflows/make-releases.yml index ab0aa87..c77edcb 100644 --- a/.github/workflows/make-releases.yml +++ b/.github/workflows/make-releases.yml @@ -29,7 +29,6 @@ jobs: needs: meta strategy: matrix: - # NGINX versions to build/test against nginx-version: - 1.20.2 # legacy - 1.22.1 # legacy @@ -39,14 +38,6 @@ jobs: - 1.27.3 # mainline - 1.27.4 # mainline - # The following versions of libjwt are compatible: - # * v1.0 - v1.12.0 - # * v1.12.1 - v1.14.0 - # * v1.15.0+ - # At the time of writing this: - # * Debian and Ubuntu's repos have v1.10.2 - # * EPEL has v1.12.1 - # This compiles against each version prior to a breaking change and the latest release libjwt-version: - 1.12.0 - 1.14.0 @@ -80,9 +71,10 @@ jobs: - name: Build jansson working-directory: ./jansson run: | - cmake . -DJANSSON_BUILD_SHARED_LIBS=1 -DJANSSON_BUILD_DOCS=OFF && \ - make && \ - make check && \ + set -e + cmake . -DJANSSON_BUILD_SHARED_LIBS=1 -DJANSSON_BUILD_DOCS=OFF + make + make check sudo make install # TODO cache the build result so we don't have to do this every time? @@ -96,9 +88,10 @@ jobs: - name: Build libjwt working-directory: ./libjwt run: | - autoreconf -i && \ - ./configure && \ - make all && \ + set -e + autoreconf -i + ./configure + make all sudo make install - name: Download NGINX diff --git a/nginx.dockerfile b/nginx.dockerfile index 360469b..68c67f0 100644 --- a/nginx.dockerfile +++ b/nginx.dockerfile @@ -1,24 +1,42 @@ -ARG BASE_IMAGE +ARG BASE_IMAGE=${:?required} ARG NGINX_VERSION +ARG LIBJWT_VERSION -FROM ${BASE_IMAGE} AS ngx_http_auth_jwt_builder_base +FROM ${BASE_IMAGE} AS ngx_http_auth_jwt_builder LABEL stage=ngx_http_auth_jwt_builder -RUN chmod 1777 /tmp +ENV PATH="${PATH}:/etc/nginx" +ENV LD_LIBRARY_PATH=/usr/local/lib +ARG NGINX_VERSION +ARG LIBJWT_VERSION + RUN <<` -apt-get update -apt-get install -y curl build-essential + set -e + apt-get update + apt-get upgrade -y ` -FROM ngx_http_auth_jwt_builder_base AS ngx_http_auth_jwt_builder_module -LABEL stage=ngx_http_auth_jwt_builder -ENV PATH "${PATH}:/etc/nginx" -ENV LD_LIBRARY_PATH=/usr/local/lib -ARG NGINX_VERSION +RUN apt-get install -y curl git zlib1g-dev libpcre3-dev build-essential libpcre2-dev zlib1g-dev libpcre3-dev pkg-config cmake dh-autoreconf + +WORKDIR /root/build/libjansson RUN <<` set -e - apt-get install -y libjwt-dev libjwt0 libjansson-dev libjansson4 libpcre2-dev zlib1g-dev libpcre3-dev - mkdir -p /root/build/ngx-http-auth-jwt-module + git clone --depth 1 --branch v2.14 https://github.com/akheron/jansson . + cmake . -DJANSSON_BUILD_SHARED_LIBS=1 -DJANSSON_BUILD_DOCS=OFF + make + make check + make install ` + +WORKDIR /root/build/libjwt +RUN <<` + set -e + git clone --depth 1 --branch v${LIBJWT_VERSION} https://github.com/benmcollins/libjwt . + autoreconf -i + ./configure + make all + make install +` + WORKDIR /root/build/ngx-http-auth-jwt-module ADD config ./ ADD src/*.h src/*.c ./src/ @@ -29,6 +47,7 @@ RUN <<` curl -O http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz tar -xzf nginx-${NGINX_VERSION}.tar.gz --strip-components 1 -C nginx ` + WORKDIR /root/build/nginx RUN <<` set -e @@ -89,30 +108,46 @@ RUN <<` ${BUILD_FLAGS} # --with-openssl=/usr/local \ ` + RUN make modules RUN make install -WORKDIR /usr/lib64/nginx/modules -RUN cp /root/build/nginx/objs/ngx_http_auth_jwt_module.so . + +WORKDIR /usr/lib/nginx/modules +RUN mv /root/build/nginx/objs/ngx_http_auth_jwt_module.so . RUN rm -rf /root/build -RUN adduser --system --no-create-home --shell /bin/false --group --disabled-login nginx -RUN mkdir -p /var/cache/nginx /var/log/nginx -WORKDIR /etc/nginx -FROM ngx_http_auth_jwt_builder_module AS ngx_http_auth_jwt_nginx -LABEL maintainer="TeslaGov" email="developers@teslagov.com" -ARG NGINX_VERSION RUN <<` set -e - - apt-get update - apt-get install -y libjansson4 libjwt0 + apt-get remove -y curl git zlib1g-dev libpcre3-dev build-essential libpcre2-dev zlib1g-dev libpcre3-dev pkg-config cmake dh-autoreconf + # apt-get install -y gnupg2 ca-certificates lsb-release debian-archive-keyring apt-get clean ` + +RUN <<` + set -e + groupadd nginx + useradd -g nginx nginx +` + +# RUN <<` +# set -e +# curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor > /usr/share/keyrings/nginx-archive-keyring.gpg +# printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian `lsb_release -cs` nginx\n" > /etc/apt/sources.list.d/nginx.list +# printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" > /etc/apt/preferences.d/99nginx +# ` + +# RUN <<` +# set -e +# apt-get update +# apt-get install -y nginx +# ` + COPY <<` /etc/nginx/nginx.conf +daemon off; user nginx; pid /var/run/nginx.pid; -load_module /usr/lib64/nginx/modules/ngx_http_auth_jwt_module.so; +load_module /usr/lib/nginx/modules/ngx_http_auth_jwt_module.so; worker_processes 1; @@ -124,12 +159,17 @@ http { include 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"'; + 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; include conf.d/*.conf; } ` -ENTRYPOINT ["nginx", "-g", "daemon off;"] + +WORKDIR /var/cache/nginx +RUN chown nginx:nginx . + +WORKDIR / +CMD ["nginx"] diff --git a/scripts b/scripts index 943bebc..ca48984 100755 --- a/scripts +++ b/scripts @@ -8,14 +8,17 @@ NC='\033[0m' # supported SSL versions SSL_VERSION_1_1_1w='1.1.1w' -SSL_VERSION_3_0_11='3.0.11' +SSL_VERSION_3_0_15='3.0.15' SSL_VERSION_3_2_1='3.2.1' -SSL_VERSIONS=(${SSL_VERSION_3_2_1}) -SSL_VERSION=${SSL_VERSION:-$SSL_VERSION_3_0_11} +SSL_VERSIONS=( + ${SSL_VERSION_1_1_1w} + ${SSL_VERSION_3_0_15} + ${SSL_VERSION_3_2_1} +) declare -A SSL_IMAGE_MAP SSL_IMAGE_MAP[$SSL_VERSION_1_1_1w]="bullseye-slim:openssl-${SSL_VERSION_1_1_1w}" -SSL_IMAGE_MAP[$SSL_VERSION_3_0_11]="bookworm-slim:openssl-${SSL_VERSION_3_0_11}" +SSL_IMAGE_MAP[$SSL_VERSION_3_0_15]="bookworm-slim:openssl-${SSL_VERSION_3_0_15}" SSL_IMAGE_MAP[$SSL_VERSION_3_2_1]="bookworm-slim:openssl-${SSL_VERSION_3_2_1}" # supported NGINX versions -- for binary distribution @@ -28,7 +31,27 @@ NGINX_VERSIONS=( 1.27.3 # mainline 1.27.4 # mainline ) + +# The following versions of libjwt are compatible: +# * v1.0 - v1.12.0 +# * v1.12.1 - v1.14.0 +# * v1.15.0+ +# At the time of writing this: +# * Debian and Ubuntu's repos have v1.10.2 +# * EPEL has v1.12.1 +# This compiles against each version prior to a breaking change and the latest release +LIBJWT_VERSION_DEBIAN=1.12.0 +LIBJWT_VERSION_EPEL=1.14.0 +LIBJWT_VERSION_LATEST=1.15.3 +LIBJWT_VERSIONS=( + ${LIBJWT_VERSION_DEBIAN} + ${LIBJWT_VERSION_EPEL} + ${LIBJWT_VERSION_LATEST} +) + +SSL_VERSION=${SSL_VERSION:-$SSL_VERSION_3_0_15} NGINX_VERSION=${NGINX_VERSION:-${NGINX_VERSIONS[-1]}} +LIBJWT_VERSION=${LIBJWT_VERSION:-${LIBJWT_VERSION_DEBIAN}} IMAGE_NAME=${IMAGE_NAME:-nginx-auth-jwt} FULL_IMAGE_NAME=${ORG_NAME:-teslagov}/${IMAGE_NAME} TEST_CONTAINER_NAME_PREFIX="${IMAGE_NAME}-test" @@ -40,7 +63,7 @@ all() { test_all } -verify_and_build_base_image() { +build_base_image() { local image=${SSL_IMAGE_MAP[$SSL_VERSION]} local baseImage=${image%%:*} @@ -53,23 +76,24 @@ verify_and_build_base_image() { --build-arg BASE_IMAGE=debian:${baseImage} \ --build-arg SSL_VERSION=${SSL_VERSION} \ -f openssl.dockerfile \ - -t ${image} . + -t ${image} \ + . fi } build_module() { - local dockerArgs=${1:-} local baseImage=${SSL_IMAGE_MAP[$SSL_VERSION]} - - verify_and_build_base_image - printf "${MAGENTA}Building module for NGINX ${NGINX_VERSION}...${NC}\n" + build_base_image + + printf "${MAGENTA}Building module for NGINX ${NGINX_VERSION}, libjwt ${LIBJWT_VERSION}...${NC}\n" docker buildx build \ -f nginx.dockerfile \ -t ${FULL_IMAGE_NAME}:${NGINX_VERSION} \ --build-arg BASE_IMAGE=${baseImage} \ --build-arg NGINX_VERSION=${NGINX_VERSION} \ - ${dockerArgs} . + --build-arg LIBJWT_VERSION=${LIBJWT_VERSION} \ + . if [ "$?" -ne 0 ]; then printf "${RED}✘ Build failed ${NC}\n" @@ -79,12 +103,9 @@ build_module() { } rebuild_module() { - clean_module - build_module --no-cache -} - -clean_module() { docker rmi -f $(docker images --filter=label=stage=ngx_http_auth_jwt_builder --quiet) 2> /dev/null || true + + build_module } start() { @@ -111,9 +132,9 @@ cp_bin() { rm -rf ${destDir}/* mkdir -p ${destDir} docker exec "${IMAGE_NAME}" sh -c "cd /; tar -chf - \ - usr/lib64/nginx/modules/ngx_http_auth_jwt_module.so \ - usr/lib/$(uname -m)-linux-gnu/libjansson.so.* \ - usr/lib/$(uname -m)-linux-gnu/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null + usr/lib/nginx/modules/ngx_http_auth_jwt_module.so \ + usr/local/lib/libjansson.so.* \ + usr/local/lib/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null if [ $stopContainer ]; then printf "${MAGENTA}Stopping NGINX container (${IMAGE_NAME})...${NC}\n" @@ -122,31 +143,30 @@ cp_bin() { } make_release() { - local moduleVersion=${1} - - NGINX_VERSION=${2} + local moduleVersion=$(git describe --tags --abbrev=0) printf "${MAGENTA}Making release for version ${moduleVersion} for NGINX ${NGINX_VERSION}...${NC}\n" rebuild_module rebuild_test - test + test --no-build cp_bin mkdir -p release - tar -czvf release/ngx_http_auth_jwt_module_${moduleVersion}_nginx_${NGINX_VERSION}.tgz \ + tar -czvf release/ngx-http-auth-jwt-module-${moduleVersion}_libjwt-${LIBJWT_VERSION}_nginx-${NGINX_VERSION}.tgz \ README.md \ - -C bin/usr/lib64/nginx/modules ngx_http_auth_jwt_module.so > /dev/null + -C bin/usr/lib/nginx/modules ngx_http_auth_jwt_module.so > /dev/null } # Create releases for all NGINX versions defined in `NGINX_VERSIONS`. make_releases() { - local moduleVersion=$(git describe --tags --abbrev=0) - rm -rf release/* - for v in ${NGINX_VERSIONS[@]}; do - make_release ${moduleVersion} ${v} + for NGINX_VERSION in ${NGINX_VERSIONS[@]}; do + for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do + export NGINX_VERSION LIBJWT_VERSION + make_release + done done } @@ -178,14 +198,21 @@ rebuild_test() { test_all() { for SSL_VERSION in "${SSL_VERSIONS[@]}"; do for NGINX_VERSION in "${NGINX_VERSIONS[@]}"; do - test + for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do + export SSL_VERSION NGINX_VERSION LIBJWT_VERSION + test + done done done } test() { - build_module - build_test + if [[ ! "$*" =~ --no-build ]]; then + build_module + build_test + fi + + trap 'test_cleanup' 0 printf "${MAGENTA}Running tests...${NC}\n" docker compose \ @@ -193,8 +220,6 @@ test() { -f ${TEST_COMPOSE_FILE} up \ --no-start - trap test_cleanup 0 - test_now } @@ -202,21 +227,22 @@ test_now() { nginxContainerName="${TEST_CONTAINER_NAME_PREFIX}-nginx" runnerContainerName="${TEST_CONTAINER_NAME_PREFIX}-runner" + echo + echo "Executing tests with the following options:" + echo " SSL Version: ${SSL_VERSION}" + echo " LIBJWT Version: ${LIBJWT_VERSION}" + echo " NGINX Version: ${NGINX_VERSION}" + docker start ${nginxContainerName} if [ "$(docker container inspect -f '{{.State.Running}}' ${nginxContainerName})" != "true" ]; then printf "${RED}Failed to start container \"${nginxContainerName}\". See logs below:\n" docker logs ${nginxContainerName} printf "${NC}\n" - return + return 1 fi docker start -a ${runnerContainerName} - - echo - echo "Tests were executed with the following options:" - echo " SSL Version: ${SSL_VERSION}" - echo " NGINX Version: ${NGINX_VERSION}" } test_cleanup() { diff --git a/test/test-runner.dockerfile b/test/test-runner.dockerfile index 18fc3d3..377c40e 100644 --- a/test/test-runner.dockerfile +++ b/test/test-runner.dockerfile @@ -15,4 +15,4 @@ RUN <<` COPY test.sh . -CMD ./test.sh +CMD ["./test.sh"] From b128954b6c3fc95d8538e75abac637405bdbd562 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Tue, 29 Jul 2025 16:02:03 -0400 Subject: [PATCH 29/29] specify platform / fix spacing --- scripts | 336 ++++++++++++++++++----------------- test/docker-compose-test.yml | 8 +- 2 files changed, 176 insertions(+), 168 deletions(-) diff --git a/scripts b/scripts index ca48984..7ce7024 100755 --- a/scripts +++ b/scripts @@ -11,9 +11,9 @@ SSL_VERSION_1_1_1w='1.1.1w' SSL_VERSION_3_0_15='3.0.15' SSL_VERSION_3_2_1='3.2.1' SSL_VERSIONS=( - ${SSL_VERSION_1_1_1w} - ${SSL_VERSION_3_0_15} - ${SSL_VERSION_3_2_1} + ${SSL_VERSION_1_1_1w} + ${SSL_VERSION_3_0_15} + ${SSL_VERSION_3_2_1} ) declare -A SSL_IMAGE_MAP @@ -23,13 +23,13 @@ SSL_IMAGE_MAP[$SSL_VERSION_3_2_1]="bookworm-slim:openssl-${SSL_VERSION_3_2_1}" # supported NGINX versions -- for binary distribution NGINX_VERSIONS=( - 1.20.2 # legacy - 1.22.1 # legacy - 1.24.0 # legacy - 1.26.2 # stable - 1.26.3 # stable - 1.27.3 # mainline - 1.27.4 # mainline + 1.20.2 # legacy + 1.22.1 # legacy + 1.24.0 # legacy + 1.26.2 # stable + 1.26.3 # stable + 1.27.3 # mainline + 1.27.4 # mainline ) # The following versions of libjwt are compatible: @@ -44,9 +44,9 @@ LIBJWT_VERSION_DEBIAN=1.12.0 LIBJWT_VERSION_EPEL=1.14.0 LIBJWT_VERSION_LATEST=1.15.3 LIBJWT_VERSIONS=( - ${LIBJWT_VERSION_DEBIAN} - ${LIBJWT_VERSION_EPEL} - ${LIBJWT_VERSION_LATEST} + ${LIBJWT_VERSION_DEBIAN} + ${LIBJWT_VERSION_EPEL} + ${LIBJWT_VERSION_LATEST} ) SSL_VERSION=${SSL_VERSION:-$SSL_VERSION_3_0_15} @@ -58,216 +58,218 @@ TEST_CONTAINER_NAME_PREFIX="${IMAGE_NAME}-test" TEST_COMPOSE_FILE='test/docker-compose-test.yml' all() { - build_module - build_test - test_all + build_module + build_test + test_all } build_base_image() { - local image=${SSL_IMAGE_MAP[$SSL_VERSION]} - local baseImage=${image%%:*} - - if [ -z ${image} ]; then - echo "Base image not set for SSL version :${SSL_VERSION}" - exit 1 - else - printf "${MAGENTA}Building ${baseImage} base image for SSL ${SSL_VERSION}...${NC}\n" - docker buildx build \ - --build-arg BASE_IMAGE=debian:${baseImage} \ - --build-arg SSL_VERSION=${SSL_VERSION} \ - -f openssl.dockerfile \ - -t ${image} \ - . - fi + local image=${SSL_IMAGE_MAP[$SSL_VERSION]} + local baseImage=${image%%:*} + + if [ -z ${image} ]; then + echo "Base image not set for SSL version :${SSL_VERSION}" + exit 1 + else + printf "${MAGENTA}Building ${baseImage} base image for SSL ${SSL_VERSION}...${NC}\n" + docker buildx build \ + --platform linux/amd64 \ + --build-arg BASE_IMAGE=debian:${baseImage} \ + --build-arg SSL_VERSION=${SSL_VERSION} \ + -f openssl.dockerfile \ + -t ${image} \ + . + fi } build_module() { - local baseImage=${SSL_IMAGE_MAP[$SSL_VERSION]} - - build_base_image - - printf "${MAGENTA}Building module for NGINX ${NGINX_VERSION}, libjwt ${LIBJWT_VERSION}...${NC}\n" - docker buildx build \ - -f nginx.dockerfile \ - -t ${FULL_IMAGE_NAME}:${NGINX_VERSION} \ - --build-arg BASE_IMAGE=${baseImage} \ - --build-arg NGINX_VERSION=${NGINX_VERSION} \ - --build-arg LIBJWT_VERSION=${LIBJWT_VERSION} \ - . - - if [ "$?" -ne 0 ]; then - printf "${RED}✘ Build failed ${NC}\n" - else - printf "${GREEN}✔ Successfully built NGINX module ${NC}\n" - fi + local baseImage=${SSL_IMAGE_MAP[$SSL_VERSION]} + + build_base_image + + printf "${MAGENTA}Building module for NGINX ${NGINX_VERSION}, libjwt ${LIBJWT_VERSION}...${NC}\n" + docker buildx build \ + --platform linux/amd64 \ + -f nginx.dockerfile \ + -t ${FULL_IMAGE_NAME}:${NGINX_VERSION} \ + --build-arg BASE_IMAGE=${baseImage} \ + --build-arg NGINX_VERSION=${NGINX_VERSION} \ + --build-arg LIBJWT_VERSION=${LIBJWT_VERSION} \ + . + + if [ "$?" -ne 0 ]; then + printf "${RED}✘ Build failed ${NC}\n" + else + printf "${GREEN}✔ Successfully built NGINX module ${NC}\n" + fi } rebuild_module() { - docker rmi -f $(docker images --filter=label=stage=ngx_http_auth_jwt_builder --quiet) 2> /dev/null || true + docker rmi -f $(docker images --filter=label=stage=ngx_http_auth_jwt_builder --quiet) 2> /dev/null || true - build_module + build_module } start() { - local port=$(get_port) + local port=$(get_port) - printf "${MAGENTA}Starting NGINX container (${IMAGE_NAME}) on port ${port}...${NC}\n" - docker run --rm --name "${IMAGE_NAME}" -d -p ${port}:80 ${FULL_IMAGE_NAME}:${NGINX_VERSION} >/dev/null + printf "${MAGENTA}Starting NGINX container (${IMAGE_NAME}) on port ${port}...${NC}\n" + docker run --rm --name "${IMAGE_NAME}" -d -p ${port}:80 ${FULL_IMAGE_NAME}:${NGINX_VERSION} >/dev/null } stop() { - docker stop "${IMAGE_NAME}" >/dev/null + docker stop "${IMAGE_NAME}" >/dev/null } cp_bin() { - local destDir=bin - local stopContainer=0; - - if [ "$(docker container inspect -f '{{.State.Running}}' ${IMAGE_NAME} | true)" != "true" ]; then - start - stopContainer=1 - fi - - printf "${MAGENTA}Copying binaries to: ${destDir}${NC}\n" - rm -rf ${destDir}/* - mkdir -p ${destDir} - docker exec "${IMAGE_NAME}" sh -c "cd /; tar -chf - \ - usr/lib/nginx/modules/ngx_http_auth_jwt_module.so \ - usr/local/lib/libjansson.so.* \ - usr/local/lib/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null - - if [ $stopContainer ]; then - printf "${MAGENTA}Stopping NGINX container (${IMAGE_NAME})...${NC}\n" - stop - fi + local destDir=bin + local stopContainer=0; + + if [ "$(docker container inspect -f '{{.State.Running}}' ${IMAGE_NAME} | true)" != "true" ]; then + start + stopContainer=1 + fi + + printf "${MAGENTA}Copying binaries to: ${destDir}${NC}\n" + rm -rf ${destDir}/* + mkdir -p ${destDir} + docker exec "${IMAGE_NAME}" sh -c "cd /; tar -chf - \ + usr/lib/nginx/modules/ngx_http_auth_jwt_module.so \ + usr/local/lib/libjansson.so.* \ + usr/local/lib/libjwt.*" | tar -xf - -C ${destDir} &>/dev/null + + if [ $stopContainer ]; then + printf "${MAGENTA}Stopping NGINX container (${IMAGE_NAME})...${NC}\n" + stop + fi } make_release() { - local moduleVersion=$(git describe --tags --abbrev=0) + local moduleVersion=$(git describe --tags --abbrev=0) - printf "${MAGENTA}Making release for version ${moduleVersion} for NGINX ${NGINX_VERSION}...${NC}\n" + printf "${MAGENTA}Making release for version ${moduleVersion} for NGINX ${NGINX_VERSION}...${NC}\n" - rebuild_module - rebuild_test - test --no-build - cp_bin + rebuild_module + rebuild_test + test --no-build + cp_bin - mkdir -p release - tar -czvf release/ngx-http-auth-jwt-module-${moduleVersion}_libjwt-${LIBJWT_VERSION}_nginx-${NGINX_VERSION}.tgz \ - README.md \ - -C bin/usr/lib/nginx/modules ngx_http_auth_jwt_module.so > /dev/null + mkdir -p release + tar -czvf release/ngx-http-auth-jwt-module-${moduleVersion}_libjwt-${LIBJWT_VERSION}_nginx-${NGINX_VERSION}.tgz \ + README.md \ + -C bin/usr/lib/nginx/modules ngx_http_auth_jwt_module.so > /dev/null } # Create releases for all NGINX versions defined in `NGINX_VERSIONS`. make_releases() { - rm -rf release/* - - for NGINX_VERSION in ${NGINX_VERSIONS[@]}; do - for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do - export NGINX_VERSION LIBJWT_VERSION - make_release - done - done + rm -rf release/* + + for NGINX_VERSION in ${NGINX_VERSIONS[@]}; do + for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do + export NGINX_VERSION LIBJWT_VERSION + make_release + done + done } build_test() { - local dockerArgs=${1:-} - local port=$(get_port) - local sslPort=$(get_port $((port + 1))) - local runnerBaseImage=${SSL_IMAGE_MAP[$SSL_VERSION]} - - export TEST_CONTAINER_NAME_PREFIX - export FULL_IMAGE_NAME - export NGINX_VERSION - - printf "${MAGENTA}Building test NGINX & runner using port ${port}...${NC}\n" - docker compose \ - -p ${TEST_CONTAINER_NAME_PREFIX} \ - -f ${TEST_COMPOSE_FILE} \ - build \ - --build-arg RUNNER_BASE_IMAGE=${runnerBaseImage} \ - --build-arg PORT=${port} \ - --build-arg SSL_PORT=${sslPort} \ - ${dockerArgs} + local dockerArgs=${1:-} + local port=$(get_port) + local sslPort=$(get_port $((port + 1))) + local runnerBaseImage=${SSL_IMAGE_MAP[$SSL_VERSION]} + + export TEST_CONTAINER_NAME_PREFIX + export FULL_IMAGE_NAME + export NGINX_VERSION + + printf "${MAGENTA}Building test NGINX & runner using port ${port}...${NC}\n" + docker compose \ + -p ${TEST_CONTAINER_NAME_PREFIX} \ + -f ${TEST_COMPOSE_FILE} \ + build \ + --build-arg RUNNER_BASE_IMAGE=${runnerBaseImage} \ + --build-arg PORT=${port} \ + --build-arg SSL_PORT=${sslPort} \ + ${dockerArgs} } rebuild_test() { - build_test --no-cache + build_test --no-cache } test_all() { - for SSL_VERSION in "${SSL_VERSIONS[@]}"; do - for NGINX_VERSION in "${NGINX_VERSIONS[@]}"; do - for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do - export SSL_VERSION NGINX_VERSION LIBJWT_VERSION - test - done - done - done + for SSL_VERSION in "${SSL_VERSIONS[@]}"; do + for NGINX_VERSION in "${NGINX_VERSIONS[@]}"; do + for LIBJWT_VERSION in ${LIBJWT_VERSIONS[@]}; do + export SSL_VERSION NGINX_VERSION LIBJWT_VERSION + test + done + done + done } test() { - if [[ ! "$*" =~ --no-build ]]; then - build_module - build_test - fi + if [[ ! "$*" =~ --no-build ]]; then + build_module + build_test + fi - trap 'test_cleanup' 0 + trap 'test_cleanup' 0 - printf "${MAGENTA}Running tests...${NC}\n" - docker compose \ - -p ${TEST_CONTAINER_NAME_PREFIX} \ - -f ${TEST_COMPOSE_FILE} up \ - --no-start + printf "${MAGENTA}Running tests...${NC}\n" + docker compose \ + -p ${TEST_CONTAINER_NAME_PREFIX} \ + -f ${TEST_COMPOSE_FILE} up \ + --no-start - test_now + test_now } test_now() { - nginxContainerName="${TEST_CONTAINER_NAME_PREFIX}-nginx" - runnerContainerName="${TEST_CONTAINER_NAME_PREFIX}-runner" - - echo - echo "Executing tests with the following options:" - echo " SSL Version: ${SSL_VERSION}" - echo " LIBJWT Version: ${LIBJWT_VERSION}" - echo " NGINX Version: ${NGINX_VERSION}" - - docker start ${nginxContainerName} - - if [ "$(docker container inspect -f '{{.State.Running}}' ${nginxContainerName})" != "true" ]; then - printf "${RED}Failed to start container \"${nginxContainerName}\". See logs below:\n" - docker logs ${nginxContainerName} - printf "${NC}\n" - return 1 - fi - - docker start -a ${runnerContainerName} + nginxContainerName="${TEST_CONTAINER_NAME_PREFIX}-nginx" + runnerContainerName="${TEST_CONTAINER_NAME_PREFIX}-runner" + + echo + echo "Executing tests with the following options:" + echo " SSL Version: ${SSL_VERSION}" + echo " LIBJWT Version: ${LIBJWT_VERSION}" + echo " NGINX Version: ${NGINX_VERSION}" + + docker start ${nginxContainerName} + + if [ "$(docker container inspect -f '{{.State.Running}}' ${nginxContainerName})" != "true" ]; then + printf "${RED}Failed to start container \"${nginxContainerName}\". See logs below:\n" + docker logs ${nginxContainerName} + printf "${NC}\n" + return 1 + fi + + docker start -a ${runnerContainerName} } test_cleanup() { - docker compose \ - -p ${TEST_CONTAINER_NAME_PREFIX} \ - -f ${TEST_COMPOSE_FILE} down + docker compose \ + -p ${TEST_CONTAINER_NAME_PREFIX} \ + -f ${TEST_COMPOSE_FILE} down } get_port() { - startPort=${1:-8000} - endPort=$((startPort + 100)) - - for p in $(seq ${startPort} ${endPort}); do - if ! ss -ln | grep -q ":${p} "; then - echo ${p} - break - fi - done + startPort=${1:-8000} + endPort=$((startPort + 100)) + + for p in $(seq ${startPort} ${endPort}); do + if ! ss -ln | grep -q ":${p} "; then + echo ${p} + break + fi + done } if [ $# -eq 0 ]; then - all + all else - fn=$1 - shift - - ${fn} "$@" + fn=$1 + shift + + ${fn} "$@" fi diff --git a/test/docker-compose-test.yml b/test/docker-compose-test.yml index 72ff710..cc570c5 100644 --- a/test/docker-compose-test.yml +++ b/test/docker-compose-test.yml @@ -5,8 +5,11 @@ services: build: context: . dockerfile: test-nginx.dockerfile + platforms: + - linux/amd64 args: BASE_IMAGE: ${FULL_IMAGE_NAME}:${NGINX_VERSION:?required} + platform: linux/amd64 logging: driver: ${LOG_DRIVER:-journald} @@ -15,5 +18,8 @@ services: build: context: . dockerfile: test-runner.dockerfile + platforms: + - linux/amd64 + platform: linux/amd64 depends_on: - - nginx \ No newline at end of file + - nginx