> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Authorizaton

> Tutorial demonstrating Istio authorization, enabling mTLS, creating ALLOW and DENY AuthorizationPolicy rules, scoping denies, and testing HTTP and TCP access with examples and best practices.

In this lesson we cover authorization in Istio. Authentication (mTLS) verifies workload identity and secures traffic between services. Authorization decides what an authenticated workload is allowed to do.

What you'll learn:

* Deploy a sample workload (httpbin)
* Enforce cluster-wide strict mTLS
* Create AuthorizationPolicy objects to ALLOW and DENY traffic
* Understand how DENY and ALLOW interact (DENY has precedence)

Prerequisites:

* An Istio control plane installed and running
* `kubectl` and `istioctl` available on your control host
* Injection enabled for namespaces you test

## Deploy the httpbin sample

Apply the httpbin sample workload:

```bash theme={null}
root@controlplane ~ ➜ kubectl apply -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/httpbin/httpbin.yaml
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created

root@controlplane ~ ➜
```

Create and label a test namespace with sidecar injection:

```bash theme={null}
root@controlplane ~ ➜ kubectl create ns test
namespace/test created

root@controlplane ~ ➜ kubectl label ns test istio-injection=enabled
namespace/test labeled

root@controlplane ~ ➜ istioctl analyze -n test
✔ No validation issues found when analyzing namespace: test.

root@controlplane ~ ➜
```

Create a test pod (nginx) in the `test` namespace so you can exec and curl from it:

```bash theme={null}
root@controlplane ~ ➜ kubectl run test --image=nginx -n test
pod/test created

root@controlplane ~ ➜ kubectl get pods -n test
NAME    READY   STATUS    RESTARTS   AGE
test    2/2     Running   0          7s
```

Verify the httpbin service and curl it from the `test` pod:

```bash theme={null}
root@controlplane ~ ➜ kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
httpbin      ClusterIP   10.96.105.55    <none>        8000/TCP   90s
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    39m

root@controlplane ~ ➜ kubectl exec -ti test -n test -- curl --head http://httpbin.default.svc:8000
HTTP/1.1 200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
content-security-policy: default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' camo.githubusercontent.com
content-type: text/html; charset=utf-8
date: Tue, 15 Apr 2025 18:49:11 GMT
x-envoy-upstream-service-time: 11
server: envoy
transfer-encoding: chunked

root@controlplane ~ ➜
```

At this point everything is accessible because no AuthorizationPolicy objects are in place.

## Enable strict mTLS globally

Create a cluster-wide PeerAuthentication in the `istio-system` namespace to enforce strict mTLS:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT
```

Apply it:

```bash theme={null}
root@controlplane ~ ➜ kubectl apply -f peer_auth_global.yaml
peerauthentication.security.istio.io/default created

root@controlplane ~ ➜ kubectl get peerauthentications.security.istio.io -A
NAMESPACE     NAME      MODE    AGE
istio-system  default   STRICT  17s

root@controlplane ~ ➜
```

Because both `default` and `test` namespaces have sidecar injection enabled, TLS will be used transparently between injected proxies.

## Allow traffic to httpbin (AuthorizationPolicy ALLOW)

Create an AuthorizationPolicy that permits GET and HEAD requests to httpbin from the `test` namespace:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: httpbin-auth-policy
spec:
  action: ALLOW
  rules:
  - from:
    - source:
        namespaces: ["test"]
    to:
    - operation:
        methods: ["GET", "HEAD"]
```

Apply the policy:

```bash theme={null}
root@controlplane ~ ➜ kubectl apply -f auth_policy.yaml
authorizationpolicy.security.istio.io/httpbin-auth-policy created
```

<Callout icon="lightbulb" color="#1CB2FE">
  HTTP method matching in AuthorizationPolicy is exact. If your policy allows only `GET` and you send a `HEAD` request, it will be denied. Include every HTTP method you expect to use.
</Callout>

## Allow another namespace (app)

Create an `app` namespace and enable injection so you can test from a different source namespace:

```bash theme={null}
root@controlplane ~ ➜ kubectl create ns app
namespace/app created

root@controlplane ~ ➜ kubectl label ns app istio-injection=enabled
namespace/app labeled

root@controlplane ~ ➜ kubectl get ns --show-labels
NAME      STATUS   AGE     LABELS
app       Active   18s     istio-injection=enabled,kubernetes.io/metadata.name=app
default   Active   43m     istio-injection=enabled,kubernetes.io/metadata.name=default
test      Active   5m58s   istio-injection=enabled,kubernetes.io/metadata.name=test
```

Create a pod in `app` and test access:

```bash theme={null}
root@controlplane ~ ➜ kubectl run test --image=nginx -n app
pod/test created

root@controlplane ~ ➜ kubectl get pods -n app
NAME    READY   STATUS    RESTARTS   AGE
test    2/2     Running   0          7s
```

If you exec into the `app` pod and curl httpbin, you'll get a 403 because your ALLOW policy currently permits only the `test` namespace:

```bash theme={null}
root@controlplane ~ ➜ kubectl exec -ti test -n app -- curl --head http://httpbin.default.svc:8000
HTTP/1.1 403 Forbidden
content-length: 19
content-type: text/plain
date: Tue, 15 Apr 2025 18:53:47 GMT
server: envoy
x-envoy-upstream-service-time: 7
```

To allow traffic from both namespaces, update the ALLOW policy's `namespaces` list to include `["test", "app"]`, then re-apply.

## Deny specific paths (AuthorizationPolicy DENY)

You can DENY specific request paths. Example: deny any request to `/delay`:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: httpbin-auth-deny-policy
spec:
  action: DENY
  rules:
  - to:
    - operation:
        paths: ["/delay"]
```

Apply the deny policy:

```bash theme={null}
root@controlplane ~ ➜ kubectl apply -f auth_policy_deny.yaml
authorizationpolicy.security.istio.io/httpbin-auth-deny-policy created
```

<Callout icon="warning" color="#FF6B6B">
  DENY rules are evaluated before ALLOW rules. If a DENY matches a request, it will block the request even if an ALLOW policy would otherwise permit it. Order and scoping matter — scope DENY policies carefully.
</Callout>

## Deny all and scoped denies

A common (but often risky) pattern is to create a namespace- or workload-scoped deny-all, then add explicit ALLOWs. Example namespace-scoped deny-all for `default`:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: deny-all
  namespace: default
spec:
  action: DENY
  rules:
  - {}
```

Apply it:

```bash theme={null}
root@controlplane ~ ➜ kubectl apply -f auth_deny_all.yaml
authorizationpolicy.security.istio.io/deny-all created

root@controlplane ~ ➜ kubectl get authorizationpolicies.security.istio.io -A
NAMESPACE   NAME                    ACTION   AGE
default     deny-all                DENY     7s
default     httpbin-auth-policy     ALLOW    28m
```

Because the deny-all policy matches everything in its scope, it will override ALLOWs unless you scope DENY policies narrowly. To remove the deny-all:

```bash theme={null}
root@controlplane ~ ➜ kubectl delete -f auth_deny_all.yaml
authorizationpolicy.security.istio.io "deny-all" deleted
```

## Scoped workload deny

You can target specific workloads using `selector.matchLabels`. Example: deny all traffic to a workload with `app: productpage`:

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: deny-all-product
spec:
  selector:
    matchLabels:
      app: productpage
  action: DENY
  rules:
  - {}
