Skip to content

Commit 4687b54

Browse files
committed
Merge remote-tracking branch 'origin/chore/fix-nap-v4' into chore/fix-nap-v4
2 parents 85c3a54 + d2ed43a commit 4687b54

File tree

18 files changed

+650
-28
lines changed

18 files changed

+650
-28
lines changed

.github/scripts/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,9 @@ markupsafe==3.0.2 \
304304
--hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \
305305
--hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50
306306
# via jinja2
307-
pycparser==2.22 \
308-
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
309-
--hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
307+
pycparser==2.23 \
308+
--hash=sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2 \
309+
--hash=sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934
310310
# via cffi
311311
pygithub==2.6.1 \
312312
--hash=sha256:6f2fa6d076ccae475f9fc392cc6cdbd54db985d4f69b8833a28397de75ed6ca3 \

.github/workflows/scorecards.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
persist-credentials: false
3535

3636
- name: "Run analysis"
37-
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
37+
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
3838
with:
3939
results_file: results.sarif
4040
results_format: sarif

build/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ RUN --mount=type=bind,from=nginx-files,src=nginx_signing.rsa.pub,target=/etc/apk
105105

106106

107107
############################################# Base image for Debian #############################################
108-
FROM nginx:1.29.1@sha256:d5f28ef21aabddd098f3dbc21fe5b7a7d7a184720bc07da0b6c9b9820e97f25e AS debian
108+
FROM nginx:1.29.1@sha256:8adbdcb969e2676478ee2c7ad333956f0c8e0e4c5a7463f4611d7a2e7a7ff5dc AS debian
109109
ARG NGINX_AGENT_VERSION
110110

111111
RUN --mount=type=bind,from=nginx-files,src=nginx_signing.key,target=/tmp/nginx_signing.key \

config/crd/bases/k8s.nginx.org_policies.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,32 @@ spec:
304304
server. If not set, the hostname from the ``jwksURI`` will be
305305
used.
306306
type: string
307+
sslVerify:
308+
default: false
309+
description: Enables verification of the JWKS server SSL certificate.
310+
Default is false.
311+
type: boolean
312+
sslVerifyDepth:
313+
default: 1
314+
description: Sets the verification depth in the JWKS server certificates
315+
chain. The default is 1.
316+
minimum: 0
317+
type: integer
307318
token:
308319
description: 'The token specifies a variable that contains the
309320
JSON Web Token. By default the JWT is passed in the Authorization
310321
header as a Bearer Token. JWT may be also passed as a cookie
311322
or a part of a query string, for example: $cookie_auth_token.
312323
Accepted variables are $http_, $arg_, $cookie_.'
313324
type: string
325+
trustedCertSecret:
326+
description: The name of the Kubernetes secret that stores the
327+
CA certificate for JWKS server verification. It must be in the
328+
same namespace as the Policy resource. The secret must be of
329+
the type nginx.org/ca, and the certificate must be stored in
330+
the secret under the key ca.crt.
331+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
332+
type: string
314333
type: object
315334
oidc:
316335
description: The OpenID Connect policy configures NGINX to authenticate

deploy/crds.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,13 +475,32 @@ spec:
475475
server. If not set, the hostname from the ``jwksURI`` will be
476476
used.
477477
type: string
478+
sslVerify:
479+
default: false
480+
description: Enables verification of the JWKS server SSL certificate.
481+
Default is false.
482+
type: boolean
483+
sslVerifyDepth:
484+
default: 1
485+
description: Sets the verification depth in the JWKS server certificates
486+
chain. The default is 1.
487+
minimum: 0
488+
type: integer
478489
token:
479490
description: 'The token specifies a variable that contains the
480491
JSON Web Token. By default the JWT is passed in the Authorization
481492
header as a Bearer Token. JWT may be also passed as a cookie
482493
or a part of a query string, for example: $cookie_auth_token.
483494
Accepted variables are $http_, $arg_, $cookie_.'
484495
type: string
496+
trustedCertSecret:
497+
description: The name of the Kubernetes secret that stores the
498+
CA certificate for JWKS server verification. It must be in the
499+
same namespace as the Policy resource. The secret must be of
500+
the type nginx.org/ca, and the certificate must be stored in
501+
the secret under the key ca.crt.
502+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
503+
type: string
485504
type: object
486505
oidc:
487506
description: The OpenID Connect policy configures NGINX to authenticate

