The Pod Certificates Kubernetes Enhancement Proposal has been in progress for over two years, and officially reached Beta in Kubernetes v1.35!
Since then, I have gotten a number of questions about what the impact will be on Istio.
There will likely be no impact; I do not think Pod Certificates are a feature that will make a meaningful difference on service meshes.
Please note this post is not meant to diminish the great work of the Kubernetes SIG Auth team. The KEP specifies pod-to-pod mTLS (i.e. service mesh) as a non-goal, though the feature has evolved to somewhat help service meshes. While the feature may be useful for some areas, this post aims to show why it won't impact Istio much at least.
What are Pod Certificates?
Pod Certificates are a way for Kubernetes to automatically issue TLS certificates to Pods. This mirrors the existing ability to automatically issue service account JWT tokens, but for TLS certificates instead.
Given in a service mesh, each pod will have an associated TLS certificate, this seems like a natural fit.
Lets try it out.
First, we will setup a cluster.
$ cat <<EOF | cfg.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
runtimeConfig:
"certificates.k8s.io/v1beta1/podcertificaterequests": "true"
featureGates:
"PodCertificateRequest": true
EOF
$ kind create cluster --name pcert --config cfg.yaml --image gcr.io/istio-testing/kind-node:v1.35.0
Next, we need a controller that will watch PodCertificateRequests and issue certificates. Presumably, a broader ecosystem will emerge around this, but for now there is pretty much nothing. I found a nice demo controller that I will use for this, but keep in mind this is by no means intended for production use. The image didn't suppose the new Beta version (instead the older Alpha) so I built my own image:
$ curl https://raw.githubusercontent.com/RafPe/pod-certificate-signer/refs/heads/main/manifests/pcs-controller-latest.yaml | sed 's|ghcr.io/rafpe/kubernetes-podcertificate-signer/controller:latest|howardjohn/kubernetes-podcertificate-signer:2|g' | kubectl apply -f -
Finally, we can create a Pod that requests a certificate:
apiVersion: apps/v1
kind: Deployment
metadata:
name: shell
labels:
app: shell
spec:
selector:
matchLabels:
app: shell
template:
metadata:
labels:
app: shell
annotations:
coolcert.example.com/foo-san: "spiffe://cluster.local/ns/default/sa/default"
coolcert.example.com/foo-duration: "1h"
coolcert.example.com/foo-refresh: "45m"
spec:
containers:
- name: shell
image: howardjohn/shell
command:
- sleep
- "infinity"
volumeMounts:
- name: pcr-x509
mountPath: /var/run/pcr-x509
readOnly: true
volumes:
- name: pcr-x509
projected:
defaultMode: 420
sources:
- podCertificate:
keyType: RSA4096
signerName: coolcert.example.com/foo
certificateChainPath: cert.pem
keyPath: key.pem
Now, we can exec into the Pod and see and use our certificate:
$ curl https://... --key /var/run/pcr-x509/key.pem --cert /var/run/pcr-x509/cert.pem
$ head /var/run/pcr-x509/* -n 3
==> /var/run/pcr-x509/cert.pem <==
-----BEGIN CERTIFICATE-----
MIIEpzCCA4+gAwIBAgIRANwyd0RVQ+TM8V78A5DRyL4wDQYJKoZIhvcNAQELBQAw
bjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRYwFAYDVQQHEw1TYW4g
==> /var/run/pcr-x509/key.pem <==
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDyTjbekWq1RVXp
vicuSzctQGNxHVpCSZ6Wwqmq5m7B/ClTaMpjVXmft6yNqC+uugSin0toOXuN2JvY
Dumping the certificate:
Version: 3 (0x2)
Serial Number:
dc:32:77:44:55:43:e4:cc:f1:5e:fc:03:90:d1:c8:be
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=New York, L=San Francisco, O=Example Company, Inc., OU=Engineering
Validity
Not Before: Dec 22 17:36:11 2025 GMT
Not After : Dec 22 18:36:11 2025 GMT
Subject: CN=shell-6d7658b866-nhplk
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Authority Key Identifier:
98:07:72:F1:9F:98:59:5B:2D:06:DD:C4:2B:E3:CD:84:36:5E:4B:6F
X509v3 Subject Alternative Name:
DNS:spiffe://cluster.local/ns/default/sa/default
So, very nice, everything works as expected and we get a certificate automatically issued to our Pod!
Under the hood
When a Pod needs to start, kubelet will create a private key and a PodCertificateRequest resource.
Kubernetes itself does not issue any certificates; it is up to an external controller to watch for these requests and issue certificates.
There is no built-in controller that can be used for application workloads.
In our example above, the signerName: coolcert.example.com/foo is the name of the external controller we deployed.
A PodCertificateRequest looks as follows:
---
apiVersion: certificates.k8s.io/v1beta1
kind: PodCertificateRequest
metadata:
creationTimestamp: '2025-12-22T17:35:56Z'
generateName: req-
name: req-2k2nk
namespace: default
ownerReferences:
- apiVersion: core/v1
kind: Pod
name: shell-7f9b7455b6-km72h
uid: 6c6f64a9-8bd6-4a3d-a011-453548b1eae3
resourceVersion: '2500'
uid: a4866221-55b7-4261-85da-275750e72fff
spec:
maxExpirationSeconds: 86400
nodeName: pcert-control-plane
nodeUID: 6cf52e1e-6f1c-4d79-8957-f40bdf807dbc
pkixPublicKey: MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx7OA2EPxe/izoQPBsX/Ti1KZvtRLfBBjynsoC+fAgD5jsFDK3HIV8gakPAKnAvR5tBj0bDqf2vpPexPWHSof8EkYghmqeFtBW5og/J8iHO9acnOHmfyTYWg9Qt1LyQYJoxd1kt15zhh8x5JOL8rH8rYKwfVaNYbz2UvvCtik6bUMwUdkyoI9DYbAtFKuVMzKyaDYKTbJzXC0b3nkMz62PyfTy0NxSjHmMvGs/xxgLc8fobw7e26nUKEPpuggkOrpBreyWM6X7FnD1m50zlDqhOSUj7ZLIkVK0Dp058FDZmoGkFQeUlnzC2OiyuS1QPd6JAG7lLgcyFMSD3vidfej+VXEG8kRYJCBK87+ky8PKW43VMMS9yC87YRy0BTkWTNBdUBlokMMiTB6Cdb9Dc+xx9byxuBBd9sE9D4R4aDJfcKihsqxLPh2GQlBBK+LmMIn7I5U1Kq3DAv0OYznEmUJk2xRggNJ4Gcdpyq2fnV5KWzmpKMj7IfcTvnXH1ZsHtec+0uNd2XCzU6UjiShZyf2MordVBDZUpqTbt66FthWKVuTbxm2LgzN39qZ4NERvoUtnUm2a+kFk1vJqITg+FKK64swXMGJdXfFXIKo0YISbLtFyy9aJakhIc0T63WH3SU0FL009LnV3sml12P1aXe/3qRd/5Sp5QX4JNHoHfDeBvkCAwEAAQ==
podName: shell-7f9b7455b6-km72h
podUID: 6c6f64a9-8bd6-4a3d-a011-453548b1eae3
proofOfPossession: lzwZ2tiJQtGZ57EL5fvQWj87xb5SaJDAZS0hVGI2RAzSOQ5DeJJfthNIQY+d/mTGNbIgKsCnFoxnuAQn5djC+vmVXfruc2BYKeVjhOe5kA9arEscK+GpyJ/zr5S9jlEiVvZQ2ZhwKfygYkr64oGdHhWjWrYmUFxMwmfy9l6ixPtpRlA/BMSuGG1Fh9mZ7GzZWR0P1fYOg2s9v+X5FiMoFJB0QIMAELLF+tRWzxJoVBoCNwtvR6w4uVHAKv6ECkynEdXpar/gRD+fgwdFqHkjyupmCPDS9T8Xq5mt0QRDU/nbKhZ2rycvjyZBHZApK+/tiiAOEBDSdUMHekbTWzGB+Ufesc5mIXZF+olLJoP+McbfmiG5ReER6WaiJfdjpTqt8eZXJ0shR8EnFMwZIIXCAox773wlpJqlDPX7nYzrTiQ83N8doIeMq1JelCSVF1yx95IaQbWdT+gSCQIyr5RfGWRf+h2+g01uiZP6mokakJQsKlUnHLowYP32BSH9od4BkunpO32ZNx7HRv+o+uLNqX+N3yDaUKCzGpYw6yFRhDzovfTV8JiJ+JHgM/RhMg57ymMGKhy5D+sEZwPPUwvkaLvWU4gbBMO0BoWIT3jbYm2/goEbsg6+QYHW3zbCg3KCPofuqdtO40vurtZqMDAnQnvXfypWCPTTx+YOaAdsR2Y=
serviceAccountName: default
serviceAccountUID: 386be21d-c7cc-44c5-b755-a0ee930079c1
signerName: coolcert.example.com/foo
status:
beginRefreshAt: '2025-12-22T17:49:56Z'
certificateChain: |-
-----BEGIN CERTIFICATE-----
MIIEpzCCA4+gAwIBAgIRALgFCmVZEQ/AF7vgfI3tBacwDQYJKoZIhvcNAQELBQAw
bjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRYwFAYDVQQHEw1TYW4g
RnJhbmNpc2NvMR4wHAYDVQQKExVFeGFtcGxlIENvbXBhbnksIEluYy4xFDASBgNV
BAsTC0VuZ2luZWVyaW5nMB4XDTI1MTIyMjE3MzQ1NloXDTI1MTIyMjE4MzQ1Nlow
ITEfMB0GA1UEAxMWc2hlbGwtN2Y5Yjc0NTViNi1rbTcyaDCCAiIwDQYJKoZIhvcN
AQEBBQADggIPADCCAgoCggIBAMezgNhD8Xv4s6EDwbF/04tSmb7US3wQY8p7KAvn
wIA+Y7BQytxyFfIGpDwCpwL0ebQY9Gw6n9r6T3sT1h0qH/BJGIIZqnhbQVuaIPyf
IhzvWnJzh5n8k2FoPULdS8kGCaMXdZLdec4YfMeSTi/Kx/K2CsH1WjWG89lL7wrY
pOm1DMFHZMqCPQ2GwLRSrlTMysmg2Ck2yc1wtG955DM+tj8n08tDcUox5jLxrP8c
YC3PH6G8O3tup1ChD6boIJDq6Qa3sljOl+xZw9ZudM5Q6oTklI+2SyJFStA6dOfB
Q2ZqBpBUHlJZ8wtjosrktUD3eiQBu5S4HMhTEg974nX3o/lVxBvJEWCQgSvO/pMv
DyluN1TDEvcgvO2EctAU5FkzQXVAZaJDDIkwegnW/Q3PscfW8sbgQXfbBPQ+EeGg
yX3CoobKsSz4dhkJQQSvi5jCJ+yOVNSqtwwL9DmM5xJlCZNsUYIDSeBnHacqtn51
eSls5qSjI+yH3E751x9WbB7XnPtLjXdlws1OlI4koWcn9jKK3VQQ2VKak27euhbY
Vilbk28Zti4Mzd/ameDREb6FLZ1JtmvpBZNbyaiE4PhSiuuLMFzBiXV3xVyCqNGC
Emy7RcsvWiWpISHNE+t1h90lNBS9NPS51d7Jpddj9Wl3v96kXf+UqeUF+CTR6B3w
3gb5AgMBAAGjgYwwgYkwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
BwMBBggrBgEFBQcDAjAfBgNVHSMEGDAWgBSYB3Lxn5hZWy0G3cQr482ENl5LbzA3
BgNVHREEMDAugixzcGlmZmU6Ly9jbHVzdGVyLmxvY2FsL25zL2RlZmF1bHQvc2Ev
ZGVmYXVsdDANBgkqhkiG9w0BAQsFAAOCAQEAQSf0ndlRCnc2jus+2i1sZyM0xMlR
VDpiMVVWNH66NVSHZwHb4DqcmifpIDil5Q2LMD9up9LU4xeh68ZlbtqeWJBUvXAd
SbyUp5+i4wIY9x2sfJ3Ea7R/pVhE6BxgSXlO6Ck0jvCVE1auKFipJXhO03hU8kW4
lpPImABaGfdJY5/FOQHKBIJJ8zDP7ERzz4U6nqGjmjGd6BvUkFvU4Ab545nJRJ01
v5G//guP9NB/6fVDxWO4Yk7cchV3VvMuoeVkZNwJS9+2iAAmuKpD+uD+MytqjOTh
pZTbl9U5H154axXkarwXBYWJXmDjcjlh9ftlbfocjixl9UcguDPTl6NmRw==
-----END CERTIFICATE-----
conditions:
- lastTransitionTime: '2025-12-22T17:35:56Z'
message: Certificate successfully issued
reason: CertificateIssued
status: 'True'
type: Issued
notAfter: '2025-12-22T18:34:56Z'
notBefore: '2025-12-22T17:34:56Z'
So we get verifiable proof of the Node, Service Account, and Namespace of the Pod requesting a certificate in the spec.
The signer (which again, is completely custom and external to Kubernetes) can then issue a certificate and set it in the status.certificateChain.
Kubelet then handles ensuring the certificate is mounted into the Pod filesystem.
Note: In my example deployment, I explicitly set the
spiffeidentity as an annotation. In a real world service mesh implementation, this would very likely be automatically detected.
Future enhancements
In the future, the API server itself may introduce some built-in signers. Depending on what these signers can do, this may make some meaningful benefits over the current state.
For example, if the API server itself can fully implement a CA, that would free up Istio itself from acting as the CA. While the same number of components would still be needed (Istio control plane still needs to run, just not as a CA), there would be fewer CAs to manage.
Why Pod Certificates won't help your service mesh
Pod Certificates provides a common pattern to interface between something that wants a certificate (a Pod/service mesh) and the thing signing certificates (a CA). However, because this has been required already for 10+ years, the ecosystem has already adapted to solve this problem in other ways.
In the Istio space, there are quite a few ways to get certificates. The most common, covering >99% of use cases:
- Istio builtin CA. This is the default, easiest to use, and requires zero external dependencies. The Istio proxy generates an in-memory private key, and requests certificate signing over an internal (gRPC) API with the Istio control plane.
- cert-manager's
istio-csrproject implements the same API and allows simply plugging in cert-manager, which supports a huge number of backing CAs.
- cert-manager's
- SPIRE, which Istio can request a certificate from using the SPIFFE Workload API. Unlike the Istio builtin CA flow, SPIRE sends the private key and signed certificate directly (rather than just signing the certificate), which is advantageous for some use cases.
- I don't see users using this much, but cert-manager also has a CSI driver which behaves almost identically to Pod Certificates.
Other service meshes typically implement a similar set of extension points.
For a simple service mesh user, Pod Certificates add nothing new. Any CA you want to use is already supported via existing extension points. You still need to manage a CA, and now it needs to support Pod Certificates (as does your Kubernetes cluster itself; even in Beta, the feature is off-by-default).
However, even if Istio adds support for Pod Certificates, it is at best no different, and generally worse than the current state:
- Private keys need to live in the filesystem, which is generally considered less secure.
- The end to end flow is more complex and slower, involving more components (kubelet, PodCertificateRequest resource, external signer controller) and direct dependencies on the API server.
- There is no actual end-user benefits.
- There is no support for ambient mode (more on this later).
For advanced users, the Pod Certificates approach is likely too restrictive. For example, SPIRE offers a vastly larger number of attestation options than would ever be available Pod Certificates.
Perhaps if you were building a new service mesh from scratch, Pod Certificates would be a reasonable option to build on. But any existing service mesh has already solved the certificate management problem in a way that meets their users' needs.
Standardization?
One potential benefit of Pod Certificates is that it could become a standard way for a service mesh and CA to interact. I could see this playing out, but it enters a crowded field of existing standards:
- The Istio CSR API is already implemented by multiple projects such as cert-manager, and of course Istio itself.
- The SPIFFE Workload API is widely implemented by SPIRE and others.
- Cert-manager is already a common way to integrate with many CAs, with a huge number of supported issuers.
As such, I don't think standardization will meaningfully benefit users.
Ambient mode
The Pod Certificates KEP specifically excludes support for anything other than 1 certificate for 1 Pod workflows. However, Istio's ambient mode does not follow this model. Instead, 1 Pod (ztunnel) has a certificate for each other Pod on the same Node.
There is no support for this in the KEP (and, as far as I know, no plans to add it), making this KEP unusable with Istio ambient mode.
Is it useful for anything?
Overall, I think we will probably find some niche use cases for Pod Certificates.
For pod-to-pod mTLS without a service mesh, this likely makes things slightly easier. I've previously talked about how DIY mTLS is very hard to get right; this feature doesn't help solve any of the 6 problems I outlined there, but it does at least make certificate distribution slightly easier. For one-off specific use cases this could be useful in environments without a service mesh, but doesn't meaningfully enable a generic "mTLS everywhere without service mesh" setup.
The other primary use case (which, if I read between some lines, I believe was the original intent of the KEP) is to allow authenticating to the API server with mTLS instead of service account tokens. This is built on the assumption that mTLS is more secure than JWT tokens, as they are not vulnerable to replay attacks. While I agree with this, generally I don't think this is particularly meaningful to most users. That being said, assuming it becomes will integrated with standard Kubernetes client libraries, this could be a low-cost way to slightly improve security for workloads that need to access the API server.
Finally, a new service mesh could potentially benefit from building on top of Pod Certificates, as unlike existing service meshes, it would not have to build its own certificate distribution mechanism (a current sunk-cost). However, I don't see many new service meshes being built in practice, and I suspect any that do would build on SPIFFE or cert-manager instead.