istio: Sidecar injection fails when using PSP with allowPrivilegeEscalation=false and istio-init container privleged=false
Describe the bug
Failed to deploy automatically injected sidecar container, with the istio-sidecar-injector configmap is configured to use securityContext: privileged: false
for proxy-init, when using a PodSecurityPolicy with privilegeEscalationAllowed: false
and CAP_NET_ADMIN
enabled, yet it works for manual sidecar injection using the same PSPs/ClusterRole/ClusterRoleBinding.
- With automatic sidecar injection enabled
kubectl label namespace default istio-injection=enabled
kubectl get deployment httpbin
message: 'pods "httpbin-7cf66f94c4-hzhhd" is forbidden: unable to validate against
any pod security policy: [spec.initContainers[0].securityContext.capabilities.add:
Invalid value: "NET_ADMIN": capability may not be added]'
reason: FailedCreate
- With manual sidecar injection and namespace label removed
kubectl label namespace default istio-injection-
istioctl kube-inject -f httpbin.yaml | kubectl create -f -
kubectl get pods
NAME READY STATUS RESTARTS AGE
httpbin-986b585bf-z5xkb 0/2 PodInitializing 0 3s
- sidecar-injector configmap
kubectl -n istio-system get cm istio-sidecar-injector -o yaml
apiVersion: v1
data:
config: "policy: enabled\ntemplate: |-\n initContainers:\n - name: istio-init\n
\ image: \"istio/proxy_init:1.0.3\"\n args:\n -
\"-p\"\n - [[ .MeshConfig.ProxyListenPort ]]\n - \"-u\"\n - 1337\n -
\"-m\"\n - [[ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode
]]\n - \"-i\"\n - \"[[ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges`
\ \"100.64.0.0/13\" ]]\"\n - \"-x\"\n - \"[[ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges`
\ \"\" ]]\"\n - \"-b\"\n - \"[[ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts`
(includeInboundPorts .Spec.Containers) ]]\"\n - \"-d\"\n - \"[[ excludeInboundPort
(annotation .ObjectMeta `status.sidecar.istio.io/port` 0 ) (annotation .ObjectMeta
`traffic.sidecar.istio.io/excludeInboundPorts` \"\" ) ]]\"\n imagePullPolicy:
IfNotPresent\n securityContext:\n capabilities:\n add:\n -
NET_ADMIN\n privileged: false \n restartPolicy: Always\n containers:\n
\ - name: istio-proxy\n image: [[ annotation .ObjectMeta `sidecar.istio.io/proxyImage`
\ \"istio/proxyv2:1.0.3\" ]]\n\n ports:\n - containerPort:
15090\n protocol: TCP\n name: http-envoy-prom\n\n args:\n - proxy\n
\ - sidecar\n - --configPath\n - [[ .ProxyConfig.ConfigPath ]]\n -
--binaryPath\n - [[ .ProxyConfig.BinaryPath ]]\n - --serviceCluster\n [[
if ne \"\" (index .ObjectMeta.Labels \"app\") -]]\n - [[ index .ObjectMeta.Labels
\"app\" ]]\n [[ else -]]\n - \"istio-proxy\"\n [[ end -]]\n - --drainDuration\n
\ - [[ formatDuration .ProxyConfig.DrainDuration ]]\n - --parentShutdownDuration\n
\ - [[ formatDuration .ProxyConfig.ParentShutdownDuration ]]\n - --discoveryAddress\n
\ - [[ .ProxyConfig.DiscoveryAddress ]]\n - --discoveryRefreshDelay\n -
[[ formatDuration .ProxyConfig.DiscoveryRefreshDelay ]]\n - --zipkinAddress\n
\ - [[ .ProxyConfig.ZipkinAddress ]]\n - --connectTimeout\n - [[ formatDuration
.ProxyConfig.ConnectTimeout ]]\n - --proxyAdminPort\n - [[ .ProxyConfig.ProxyAdminPort
]]\n [[ if gt .ProxyConfig.Concurrency 0 -]]\n - --concurrency\n - [[
.ProxyConfig.Concurrency ]]\n [[ end -]]\n - --controlPlaneAuthPolicy\n
\ - [[ annotation .ObjectMeta `sidecar.istio.io/controlPlaneAuthPolicy` .ProxyConfig.ControlPlaneAuthPolicy
]]\n [[- if (ne (annotation .ObjectMeta `status.sidecar.istio.io/port` 0 ) \"0\")
]]\n - --statusPort\n - [[ annotation .ObjectMeta `status.sidecar.istio.io/port`
\ 0 ]]\n - --applicationPorts\n - \"[[ annotation .ObjectMeta `readiness.status.sidecar.istio.io/applicationPorts`
(applicationPorts .Spec.Containers) ]]\"\n [[- end ]]\n env:\n - name:
POD_NAME\n valueFrom:\n fieldRef:\n fieldPath: metadata.name\n
\ - name: POD_NAMESPACE\n valueFrom:\n fieldRef:\n fieldPath:
metadata.namespace\n - name: INSTANCE_IP\n valueFrom:\n fieldRef:\n
\ fieldPath: status.podIP\n - name: ISTIO_META_POD_NAME\n valueFrom:\n
\ fieldRef:\n fieldPath: metadata.name\n - name: ISTIO_META_INTERCEPTION_MODE\n
\ value: [[ or (index .ObjectMeta.Annotations \"sidecar.istio.io/interceptionMode\")
.ProxyConfig.InterceptionMode.String ]]\n [[ if .ObjectMeta.Annotations ]]\n
\ - name: ISTIO_METAJSON_ANNOTATIONS\n value: |\n [[ toJson
.ObjectMeta.Annotations ]]\n [[ end ]]\n [[ range $k,$v := .ObjectMeta.Labels
]]\n - name: ISTIO_META_[[ $k ]]\n value: \"[[ $v ]]\"\n [[ end ]]\n
\ imagePullPolicy: IfNotPresent\n [[ if (ne (annotation .ObjectMeta `status.sidecar.istio.io/port`
\ 0 ) \"0\") ]]\n readinessProbe:\n httpGet:\n path: /healthz/ready\n
\ port: [[ annotation .ObjectMeta `status.sidecar.istio.io/port` 0 ]]\n
\ initialDelaySeconds: [[ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds`
\ 1 ]]\n periodSeconds: [[ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds`
\ 2 ]]\n failureThreshold: [[ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold`
\ 30 ]]\n [[ end -]]securityContext:\n \n readOnlyRootFilesystem:
true\n [[ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode`
.ProxyConfig.InterceptionMode) \"TPROXY\" -]]\n capabilities:\n add:\n
\ - NET_ADMIN\n runAsGroup: 1337\n [[ else -]]\n runAsUser:
1337\n [[ end -]]\n restartPolicy: Always\n resources:\n [[ if
(isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -]]\n requests:\n
\ cpu: \"[[ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` ]]\"\n
\ memory: \"[[ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`
]]\"\n [[ else -]]\n requests:\n cpu: 10m\n \n [[ end -]]\n
\ volumeMounts:\n - mountPath: /etc/istio/proxy\n name: istio-envoy\n
\ - mountPath: /etc/certs/\n name: istio-certs\n readOnly: true\n
\ volumes:\n - emptyDir:\n medium: Memory\n name: istio-envoy\n - name:
istio-certs\n secret:\n optional: true\n [[ if eq .Spec.ServiceAccountName
\"\" -]]\n secretName: istio.default\n [[ else -]]\n secretName:
[[ printf \"istio.%s\" .Spec.ServiceAccountName ]]\n [[ end -]]"
kind: ConfigMap
PSP, ClusterRole and ClusterRoleBinding shown in following sections
Expected behavior
With NONE of the inject container’ssecurityContext
demanding privileged: true
, a PSP with privilegeEscalationAllowed: false
and CAP_NET_ADMIN
enabled should satisty the requirements to successfully deploy a pod with the sidecar injected.
I have proved this by removing the namespace label istio-injecion
(to disable automatic sidecar injection) and using manual sidecar injection using istioctl kube-inject -f httpbin.yaml
to generate and apply the modified spec. In this case the deployment succeeds with the aforemention PSP.
Steps to reproduce the bug
With the istio-sidecar-injector configmap using the value privilege: false
being used in the initContainer `securityContext, enable automatic injection in default namespace by labeling with istio-injection=enabled, as follows,
kubectl label namespace default istio-injection=enabled
Create a PSP, ClusterRole and ClusterRoleBinding as follows in the default namespace,
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
name: istio-psp
spec:
allowPrivilegeEscalation: false
allowedCapabilities:
- NET_ADMIN
fsGroup:
rule: RunAsAny
runAsUser:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
volumes:
- configMap
- emptyDir
- projected
- secret
- downwardAPI
- persistentVolumeClaim
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: psp-user
rules:
- apiGroups:
- extensions
resourceNames:
- istio-psp
resources:
- podsecuritypolicies
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: psp-user-binding
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp-user
subjects:
- apiGroup: ""
kind: ServiceAccount
name: default
namespace: default
, and apply.
Now deploy sample/httpbin/httpbin.yaml
to the default namespace.
`kubectl create -f sample/httpbin/httpbin.yaml
Version kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.5", GitCommit:"32ac1c9073b132b8ba18aa830f46b77dcceb0723", GitTreeState:"clean", BuildDate:"2018-06-21T11:46:00Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.3", GitCommit:"435f92c719f279a3a67808c80521ea17d5715c66", GitTreeState:"clean", BuildDate:"2018-11-26T12:46:57Z", GoVersion:"go1.10.4", Compiler:"gc", Platform:"linux/amd64"}
istioctl version
Version: 1.0.3
GitRevision: a44d4c8bcb427db16ca4a439adfbd8d9361b8ed3
User: root@0ead81bba27d
Hub: docker.io/istio
GolangVersion: go1.10.4
BuildStatus: Clean
Installation
Istio was installed using helm template
with --set global.proxy.privileged=false
among other settings.
Environment Self managed kubernetes cluster running on AWS.
Cluster state Can provide as needed.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 32 (17 by maintainers)
I’m also encountering this issue, even when using istio-cni, which avoids needing
proxy_init
and thus theNET_ADMIN
privilege, and withproxy.privileged: false
in the Helm chart values. This is surprising to me because thesecurityContext
doesn’t seem to need privilege escalation:I found issue https://github.com/kubernetes/kubernetes/issues/65716, which suggests that this could be solved if the sidecar injector added
allowPrivilegeEscalation: false
toistio-proxy
’ssecurityContext
. I tested this out by adding that to theistio-sidecar-injector
ConfigMap and restarting the sidecar injector, and it fixed this issue. Also, I addedto the security context, which allowed me to include
requiredDropCapabilities: ["ALL"]
in my PSP. Could we consider adding these to the security context in the helm chart?Thanks for bringing this up @tbarrella, I’ve created https://github.com/istio/istio/issues/12231 to track it.
Yeah, I’ve since removed the change, but it should be around this line in 1.1.0