docs/crd/k8s.nginx.org_policies.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ The `.spec` object supports the following fields:
5858
| `jwt.secret` | `string` | The name of the Kubernetes secret that stores the Htpasswd configuration. It must be in the same namespace as the Policy resource. The secret must be of the type nginx.org/htpasswd, and the config must be stored in the secret under the key htpasswd, otherwise the secret will be rejected as invalid. |
5959
| `jwt.sniEnabled` | `boolean` | Enables SNI (Server Name Indication) for the JWT policy. This is useful when the remote server requires SNI to serve the correct certificate. |
6060
| `jwt.sniName` | `string` | The SNI name to use when connecting to the remote server. If not set, the hostname from the ``jwksURI`` will be used. |
61+
| `jwt.sslVerify` | `boolean` | Enables verification of the JWKS server SSL certificate. Default is false. |
62+
| `jwt.sslVerifyDepth` | `integer` | Sets the verification depth in the JWKS server certificates chain. The default is 1. |
6163
| `jwt.token` | `string` | The token specifies a variable that contains the JSON Web Token. By default the JWT is passed in the Authorization header as a Bearer Token. JWT may be also passed as a cookie or a part of a query string, for example: $cookie_auth_token. Accepted variables are $http_, $arg_, $cookie_. |
64+
| `jwt.trustedCertSecret` | `string` | The name of the Kubernetes secret that stores the CA certificate for JWKS server verification. It must be in the same namespace as the Policy resource. The secret must be of the type nginx.org/ca, and the certificate must be stored in the secret under the key ca.crt. |
6265
| `oidc` | `object` | The OpenID Connect policy configures NGINX to authenticate client requests by validating a JWT token against an OAuth2/OIDC token provider, such as Auth0 or Keycloak. |
6366
| `oidc.accessTokenEnable` | `boolean` | Option of whether Bearer token is used to authorize NGINX to access protected backend. |
6467
| `oidc.authEndpoint` | `string` | URL for the authorization endpoint provided by your OpenID Connect provider. |

