Skip to content

Commit dbba378

Browse files
haywoodshpre-commit-ci[bot]Jim Ryan
authored
Support APIKey authentication (nginx#5580)
* always load njs module Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * accept api key policy yaml Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add njs and nginx config * move js import to http * update schema and allow update after NIC starts Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove hardcoded variable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add updated njs * move js set outside location * use nginx.org/apikey secret type Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * simplify njs * make query params work * clean up template files * add api key secret validation to reject duplicated keys, remove repeated maps in config, remove reject code, add unit tests Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix linting, remove unused structs, update crds and codegen Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * add unit tests, unique map names, add validate apikey location block to conf only if api key policy is used Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * add python tests for vs and vsr * fix dockerfile merge * add wait until pods are ready * update error message Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * test setting same namespace * custom objects * add crd print * add unit tests Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * Add example readme for apikey auth policy * clean up * further cleanup * clean up test * add unit tests, clean up code Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * remove logs, refactor, add tests Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * remove logs Signed-off-by: Haywood Shannon <[email protected]> Signed-off-by: Haywood Shannon <[email protected]> * add api key auth to telemetry --------- Signed-off-by: Haywood Shannon <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jim Ryan <[email protected]>
1 parent 8752533 commit dbba378

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2563
-52
lines changed

build/scripts/common.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ if [ -z "${BUILD_OS##*plus*}" ]; then
1010
PLUS=-plus
1111
fi
1212

13+
mkdir -p /etc/nginx/njs/ && cp -a /tmp/internal/configs/njs/* /etc/nginx/njs/
1314
mkdir -p /var/lib/nginx /etc/nginx/secrets /etc/nginx/stream-conf.d
1415
setcap 'cap_net_bind_service=+eip' /usr/sbin/nginx 'cap_net_bind_service=+eip' /usr/sbin/nginx-debug
1516
setcap -v 'cap_net_bind_service=+eip' /usr/sbin/nginx 'cap_net_bind_service=+eip' /usr/sbin/nginx-debug

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,25 @@ spec:
6767
type: string
6868
type: array
6969
type: object
70+
apiKey:
71+
description: APIKey defines an API Key policy.
72+
properties:
73+
clientSecret:
74+
type: string
75+
suppliedIn:
76+
description: SuppliedIn defines the locations API Key should be
77+
supplied in.
78+
properties:
79+
header:
80+
items:
81+
type: string
82+
type: array
83+
query:
84+
items:
85+
type: string
86+
type: array
87+
type: object
88+
type: object
7089
basicAuth:
7190
description: |-
7291
BasicAuth holds HTTP Basic authentication configuration

deploy/crds.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,25 @@ spec:
269269
type: string
270270
type: array
271271
type: object
272+
apiKey:
273+
description: APIKey defines an API Key policy.
274+
properties:
275+
clientSecret:
276+
type: string
277+
suppliedIn:
278+
description: SuppliedIn defines the locations API Key should be
279+
supplied in.
280+
properties:
281+
header:
282+
items:
283+
type: string
284+
type: array
285+
query:
286+
items:
287+
type: string
288+
type: array
289+
type: object
290+
type: object
272291
basicAuth:
273292
description: |-
274293
BasicAuth holds HTTP Basic authentication configuration

docs/content/overview/product-telemetry.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ These are the data points collected and reported by NGINX Ingress Controller:
4646
- **IngressAnnotations** List of Ingress annotations managed by NGINX Ingress Controller
4747
- **AccessControlPolicies** Number of AccessControl policies.
4848
- **RateLimitPolicies** Number of RateLimit policies.
49+
- **APIKeyPolicies** Number of API Key Auth policies.
4950
- **JWTAuthPolicies** Number of JWTAuth policies.
5051
- **BasicAuthPolicies** Number of BasicAuth policies.
5152
- **IngressMTLSPolicies** Number of IngressMTLS policies.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# API Key Authentication
2+
3+
NGINX supports authenticating requests with
4+
[ngx_http_auth_request_module](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html). In this example, we deploy
5+
a web application, configure load balancing for it via a VirtualServer, and apply an API Key Auth policy.
6+
7+
## Prerequisites
8+
9+
1. Follow the [installation](https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/)
10+
instructions to deploy the Ingress Controller. In this example we will be using a snippet to turn the policy off on a specific path so ensure that the `enable-snippets` flag is set.
11+
1. Save the public IP address of the Ingress Controller into a shell variable:
12+
13+
```console
14+
IC_IP=XXX.YYY.ZZZ.III
15+
```
16+
17+
1. Save the HTTP port of the Ingress Controller into a shell variable:
18+
19+
```console
20+
IC_HTTP_PORT=<port number>
21+
```
22+
23+
## Step 1 - Deploy a Web Application
24+
25+
Create the application deployment and service:
26+
27+
```console
28+
kubectl apply -f cafe.yaml
29+
```
30+
31+
## Step 2 - Deploy the API Key Auth Secret
32+
33+
Create a secret of type `nginx.org/apikey` with the name `api-key-client-secret` that will be used for authorization on the server level.
34+
35+
This secret will contain a mapping of client IDs to base64 encoded API Keys.
36+
37+
```console
38+
kubectl apply -f api-key-secret.yaml
39+
```
40+
41+
## Step 3 - Deploy the API Key Auth Policy
42+
43+
Create a policy with the name `api-key-policy` that references the secret from the previous step in the clientSecret field.
44+
Provide an array of headers and queries in the header and query fields of the suppliedIn field, indicating where the API key can be sent
45+
46+
```console
47+
kubectl apply -f api-key-policy.yaml
48+
```
49+
50+
## Step 4 - Configure Load Balancing
51+
52+
Create a VirtualServer resource for the web application:
53+
54+
```console
55+
kubectl apply -f cafe-virtual-server.yaml
56+
```
57+
58+
Note that the VirtualServer references the policy `api-key-policy` created in Step 3.
59+
60+
## Step 5 - Test the Configuration
61+
62+
If you attempt to access the application without providing a valid API Key in a expected header or query param for that VirtualServer:
63+
64+
```console
65+
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/
66+
```
67+
68+
```text
69+
<html>
70+
<head><title>401 Authorization Required</title></head>
71+
<body>
72+
<center><h1>401 Authorization Required</h1></center>
73+
<hr><center>nginx/1.21.5</center>
74+
</body>
75+
</html>
76+
```
77+
78+
If you attempt to access the application providing an incorrect API Key in an expected header or query param for that VirtualServer:
79+
80+
```console
81+
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP -H "X-header-name: wrongpassword" http://cafe.example.com:$IC_HTTP_PORT/coffee
82+
```
83+
84+
```text
85+
<html>
86+
<head><title>403 Forbidden</title></head>
87+
<body>
88+
<center><h1>403 Forbidden</h1></center>
89+
<hr><center>nginx/1.27.0</center>
90+
</body>
91+
</html>
92+
```
93+
94+
If you provide a valid API Key in an a header or query defined in the policy, your request will succeed:
95+
96+
```console
97+
curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP -H "X-header-name: password" https://cafe.example.com:$IC_HTTPS_PORT/coffee
98+
```
99+
100+
```text
101+
Server address: 10.244.0.6:8080
102+
Server name: coffee-56b44d4c55-vjwxd
103+
Date: 13/Jun/2024:13:12:17 +0000
104+
URI: /coffee
105+
Request ID: 4feedb3265a0430a1f58831d016e846d
106+
```
107+
108+
If you attempt to access the /tea path, the request will be allowed without an API Key, because the auth_request directive is turned off for that path with a location snippet:
109+
110+
```console
111+
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/tea
112+
```
113+
114+
```text
115+
Server address: 10.244.0.5:8080
116+
Server name: tea-596697966f-dmq7t
117+
Date: 13/Jun/2024:13:16:46 +0000
118+
URI: /tea
119+
Request ID: 26e6d7dd0272eca82f31f33bf90698c9
120+
```
121+
122+
Additionally you can set [error pages](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/#errorpage) to handle the 401 and 403 responses.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: Policy
3+
metadata:
4+
name: api-key-policy
5+
spec:
6+
apiKey:
7+
suppliedIn:
8+
header:
9+
- "X-header-name"
10+
query:
11+
- "queryName"
12+
clientSecret: api-key-client-secret
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: api-key-client-secret
5+
type: nginx.org/apikey
6+
data:
7+
client1: cGFzc3dvcmQ= # password
8+
client2: YW5vdGhlci1wYXNzd29yZA== # another-password
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: cafe-secret
5+
type: kubernetes.io/tls
6+
data:
7+
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
8+
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMlpHVm1WWThLdlVWQ2cxNWhRCmRtTWpkVnhGbldkSHNFYmltRkNaYkp1dmRySkRxeWtJOUw1S0Q1K05SQlAvazJsUkthaTAwYUZHTjY4NE93NUgKYzMzUVlzeFR0bmJEelJjZGdsdEkxNURTN3lxTnVRbWF6b1Z3QndJREFRQUJBb0lCQVFDUFNkU1luUXRTUHlxbApGZlZGcFRPc29PWVJoZjhzSStpYkZ4SU91UmF1V2VoaEp4ZG01Uk9ScEF6bUNMeUw1VmhqdEptZTIyM2dMcncyCk45OUVqVUtiL1ZPbVp1RHNCYzZvQ0Y2UU5SNThkejhjbk9SVGV3Y290c0pSMXBuMWhobG5SNUhxSkpCSmFzazEKWkVuVVFmY1hackw5NGxvOUpIM0UrVXFqbzFGRnM4eHhFOHdvUEJxalpzVjdwUlVaZ0MzTGh4bndMU0V4eUZvNApjeGI5U09HNU9tQUpvelN0Rm9RMkdKT2VzOHJKNXFmZHZ5dGdnOXhiTGFRTC94MGtwUTYyQm9GTUJEZHFPZVBXCktmUDV6WjYvMDcvdnBqNDh5QTFRMzJQem9idWJzQkxkM0tjbjMyamZtMUU3cHJ0V2wrSmVPRmlPem5CUUZKYk4KNHFQVlJ6NWhBb0dCQU50V3l4aE5DU0x1NFArWGdLeWNrbGpKNkY1NjY4Zk5qNUN6Z0ZScUowOXpuMFRsc05ybwpGVExaY3hEcW5SM0hQWU00MkpFUmgySi9xREZaeW5SUW8zY2czb2VpdlVkQlZHWTgrRkkxVzBxZHViL0w5K3l1CmVkT1pUUTVYbUdHcDZyNmpleHltY0ppbS9Pc0IzWm5ZT3BPcmxEN1NQbUJ2ek5MazRNRjZneGJYQW9HQkFNWk8KMHA2SGJCbWNQMHRqRlhmY0tFNzdJbUxtMHNBRzR1SG9VeDBlUGovMnFyblRuT0JCTkU0TXZnRHVUSnp5K2NhVQprOFJxbWRIQ2JIelRlNmZ6WXEvOWl0OHNaNzdLVk4xcWtiSWN1YytSVHhBOW5OaDFUanNSbmU3NFowajFGQ0xrCmhIY3FIMHJpN1BZU0tIVEU4RnZGQ3haWWRidUI4NENtWmlodnhicFJBb0dBSWJqcWFNWVBUWXVrbENkYTVTNzkKWVNGSjFKelplMUtqYS8vdER3MXpGY2dWQ0thMzFqQXdjaXowZi9sU1JxM0hTMUdHR21lemhQVlRpcUxmZVpxYwpSMGlLYmhnYk9jVlZrSkozSzB5QXlLd1BUdW14S0haNnpJbVpTMGMwYW0rUlk5WUdxNVQ3WXJ6cHpjZnZwaU9VCmZmZTNSeUZUN2NmQ21mb09oREN0enVrQ2dZQjMwb0xDMVJMRk9ycW40M3ZDUzUxemM1em9ZNDR1QnpzcHd3WU4KVHd2UC9FeFdNZjNWSnJEakJDSCtULzZzeXNlUGJKRUltbHpNK0l3eXRGcEFOZmlJWEV0LzQ4WGY2ME54OGdXTQp1SHl4Wlp4L05LdER3MFY4dlgxUE9ucTJBNWVpS2ErOGpSQVJZS0pMWU5kZkR1d29seHZHNmJaaGtQaS80RXRUCjNZMThzUUtCZ0h0S2JrKzdsTkpWZXN3WEU1Y1VHNkVEVXNEZS8yVWE3ZlhwN0ZjanFCRW9hcDFMU3crNlRYcDAKWmdybUtFOEFSek00NytFSkhVdmlpcS9udXBFMTVnMGtKVzNzeWhwVTl6WkxPN2x0QjBLSWtPOVpSY21Vam84UQpjcExsSE1BcWJMSjhXWUdKQ2toaVd4eWFsNmhZVHlXWTRjVmtDMHh0VGwvaFVFOUllTktvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: VirtualServer
3+
metadata:
4+
name: cafe
5+
spec:
6+
host: cafe.example.com
7+
tls:
8+
secret: cafe-secret
9+
policies:
10+
- name: api-key-policy
11+
upstreams:
12+
- name: coffee
13+
service: coffee-svc
14+
port: 80
15+
- name: tea
16+
service: tea-svc
17+
port: 80
18+
routes:
19+
- path: /coffee
20+
action:
21+
pass: coffee
22+
- path: /tea
23+
location-snippets: |
24+
auth_request off;
25+
action:
26+
pass: tea
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: coffee
5+
spec:
6+
replicas: 2
7+
selector:
8+
matchLabels:
9+
app: coffee
10+
template:
11+
metadata:
12+
labels:
13+
app: coffee
14+
spec:
15+
containers:
16+
- name: coffee
17+
image: nginxdemos/nginx-hello:plain-text
18+
ports:
19+
- containerPort: 8080
20+
---
21+
apiVersion: v1
22+
kind: Service
23+
metadata:
24+
name: coffee-svc
25+
spec:
26+
ports:
27+
- port: 80
28+
targetPort: 8080
29+
protocol: TCP
30+
name: http
31+
selector:
32+
app: coffee
33+
---
34+
apiVersion: apps/v1
35+
kind: Deployment
36+
metadata:
37+
name: tea
38+
spec:
39+
replicas: 1
40+
selector:
41+
matchLabels:
42+
app: tea
43+
template:
44+
metadata:
45+
labels:
46+
app: tea
47+
spec:
48+
containers:
49+
- name: tea
50+
image: nginxdemos/nginx-hello:plain-text
51+
ports:
52+
- containerPort: 8080
53+
---
54+
apiVersion: v1
55+
kind: Service
56+
metadata:
57+
name: tea-svc
58+
spec:
59+
ports:
60+
- port: 80
61+
targetPort: 8080
62+
protocol: TCP
63+
name: http
64+
selector:
65+
app: tea

0 commit comments

Comments
 (0)