```

Apply it:

```bash theme={null}
root@controlplane ~ ➜ kubectl apply -f auth_deny_product.yaml
authorizationpolicy.security.istio.io/deny-all-product created
```

Before applying the deny, `productpage` is reachable:

```bash theme={null}
root@controlplane ~ ➜ kubectl exec -ti test -n app -- curl --head http://productpage.default.svc:9080/productpage
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 5179
server: envoy
date: Tue, 15 Apr 2025 19:30:56 GMT
x-envoy-upstream-service-time: 750
```

After applying the deny, requests are forbidden:

```bash theme={null}
root@controlplane ~ ➜ kubectl exec -ti test -n app -- curl --head http://productpage.default.svc:9080/productpage
HTTP/1.1 403 Forbidden
content-length: 19
content-type: text/plain
date: Tue, 15 Apr 2025 19:30:56 GMT
server: envoy
x-envoy-upstream-service-time: 7
```

## Quick reference table

| Concept            | Where to match          | Examples                                                    |
| ------------------ | ----------------------- | ----------------------------------------------------------- |
| Source matching    | `from.source`           | `namespaces`, `principals`, `requestPrincipals`, `ipBlocks` |
| Operation matching | `to.operation`          | `methods`, `paths`, `ports`                                 |
| Workload scoping   | `selector.matchLabels`  | Target a specific workload or app                           |
| Actions            | `spec.action`           | `ALLOW`, `DENY`, `AUDIT`, `CUSTOM`                          |
| Precedence         | Policy evaluation order | `DENY` rules evaluated before `ALLOW` rules                 |

See the Istio docs for the full set of attributes and examples:

* [Istio AuthorizationPolicy reference](https://istio.io/latest/docs/reference/config/security/authorization-policy/)

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/zDpSzOByf0QVxNkX/images/Prep-Course-Istio-Certified-Associate-ICA-Certification/Securing-Workloads/Demo-Authorizaton/istio-authorization-policy-custom-deny-allow.jpg?fit=max&auto=format&n=zDpSzOByf0QVxNkX&q=85&s=bea7e94c30523c6b39acd8da8d96514f" alt="A screenshot of the Istio documentation page titled &#x22;Authorization Policy,&#x22; showing explanatory text about CUSTOM, DENY, and ALLOW actions and a numbered rules list. The page has the Istio logo at the top, site navigation across the header, a left-side table of contents, and a &#x22;Try Istio&#x22; button." width="1920" height="1080" data-path="images/Prep-Course-Istio-Certified-Associate-ICA-Certification/Securing-Workloads/Demo-Authorizaton/istio-authorization-policy-custom-deny-allow.jpg" />
</Frame>

## Useful TCP example

AuthorizationPolicy can also be used for TCP by matching `ports` (no HTTP attributes):

```yaml theme={null}
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: tcp-policy
  namespace: foo
spec:
  selector:
    matchLabels:
      app: tcp-echo
  action: ALLOW
  rules:
  - to:
    - operation:
        ports: ["9000", "9001"]
```

Test connecting to TCP ports (from the Istio docs):

```bash theme={null}
$ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" \
  -c curl -n foo -- sh -c \
  'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo
hello port 9000
connection succeeded
```

## Practice checklist

* Allow specific namespaces or service accounts when possible (least privilege).
* Allow specific HTTP methods, paths, and ports — remember exact matching.
* Use scoped DENY policies rather than broad deny-all when possible.
* Test from pods in different namespaces to validate source-based rules.
* For TCP services, use `ports` in AuthorizationPolicy and avoid HTTP attributes.

## Summary

* Authentication (mTLS) verifies identity; Authorization decides what that identity can do.
* AuthorizationPolicy rules can match on source (namespace, principal, service account, labels), operation (methods, paths, ports), and workload selectors.
* Actions include ALLOW, DENY, AUDIT, and CUSTOM. DENY takes precedence over ALLOW — scope DENY policies carefully.
* Use `selector.matchLabels` to target specific workloads; use `ports` for TCP rules.

Further reading:

* [Istio AuthorizationPolicy reference](https://istio.io/latest/docs/reference/config/security/authorization-policy/)
* [Istio Security Concepts](https://istio.io/latest/docs/concepts/security/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/17ba1cac-61f4-48b6-b354-c2c735f5791d/lesson/a1c6e62c-1037-47a6-8ed9-d7cda24ad89b" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/17ba1cac-61f4-48b6-b354-c2c735f5791d/lesson/4720e4dc-a793-4ae5-945d-1f639d5b6612" />
</CardGroup>