internal/configs/version2/__snapshots__/templates_test.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,7 @@ server {
12591259
proxy_set_header Content-Length "";
12601260
proxy_ssl_server_name on;
12611261
proxy_ssl_name sni.idp.spec.example.com;
1262+
proxy_ssl_verify off;
12621263
proxy_pass_request_headers off;
12631264
proxy_pass_request_body off;
12641265
proxy_set_header Host idp.spec.example.com;
@@ -1271,6 +1272,7 @@ server {
12711272
proxy_set_header Content-Length "";
12721273
proxy_ssl_server_name on;
12731274
proxy_ssl_name sni.idp.spec.example.com;
1275+
proxy_ssl_verify off;
12741276
proxy_pass_request_headers off;
12751277
proxy_pass_request_body off;
12761278
proxy_set_header Host idp.route.example.com;
@@ -1380,6 +1382,7 @@ server {
13801382
internal;
13811383
proxy_method GET;
13821384
proxy_set_header Content-Length "";
1385+
proxy_ssl_verify off;
13831386
proxy_pass_request_headers off;
13841387
proxy_pass_request_body off;
13851388
proxy_set_header Host idp.spec.example.com;
@@ -1390,6 +1393,7 @@ server {
13901393
internal;
13911394
proxy_method GET;
13921395
proxy_set_header Content-Length "";
1396+
proxy_ssl_verify off;
13931397
proxy_pass_request_headers off;
13941398
proxy_pass_request_body off;
13951399
proxy_set_header Host idp.route.example.com;

internal/configs/version2/http.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ type JwksURI struct {
448448
JwksPath string
449449
JwksSNIName string
450450
JwksSNIEnabled bool
451+
SSLVerify bool
452+
TrustedCert string
453+
SSLVerifyDepth int
451454
}
452455

453456
// BasicAuth refers to basic HTTP authentication mechanism options

internal/configs/version2/nginx-plus.virtualserver.tmpl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,17 @@ server {
293293
proxy_ssl_name {{ .JwksSNIName }};
294294
{{- end }}
295295
{{- end }}
296+
{{- if .SSLVerify }}
297+
proxy_ssl_verify on;
298+
proxy_ssl_verify_depth {{ .SSLVerifyDepth }};
299+
{{- if .TrustedCert }}
300+
proxy_ssl_trusted_certificate {{ .TrustedCert }};
301+
{{- else }}
302+
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
303+
{{- end }}
304+
{{- else }}
305+
proxy_ssl_verify off;
306+
{{- end }}
296307
proxy_pass_request_headers off;
297308
proxy_pass_request_body off;
298309
proxy_set_header Host {{ .JwksHost }};

internal/configs/version2/templates_test.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,4 +3031,161 @@ var (
30313031
},
30323032
},
30333033
}
3034+
3035+
// JWT SSL Verification test configs
3036+
virtualServerCfgWithJWTSSLDefaultCert = VirtualServerConfig{
3037+
Upstreams: []Upstream{
3038+
{
3039+
Name: "vs_default_cafe_tea",
3040+
Servers: []UpstreamServer{
3041+
{Address: "10.0.0.20:80"},
3042+
},
3043+
},
3044+
},
3045+
Server: Server{
3046+
JWTAuthList: map[string]*JWTAuth{
3047+
"default/jwt-ssl-policy": {
3048+
Key: "default/jwt-ssl-policy",
3049+
Realm: "SSL API",
3050+
KeyCache: "1h",
3051+
JwksURI: JwksURI{
3052+
JwksScheme: "https",
3053+
JwksHost: "idp.example.com",
3054+
JwksPath: "/keys",
3055+
SSLVerify: true,
3056+
SSLVerifyDepth: 1,
3057+
},
3058+
},
3059+
},
3060+
Locations: []Location{
3061+
{
3062+
Path: "/",
3063+
ProxyPass: "http://vs_default_cafe_tea",
3064+
JWTAuth: &JWTAuth{
3065+
Key: "default/jwt-ssl-policy",
3066+
},
3067+
},
3068+
},
3069+
},
3070+
}
3071+
3072+
virtualServerCfgWithJWTSSLSecretCert = VirtualServerConfig{
3073+
Upstreams: []Upstream{
3074+
{
3075+
Name: "vs_default_cafe_tea",
3076+
Servers: []UpstreamServer{
3077+
{Address: "10.0.0.20:80"},
3078+
},
3079+
},
3080+
},
3081+
Server: Server{
3082+
JWTAuthList: map[string]*JWTAuth{
3083+
"default/jwt-ssl-policy": {
3084+
Key: "default/jwt-ssl-policy",
3085+
Realm: "SSL API",
3086+
KeyCache: "1h",
3087+
JwksURI: JwksURI{
3088+
JwksScheme: "https",
3089+
JwksHost: "idp.example.com",
3090+
JwksPath: "/keys",
3091+
SSLVerify: true,
3092+
TrustedCert: "/etc/nginx/secrets/default-my-ca-secret",
3093+
SSLVerifyDepth: 2,
3094+
},
3095+
},
3096+
},
3097+
Locations: []Location{
3098+
{
3099+
Path: "/",
3100+
ProxyPass: "http://vs_default_cafe_tea",
3101+
JWTAuth: &JWTAuth{
3102+
Key: "default/jwt-ssl-policy",
3103+
},
3104+
},
3105+
},
3106+
},
3107+
}
3108+
3109+
virtualServerCfgWithJWTNoSSL = VirtualServerConfig{
3110+
Upstreams: []Upstream{
3111+
{
3112+
Name: "vs_default_cafe_tea",
3113+
Servers: []UpstreamServer{
3114+
{Address: "10.0.0.20:80"},
3115+
},
3116+
},
3117+
},
3118+
Server: Server{
3119+
JWTAuthList: map[string]*JWTAuth{
3120+
"default/jwt-no-ssl-policy": {
3121+
Key: "default/jwt-no-ssl-policy",
3122+
Realm: "No SSL API",
3123+
KeyCache: "1h",
3124+
JwksURI: JwksURI{
3125+
JwksScheme: "https",
3126+
JwksHost: "idp.example.com",
3127+
JwksPath: "/keys",
3128+
SSLVerify: false,
3129+
},
3130+
},
3131+
},
3132+
Locations: []Location{
3133+
{
3134+
Path: "/",
3135+
ProxyPass: "http://vs_default_cafe_tea",
3136+
JWTAuth: &JWTAuth{
3137+
Key: "default/jwt-no-ssl-policy",
3138+
},
3139+
},
3140+
},
3141+
},
3142+
}
30343143
)
3144+
3145+
func TestJWTSSLVerificationDefaultCert(t *testing.T) {
3146+
t.Parallel()
3147+
executor := newTmplExecutorNGINXPlus(t)
3148+
got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithJWTSSLDefaultCert)
3149+
if err != nil {
3150+
t.Error(err)
3151+
}
3152+
if !bytes.Contains(got, []byte("proxy_ssl_verify on;")) {
3153+
t.Error("want `proxy_ssl_verify on;` in generated template")
3154+
}
3155+
if !bytes.Contains(got, []byte("proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;")) {
3156+
t.Error("want system CA bundle path in generated template")
3157+
}
3158+
}
3159+
3160+
func TestJWTSSLVerificationSecretCert(t *testing.T) {
3161+
t.Parallel()
3162+
executor := newTmplExecutorNGINXPlus(t)
3163+
got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithJWTSSLSecretCert)
3164+
if err != nil {
3165+
t.Error(err)
3166+
}
3167+
if !bytes.Contains(got, []byte("proxy_ssl_verify on;")) {
3168+
t.Error("want `proxy_ssl_verify on;` in generated template")
3169+
}
3170+
if !bytes.Contains(got, []byte("proxy_ssl_trusted_certificate /etc/nginx/secrets/default-my-ca-secret;")) {
3171+
t.Error("want custom CA secret path in generated template")
3172+
}
3173+
if !bytes.Contains(got, []byte("proxy_ssl_verify_depth 2;")) {
3174+
t.Error("want custom SSL verify depth in generated template")
3175+
}
3176+
}
3177+
3178+
func TestJWTNoSSLVerification(t *testing.T) {
3179+
t.Parallel()
3180+
executor := newTmplExecutorNGINXPlus(t)
3181+
got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithJWTNoSSL)
3182+
if err != nil {
3183+
t.Error(err)
3184+
}
3185+
if !bytes.Contains(got, []byte("proxy_ssl_verify off;")) {
3186+
t.Error("want `proxy_ssl_verify off;` in generated template")
3187+
}
3188+
if bytes.Contains(got, []byte("proxy_ssl_trusted_certificate")) {
3189+
t.Error("want no SSL trusted certificate directive in generated template")
3190+
}
3191+
}

0 commit comments

Comments
 (0)