介绍
漏洞
k8s有一个漏洞:https://www.cvedetails.com/cve/CVE-2020-8554/
如果攻击者可以创建或编辑服务和容器,则此漏洞使攻击者能够拦截来自群集中其他容器(或节点)的流量。攻击者可利用该漏洞通过 Kubernetes 上的 LoadBalancer 或 ExternalIP 充当中间人,以便在会话中读取或写入数据。使用externalip-webhook可以进行修复。
漏洞演示请参考:https://my.oschina.net/cncf/blog/4824872(侵删)
简介
externalip-webhook 是一个验证 webhook,它可以防止服务使用随机外部 IP。集群管理员可以通过指定 allowed-external-ip-cidrs 参数来指定允许用作外部 IP 的 CIDR 列表。 Webhook 将只允许创建不需要外部 IP 或外部 IP 在管理员指定范围内的服务。 externalip-webhook 还可以通过指定 allowed-usernames 和 allowed-groups 参数来限制谁可以为服务指定允许的外部 IP 范围。
部署
官方地址:https://github.com/kubernetes-sigs/externalip-webhook
- 要将外部 IP 限制为某些 CIDR,请取消注释并更新 webhook.yaml 中的 allowed-external-ip-cidrs 。
- 要限制可以为服务指定外部 IP 的用户,请取消注释并更新 webhook.yaml 中的 allowed-usernames 和/或 allowed-groups 。这两个参数的默认值都是空的,这意味着任何用户都可以指定。如果设置了任一参数,则指定外部 IP 仅限于与这些参数中的任何一个匹配的用户。
- 要限制可以为服务指定外部 IP 的用户,请取消注释并更新 webhook.yaml 中的 allowed-usernames 和/或 allowed-groups 。这两个参数的默认值都是空的,这意味着任何用户都可以指定。如果设置了任一参数,则指定外部 IP 仅限于与这些参数中的任何一个匹配的用户。
部署流程
# 我的虚拟机访问不了github
# git clone https://github.com/kubernetes-sigs/externalip-webhook.git
wget https://github.com/kubernetes-sigs/externalip-webhook/archive/refs/heads/master.zip
unzip externalip-webhook-master.zip
make docker-build docker-push IMG=externalip-webhook:v1
make deploy IMG=externalip-webhook:v1
安装kustomize
github中的kustomize地址已经失效了,新的地址是:https://kubectl.docs.kubernetes.io/installation/kustomize/binaries/
# curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
wget https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv4.2.0/kustomize_v4.2.0_linux_amd64.tar.gz
tar xvf kustomize_v4.2.0_linux_amd64.tar.gz
cp kustomize /usr/bin
我的k8s版本是1.22.17,所以这里下载4.2.0的kustomize。
安装controller-gen
报错:which: no controller-gen in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
export GOPROXY=https://goproxy.cn/
# git clone https://github.com/kubernetes-sigs/controller-tools.git
wget https://github.com/kubernetes-sigs/controller-tools/archive/refs/heads/master.zip
unzip controller-tools-master.zip
cd controller-tools-master/
go install ./cmd/{controller-gen,helpgen,type-scaffold}
cd $GOPATH
cp controller-gen /usr/bin
再次部署报错:go: github.com/ghodss/yaml@v1.0.0: Get https://proxy.golang.org/github.com/ghodss/yaml/@v/v1.0.0.mod: dial tcp 142.251.43.17:443: i/o timeout
查看并修改Dockerfile,添加goproxy
ENV GO111MODULE=on \
GOPROXY=https://goproxy.cn,direct
报错:denied: requested access to the resource is denied
,make命令去掉docker-push即可。即 make docker-build IMG=externalip-webhook:v1
查看镜像
再次部署
kustomize build config/default | kubectl delete -f -
make deploy IMG=externalip-webhook:v1
报错:MountVolume.SetUp failed for volume "cert" : secret "webhook-server-cert" not found
部署cert-manager
helm repo add bitnami https://charts.bitnami.com/bitnami
helm search repo cert-manager
# kustomize build config/default | kubectl delete -f -
helm install cert-manager bitnami/cert-manager -n cert-manager --create-namespace --set installCRDs=true
# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.crds.yaml
最好先把cert-manager安装好,否则就会报下面这个错误,调用validate-externalip.webhook失败。
查看README中关于webhook-server-cert的描述,需要手动上传证书相关信息。
创建自签名证书
openssl genrsa -out ca.key 2048
openssl req -new -key ca.key -x509 -out ca.crt -subj "/CN=my-ca"
openssl genrsa -out tls.key 2048
openssl req -new -key tls.key -out tls.csr -subj "/CN=my-ca"
openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt
kubectl create secret generic webhook-server-cert --from-file=tls.crt --from-file=tls.key --from-file=ca.crt -n externalip-validation-system
修改manifest.yaml
openssl x509 -in ca.crt -out ca.pem -outform PEM
cat ca.pem
将ca.pem粘贴到 https://base64.supfree.net/ ,进行base64加密,再将加密后的内容粘贴到manifest.yaml。(有坑,见下方的测试)
重新部署
make deploy IMG=externalip-webhook:v1
k get po -n externalip-validation-system
k delete po externalip-validation-webhook-9d48754f9-kr4ks -n externalip-validation-system
k get po -n externalip-validation-system
测试
报错1
创建nginx服务。报错:failed calling webhook "validate-externalip.webhook.svc": unable to load root certificates: unable to parse bytes as PEM block
原因是caBundle中pem证书格式有问题,不能被k8s识别。可以用下面的命令来将一个pem证书转换为 k8s可以接受的格式。
cat ca.pem | base64 -w 0
报错2
再次部署,报错:Internal error occurred: failed calling webhook "validate-externalip.webhook.svc": Post "https://externalip-validation-webhook-service.externalip-validation-system.svc:443/validate-service?timeout=10s": x509: certificate is not valid for any names, but wanted to match externalip-validation-webhook-service.externalip-validation-system.svc
证书有问题,重新创建。而且不能用上面的 externalip-validation-webhook-service.externalip-validation-system.svc
来当作CN,会报错长度过长。
注释namePrefix
vim config/default/kustomization.yaml
使用 webhook-service.externalip-validation-system.svc
当作CN。
openssl req -new -key ca.key -x509 -out ca.crt -subj "/CN=webhook-service.externalip-validation-system.svc"
报错3
再次替换caBundle,重新部署。报错:Error from server (InternalError): error when creating "nginx.yaml": Internal error occurred: failed calling webhook "validate.webhook.svc": Post "https://webhook-service.externalip-validation-system.svc:443/validate-service?timeout=10s": x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0
这个是因为go在1.15版本之后废弃了CN,推荐使用SAN证书,参考:https://golang.org/doc/go1.15#commonname 。如果你的自签名证书中不包含SANs,就会出现这个报错。
解决方法:重新生成SAN证书
find / -name openssl.cnf
cp /etc/pki/tls/openssl.cnf /etc/ssl/openssl.cnf
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/CN=webhook-service.externalip-validation-system.svc"
openssl genrsa -out tls.key 2048
openssl req -new -key tls.key -out tls.csr -subj "/CN=webhook-service.externalip-validation-system.svc" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:webhook-service.externalip-validation-system.svc,DNS:*.svc"))
openssl x509 -req -days 365 -in tls.csr -out tls.crt -CA ca.crt -CAkey ca.key -CAcreateserial -extensions SAN -extfile <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:webhook-service.externalip-validation-system.svc,DNS:*.svc"))
openssl x509 -noout -text -in tls.crt
openssl x509 -in ca.crt -out ca.pem -outform PEM
cat ca.pem | base64 -w 0
k delete secrets -n externalip-validation-system webhook-server-cert
k create secret generic webhook-server-cert --from-file=tls.crt --from-file=tls.key --from-file=ca.crt -n externalip-validation-system
k delete po -n externalip-validation-system webhook-9d48754f9-9qvpr
k logs -f -n externalip-validation-system webhook-9d48754f9-xfvff
k apply -f /root/nginx.yaml
创建成功。
注:添加环境变量无效
GODEBUG=x509ignoreCN=0
# 在本地,Dockerfile,docker.service中添加都无效,而且Dockerfile中使用的镜像是go1.13.