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

# Traffic Encryption (mTLS)

> Encrypt traffic between workloads and verify their identities without managing certificates

Traffic encryption protects data moving between workloads in your environment. It uses mutual TLS (mTLS), which
encrypts the connection and verifies the identity at both ends. Ryvn issues and rotates the certificates; your
workloads keep their existing addresses and need no code changes.

> [!NOTE]
>
> Traffic encryption is in preview and isn't enabled by default. [Contact Ryvn support](https://ryvn.ai/docs/support/contact-us.md) to enable it for
> your environment.

## When to use it

* Protect sensitive data exchanged between services, such as an API calling a worker or a database in the environment.
* Meet customer requirements for encrypted, authenticated connections, even on private networks.

## How it works

You enable mTLS per [namespace](https://ryvn.ai/docs/guides/namespaces.md). Workloads in enabled namespaces communicate over encrypted TCP
connections, within or across those namespaces. Running workloads start using mTLS without a restart.

mTLS verifies workload identity. Use [network policy](https://ryvn.ai/docs/networking/network-policy.md) to control which destinations a
workload can reach. Keep using [HTTPS for public endpoints](https://ryvn.ai/docs/networking/tls-certificates.md) and TLS for connections to
external services.

## Enable mTLS

Add the `networking.ryvn.app/mtls` label to the namespace in the environment's
[`namespaces` list](https://ryvn.ai/docs/guides/namespaces#declaring-namespaces-on-the-environment). Start with `permissive` on every
namespace whose workloads need to communicate:

```yaml theme={null}
kind: Environment
metadata:
  name: prod-us
spec:
  namespaces:
    - name: prod-us
      labels:
        networking.ryvn.app/mtls: permissive
```

| Mode | Incoming connections |
| - | - |
| `permissive` | Accepts both mTLS and unencrypted connections. |
| `strict` | Requires mTLS; rejects connections without it. |

Switch a namespace to `strict` only after all workloads that call it have mTLS enabled. Callers left outside mTLS
will lose access to that namespace.

## Check that traffic is encrypted

To confirm that a caller's traffic is encrypted, run a short check from the caller's namespace. It starts a temporary
pod, records the traffic leaving that pod, and sends one request to the destination with a marker header. If the marker
appears in the recording, the request crossed the network as plain text.

The check needs a permission set that can create pods and attach to them in the caller's namespace:

```yaml theme={null}
kind: PermissionSet
metadata:
  name: mtls-check
spec:
  kubernetes:
    permissions:
      - scope:
          namespaceNames: [prod-us]
        rules:
          - apiGroups: [""]
            resources: [pods]
            verbs: [get, list, watch, create, delete]
          - apiGroups: [""]
            resources: [pods/attach]
            verbs: [get, create]
          - apiGroups: [""]
            resources: [pods/log]
            verbs: [get]
```

Open a [Ryvn Connect](https://ryvn.ai/docs/guides/jit-kubectl-access.md) session with that set and run the check. Replace `prod-us` with the
caller's namespace and the URL with the address the caller uses:

```bash theme={null}
kubectl run mtls-check -n prod-us -i --rm --restart=Never --pod-running-timeout=5m --image=nicolaka/netshoot:v0.14 -- sh -c '
  tcpdump -i eth0 -n -A -l > /tmp/wire 2>/dev/null &
  sleep 2
  curl -sS -o /dev/null -H "X-Check: marker-7f3a" http://worker:8080/ || exit 1
  sleep 1; kill $!; wait
  [ -s /tmp/wire ] || { echo "Nothing captured"; exit 1; }
  grep -q marker-7f3a /tmp/wire && echo "NOT encrypted: marker readable on the network" || echo "Encrypted: marker not readable on the network"'
```

`Encrypted` means the destination answered and the marker never appeared on the network. `NOT encrypted` means the
caller's or the destination's namespace doesn't have mTLS enabled. A `curl` error means the request got no answer;
that's also what a caller without mTLS sees when the destination is `strict`. The pod is deleted when the check ends.
