Istio CNI 플러그인과 Pod Security Admission
https://istio.io/latest/docs/setup/additional-setup/cni/
https://istio.io/latest/docs/setup/additional-setup/pod-security-admission/
Istio CNI 플러그인 활성화하여 설치
kubernetes 의 pod security admission 을 baseline 정책으로 enforce 할 경우(enforce=baseline) istio-init 컨테이너의 NET_ADMIN, NET_RAW 에 대한 capabilities 옵션 때문에 파드 생성 자체가 불가하다.
Istio CNI 플러그인을 활성화하면 istio-init 컨테이너와 istio-proxy 컨테이너는 주입되지만 istio-init 컨테이너가 실제로 iptables 조작을 하지 않고, 그 역할을 istio-cni-node 데몬셋이 대신하기 때문에 capabilities 관련 정책을 위반하지 않기 때문에 pod security admission 을 적용할 수 있다.
pod security 에 의해 파드 생성이 차단된 로그
Warning FailedCreate 17s (x9 over 56s) replicaset-controller (combined from similar events): Error creating: pods "bootcamp-96886dc5b-8r4tj" is forbidden: violates PodSecurity "baseline:latest": non-default capabilities (container "istio-init" must not include "NET_ADMIN", "NET_RAW" in securityContext.capabilities.add)
istio operator 에 Istio CNI 플러그인 활성화하도록 설정
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
namespace: istio-system
name: istiocontrolplane
spec:
...
components:
cni:
enabled: true
...
GKE(Google Kubernetes Engine) 환경에서 Istio CNI 플러그인 활성화
https://istio.io/latest/docs/setup/additional-setup/cni/#hosted-kubernetes-settings
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
...
components:
cni:
enabled: true
namespace: kube-system
values:
cni:
cniBinDir: /home/kubernetes/bin
...
Istio CNI 플러그인 활성화 전/후의 istio-init 컨테이너의 스펙
활성화 전
initContainers:
- args:
- istio-iptables
- -p
- "15001"
- -z
- "15006"
- -u
- "1337"
- -m
- REDIRECT
- -i
- '*'
- -x
- ""
- -b
- '*'
- -d
- 15090,15021,15020
- --log_output_level=default:info
image: docker.io/istio/proxyv2:1.21.2
imagePullPolicy: IfNotPresent
name: istio-init
resources:
limits:
cpu: "2"
memory: 1Gi
requests:
cpu: 10m
memory: 40Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
- NET_RAW
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-6r4n8
readOnly: true
활성화 후
initContainers:
- args:
- istio-iptables
- -p
- "15001"
- -z
- "15006"
- -u
- "1337"
- -m
- REDIRECT
- -i
- '*'
- -x
- ""
- -b
- '*'
- -d
- 15090,15021,15020
- --log_output_level=default:info
- --run-validation
- --skip-rule-apply
image: docker.io/istio/proxyv2:1.21.2
imagePullPolicy: IfNotPresent
name: istio-validation
resources:
limits:
cpu: "2"
memory: 1Gi
requests:
cpu: 10m
memory: 40Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsGroup: 1337
runAsNonRoot: true
runAsUser: 1337
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-25b2p
readOnly: true
pod security 활성화
-> istio-init 컨테이너로 인해 파드 생성이 차단되지 않는지 확인(enforce=baseline)
https://kubernetes.io/docs/concepts/security/pod-security-standards/
https://kubernetes.io/docs/concepts/security/pod-security-admission/
kubectl label ns bootcamp pod-security.kubernetes.io/warn=baseline
kubectl label ns bootcamp pod-security.kubernetes.io/audit=baseline
kubectl label ns bootcamp pod-security.kubernetes.io/enforce=baseline
(참고) 생성된 Istio CNI 플러그인 데몬셋
➜ [istio] k get ds -n istio-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
istio-cni-node 1 1 1 1 1 kubernetes.io/os=linux 8m54s
(참고) docker desktop 에서 실행한 로컬 kubernetes 클러스터에서의 cni 는 istio cni 적용 불가
https://istio.io/latest/docs/setup/additional-setup/cni/#prerequisites
Warning FailedCreatePodSandBox 4m59s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = [failed to set up sandbox container "5783eafdc481c5c1925334fe2514c938a77be91c6c8f436f8c9c5db225a3c1e3" network for pod "bootcamp-5b67dc74bb-8smzk": networkPlugin cni failed to set up pod "bootcamp-5b67dc74bb-8smzk_bootcamp" network: plugin type="loopback" failed (add): missing network name:, failed to clean up sandbox container "5783eafdc481c5c1925334fe2514c938a77be91c6c8f436f8c9c5db225a3c1e3" network for pod "bootcamp-5b67dc74bb-8smzk": networkPlugin cni failed to teardown pod "bootcamp-5b67dc74bb-8smzk_bootcamp" network: plugin type="loopback" failed (delete): missing network name]
(참고) istio-cni-node 파드 로그
-> 노드에 istio-proxy 컨테이너 주입이 활성화된 파드 배치 시 자동으로 iptables 세팅
2024-05-10T01:52:31.053848Z info cni ============= Start iptables configuration for bootcamp-764dbb76f4-7688l =============
2024-05-10T01:52:31.055613Z info cni Istio iptables environment:
ENVOY_PORT=
INBOUND_CAPTURE_PORT=
ISTIO_INBOUND_INTERCEPTION_MODE=
ISTIO_INBOUND_TPROXY_ROUTE_TABLE=
ISTIO_INBOUND_PORTS=
ISTIO_OUTBOUND_PORTS=
ISTIO_LOCAL_EXCLUDE_PORTS=
ISTIO_EXCLUDE_INTERFACES=
ISTIO_SERVICE_CIDR=
ISTIO_SERVICE_EXCLUDE_CIDR=
ISTIO_META_DNS_CAPTURE=
INVALID_DROP=
2024-05-10T01:52:31.055706Z info cni Istio iptables variables:
IPTABLES_VERSION=
PROXY_PORT=15001
PROXY_INBOUND_CAPTURE_PORT=15006
PROXY_TUNNEL_PORT=15008
PROXY_UID=1337
PROXY_GID=1337
INBOUND_INTERCEPTION_MODE=REDIRECT
INBOUND_TPROXY_MARK=1337
INBOUND_TPROXY_ROUTE_TABLE=133
INBOUND_PORTS_INCLUDE=*
INBOUND_PORTS_EXCLUDE=15020,15021,15090
OUTBOUND_OWNER_GROUPS_INCLUDE=*
OUTBOUND_OWNER_GROUPS_EXCLUDE=
OUTBOUND_IP_RANGES_INCLUDE=*
OUTBOUND_IP_RANGES_EXCLUDE=
OUTBOUND_PORTS_INCLUDE=
OUTBOUND_PORTS_EXCLUDE=15020
KUBE_VIRT_INTERFACES=
ENABLE_INBOUND_IPV6=false
DUAL_STACK=false
DNS_CAPTURE=false
DROP_INVALID=false
CAPTURE_ALL_DNS=false
DNS_SERVERS=[],[]
NETWORK_NAMESPACE=/var/run/netns/cni-2ac62102-d38c-f082-047c-828feb67b467
CNI_MODE=true
EXCLUDE_INTERFACES=
2024-05-10T01:52:31.055774Z info cni Running iptables-restore with the following input:
* nat
-N ISTIO_INBOUND
-N ISTIO_REDIRECT
-N ISTIO_IN_REDIRECT
-N ISTIO_OUTPUT
-A ISTIO_INBOUND -p tcp --dport 15008 -j RETURN
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A ISTIO_INBOUND -p tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -p tcp --dport 15020 -j RETURN
-A ISTIO_OUTPUT -o lo -s 127.0.0.6/32 -j RETURN
-A ISTIO_OUTPUT -o lo ! -d 127.0.0.1/32 -p tcp ! --dport 15008 -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -o lo ! -d 127.0.0.1/32 -p tcp ! --dport 15008 -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
COMMIT
2024-05-10T01:52:31.055821Z info cni Running command (without lock by mount and nss): iptables-restore --noflush
2024-05-10T01:52:31.060019Z info cni Running ip6tables-restore with the following input:
2024-05-10T01:52:31.060107Z info cni Running command (without lock by mount and nss): ip6tables-restore --noflush
2024-05-10T01:52:31.062294Z info cni Running command (without nss): iptables-save
2024-05-10T01:52:31.065981Z info cni Command output:
# Generated by iptables-save v1.8.5 on Fri May 10 01:52:31 2024
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:ISTIO_INBOUND - [0:0]
:ISTIO_IN_REDIRECT - [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_REDIRECT - [0:0]
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A ISTIO_OUTPUT -p tcp -m tcp --dport 15020 -j RETURN
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -p tcp -m tcp ! --dport 15008 -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -p tcp -m tcp ! --dport 15008 -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
COMMIT
# Completed on Fri May 10 01:52:31 2024
2024-05-10T01:52:31.066106Z info cni ============= End iptables configuration for bootcamp-764dbb76f4-7688l =============
만약 노드에 배치된 파드가 istio-proxy 컨테이너가 없는 경우 iptables 룰 주입을 하지 않음
2024-05-10T01:56:07.895173Z info cni excluded because it does not have istio-proxy container (have [bootcamp])
샘플 매니페스트 작성
apiVersion: apps/v1
kind: Deployment
metadata:
name: bootcamp
spec:
selector:
matchLabels:
app: bootcamp
replicas: 5
template:
metadata:
labels:
app: bootcamp
spec:
containers:
- image: gcr.io/google-samples/kubernetes-bootcamp:v1
imagePullPolicy: Always
name: bootcamp
ports:
- containerPort: 8080
resources:
limits:
cpu: 200m
memory: 256Mi
ephemeral-storage: 1Gi
requests:
cpu: 200m
memory: 256Mi
ephemeral-storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: bootcamp
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: LoadBalancer
selector:
app: bootcamp
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: istio-ingressgateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: bootcamp
spec:
gateways:
- istio-system/istio-ingressgateway
hosts:
- www.example.com
http:
- match:
- uri:
prefix: /
route:
- destination:
host: bootcamp.bootcamp.svc.cluster.local
port:
number: 80
호출 테스트
kubectl exec -it bootcamp-84bb8b8cd-5r5rm -- curl -v -H 'Host: www.example.com' http://istio-ingressgateway.istio-system.svc.cluster.local/