|
1 | 1 | # Hardened Mode |
2 | 2 |
|
3 | | -```{note} |
4 | | -This page is planned but not yet written |
| 3 | +In a zero-trust network, no service is trusted by default and every request must be explicitly authorized before it is allowed through. Hardened mode brings this principle to the service mesh by enforcing a deny-by-default security posture: all inbound traffic to services on the mesh is denied unless an authorization policy explicitly allows it. |
| 4 | + |
| 5 | +## Why hardened mode is needed |
| 6 | + |
| 7 | +By default, Istio ambient only enforces authorization on workloads that are explicitly targeted by an `AuthorizationPolicy`. Any workload without a policy applied to it will accept all inbound traffic. This means: |
| 8 | + |
| 9 | +* if a service on the mesh has no policy targeting it, the **ztunnel** will allow any traffic through to that service |
| 10 | +* when [`auto-allow-waypoint-policy`](https://charmhub.io/istio-k8s/configurations#auto-allow-waypoint-policy) is enabled (the default), if no policy targets a service at the **waypoint**, any service can reach it |
| 11 | + |
| 12 | +In practice, this leaves services that haven't been explicitly locked down wide open. Hardened mode closes this gap by ensuring that all traffic is denied by default across the entire mesh. |
| 13 | + |
| 14 | +## How it works |
| 15 | + |
| 16 | +Enabling hardened mode on the [`istio-k8s`](https://charmhub.io/istio-k8s) charm creates two global **allow-nothing** `AuthorizationPolicies`. |
| 17 | + |
| 18 | +### Why allow-nothing instead of deny-all? |
| 19 | + |
| 20 | +The distinction matters because of how Istio ambient [evaluates authorization policies](https://istio.io/latest/docs/concepts/security/#authorization-policy-precedence). Istio ambient processes policies in this order: `CUSTOM` then `DENY` then `ALLOW`. A `DENY` policy always takes precedence. Once traffic matches a `DENY` rule, it is rejected regardless of any `ALLOW` policies. This means a global `DENY`-all policy would lock down the cluster permanently with no way to override it. |
| 21 | + |
| 22 | +An allow-nothing policy works differently. It is an `ALLOW` policy with an empty rule set, meaning it matches no traffic. However, its presence activates Istio ambient's [implicit deny behavior](https://istio.io/latest/docs/concepts/security/#allow-nothing-deny-all-and-allow-all-policy): once at least one `ALLOW` policy exists for a workload, any traffic that does not match an `ALLOW` rule is denied. Other `ALLOW` policies can then selectively open up the specific traffic that should be permitted. This gives us a secure default that can still be overridden by explicit allow rules. |
| 23 | + |
| 24 | +### ztunnel allow-nothing policy |
| 25 | + |
| 26 | +An `AuthorizationPolicy` with an empty spec acts as a global allow-nothing policy. This ensures that any traffic not matched by an explicit `ALLOW` policy is denied at the ztunnel layer: |
| 27 | + |
| 28 | +```yaml |
| 29 | +apiVersion: security.istio.io/v1 |
| 30 | +kind: AuthorizationPolicy |
| 31 | +metadata: |
| 32 | + name: istio-k8s-istio-system-policy-global-allow-nothing-ztunnel |
| 33 | + namespace: istio-system |
| 34 | +spec: {} |
| 35 | +``` |
| 36 | +
|
| 37 | +### Waypoint allow-nothing policy |
| 38 | +
|
| 39 | +A similar policy targets the `istio-waypoint` `GatewayClass`, locking down all traffic that passes through the waypoint: |
| 40 | + |
| 41 | +```yaml |
| 42 | +apiVersion: security.istio.io/v1 |
| 43 | +kind: AuthorizationPolicy |
| 44 | +metadata: |
| 45 | + name: istio-k8s-istio-system-policy-global-allow-nothing-waypoint |
| 46 | + namespace: istio-system |
| 47 | +spec: |
| 48 | + targetRefs: |
| 49 | + - kind: GatewayClass |
| 50 | + group: gateway.networking.k8s.io |
| 51 | + name: istio-waypoint |
| 52 | +``` |
| 53 | + |
| 54 | +Together, these two policies ensure that no service-to-service communication is allowed anywhere on the mesh unless an explicit `ALLOW` policy exists for it. See [Traffic authorization](./traffic-authorization.md) for how these allow policies are created by the beacon charm. |
| 55 | + |
| 56 | +## Effect on ingress traffic |
| 57 | + |
| 58 | +Hardened mode also blocks external traffic from reaching the Istio ingress gateway, since the global deny applies to all inbound traffic including traffic arriving from a `LoadBalancer`. |
| 59 | + |
| 60 | +To handle this, the [`istio-ingress-k8s`](https://charmhub.io/istio-ingress-k8s) charm automatically creates an `ALLOW` policy that permits external traffic to reach the gateway: |
| 61 | + |
| 62 | +```yaml |
| 63 | +apiVersion: security.istio.io/v1 |
| 64 | +kind: AuthorizationPolicy |
| 65 | +metadata: |
| 66 | + name: istio-ingress-k8s-istio-system-external-traffic |
| 67 | + namespace: istio-system |
| 68 | +spec: |
| 69 | + action: ALLOW |
| 70 | + targetRefs: |
| 71 | + - kind: Gateway |
| 72 | + group: gateway.networking.k8s.io |
| 73 | + name: istio-ingress-k8s |
| 74 | + rules: |
| 75 | + - from: |
| 76 | + - source: |
| 77 | + ipBlocks: |
| 78 | + - "0.0.0.0/0" |
5 | 79 | ``` |
6 | | -```{note} |
7 | | -This should be written for general service mesh charms, not just istio |
8 | | -``` |
| 80 | + |
| 81 | +By default, this allows traffic from any source IP (`0.0.0.0/0`) because an ingressed application is typically meant to be publicly accessible. To restrict access to specific source IPs or CIDR ranges, use the `external-traffic-policy-cidrs` configuration option on [`istio-ingress-k8s`](https://charmhub.io/istio-ingress-k8s/configurations). |
| 82 | + |
| 83 | +## Enabling hardened mode |
| 84 | + |
| 85 | +To enable hardened mode, set the `hardened-mode` configuration option on the `istio-k8s` charm: |
| 86 | + |
| 87 | +```bash |
| 88 | +juju config istio-k8s hardened-mode=true |
| 89 | +``` |
| 90 | + |
| 91 | +For all available configuration options, see the [istio-k8s](https://charmhub.io/istio-k8s/configurations) and [istio-ingress-k8s](https://charmhub.io/istio-ingress-k8s/configurations) charm configuration pages. |
0 commit comments