Deploy a Policy for access control

This topic describes how to use F5 NGINX Ingress Controller to apply and update a Policy for access control. You can use access control policies with VirtualServer custom resources or with Ingress resources using the nginx.org/policies annotation.


Before you begin

You should have a working NGINX Ingress Controller instance.

For ease of use in shell commands, set the following shell variables:

  1. The public IP address for your NGINX Ingress Controller instance.
IC_IP=<ip-address>
  1. The HTTP port of the same instance.
IC_HTTP_PORT=<port number>
  1. The HTTPS port of the same instance (used for the Ingress resource example).
IC_HTTPS_PORT=<port number>

Use access control with VirtualServer resources

Deploy the example application

Create the file webapp.yaml with the following contents:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: nginxdemos/nginx-hello:plain-text
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: webapp-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: webapp

Apply it using kubectl:

kubectl apply -f webapp.yaml

Deploy a Policy to create a deny rule

Create a file named access-control-policy-deny.yaml. The highlighted deny field will be used by the example application, and should be changed to the subnet of your machine.

yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: webapp-policy
spec:
  accessControl:
    deny:
    - 10.0.0.0/8

Apply the policy:

kubectl apply -f access-control-policy-deny.yaml

Configure load balancing

Create a file named virtual-server.yaml for the VirtualServer resource. The policies field references the access control Policy created in the previous section.

yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: webapp
spec:
  host: webapp.example.com
  policies:
  - name: webapp-policy
  upstreams:
  - name: webapp
    service: webapp-svc
    port: 80
  routes:
  - path: /
    action:
      pass: webapp

Apply the policy:

kubectl apply -f virtual-server.yaml

Test the example application

Use curl to attempt to access the application:

curl --resolve webapp.example.com:$IC_HTTP_PORT:$IC_IP http://webapp.example.com:$IC_HTTP_PORT
text
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

The 403 response is expected, successfully blocking your machine.


Update the Policy to create an allow rule

Update the Policy with the file access-control-policy-allow.yaml, setting the allow field to the subnet of your machine.

yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: webapp-policy
spec:
  accessControl:
    allow:
    - 10.0.0.0/8

Apply the Policy:

kubectl apply -f access-control-policy-allow.yaml

Verify the Policy update

Attempt to access the application again:

curl --resolve webapp.example.com:$IC_HTTP_PORT:$IC_IP http://webapp.example.com:$IC_HTTP_PORT
text
Server address: 10.64.0.13:8080
Server name: webapp-5cbbc7bd78-wf85w

The successful response demonstrates that the policy has been updated.


Use access control with Ingress resources

You can also apply access control policies to standard Kubernetes Ingress resources using the nginx.org/policies annotation. This section walks through a complete example.

Deploy the cafe application

Create the file cafe.yaml with the following contents:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: coffee
spec:
  replicas: 2
  selector:
    matchLabels:
      app: coffee
  template:
    metadata:
      labels:
        app: coffee
    spec:
      containers:
      - name: coffee
        image: nginxdemos/nginx-hello:plain-text
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: coffee-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: coffee
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tea
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tea
  template:
    metadata:
      labels:
        app: tea
    spec:
      containers:
      - name: tea
        image: nginxdemos/nginx-hello:plain-text
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: tea-svc
  labels:
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: tea

Apply it using kubectl:

kubectl apply -f cafe.yaml

Configure NGINX to use the X-Real-IP header

Create the file nginx-config.yaml to configure NGINX to trust the X-Real-IP header. This ensures the access control policy uses the client IP provided in that header.

yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-config
data:
  set-real-ip-from: "0.0.0.0/0,::/0"
  real-ip-header: "X-Real-IP"

Apply the ConfigMap:

kubectl apply -f nginx-config.yaml

Deploy a Policy to create an allow rule

Create a file named access-control-policy-allow.yaml. The highlighted allow field permits traffic from the 10.0.0.0/8 CIDR range and blocks all other addresses.

yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: webapp-policy
spec:
  accessControl:
    allow:
      - 10.0.0.0/8

Apply the policy:

kubectl apply -f access-control-policy-allow.yaml

Create the Ingress resource

Create a file named cafe-ingress.yaml for the Ingress resource. The highlighted nginx.org/policies annotation references the access control Policy created in the previous step.

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cafe-ingress
  annotations:
    nginx.org/policies: "webapp-policy"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - cafe.example.com
    secretName: tls-secret
  rules:
  - host: cafe.example.com
    http:
      paths:
      - path: /tea
        pathType: Prefix
        backend:
          service:
            name: tea-svc
            port:
              number: 80
      - path: /coffee
        pathType: Prefix
        backend:
          service:
            name: coffee-svc
            port:
              number: 80

Apply the Ingress:

kubectl apply -f cafe-ingress.yaml

Test the allow policy

  1. Send a request with an IP in the allowed 10.0.0.0/8 range using the X-Real-IP header:

    curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure -H "X-Real-IP: 10.0.0.1"
    text
    Server address: 10.244.0.6:8080
    Server name: coffee-7586895968-r26zn
    ...

    The request succeeds because 10.0.0.1 is in the allowed range.

  2. Send a request with an IP outside the allowed range:

    curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure -H "X-Real-IP: 192.168.1.1"
    text
    <html>
    <head><title>403 Forbidden</title></head>
    <body>
    <center><h1>403 Forbidden</h1></center>
    </body>
    </html>

    The 403 response confirms that NGINX blocks clients outside the allowed range.

Update the Policy to create a deny rule

Update the Policy with the file access-control-policy-deny.yaml, which denies traffic from the 10.0.0.0/8 CIDR range and allows all other addresses.

yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: webapp-policy
spec:
  accessControl:
    deny:
      - 10.0.0.0/8

Apply the updated Policy:

kubectl apply -f access-control-policy-deny.yaml

The Ingress resource picks up the change automatically because the policy name (webapp-policy) stays the same.

Verify the deny policy

  1. Send a request with an IP in the now-denied 10.0.0.0/8 range:

    curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure -H "X-Real-IP: 10.0.0.1"
    text
    <html>
    <head><title>403 Forbidden</title></head>
    <body>
    <center><h1>403 Forbidden</h1></center>
    </body>
    </html>

    The same IP that was previously allowed is now rejected.

  2. Send a request with an IP outside the denied range:

    curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure -H "X-Real-IP: 192.168.1.1"
    text
    Server address: 10.244.0.6:8080
    Server name: coffee-7586895968-r26zn
    ...

    Clients outside the denied range are now allowed through.