手把手搭建 k8s docker 漏洞環境
一、前言
此環境為復現 docker 以及 k8s 容器逃逸的集成漏洞環境。
涉及以下漏洞:
1、docker privileged 特權模式
2、參數SYS_ADMIN導致cgroup逃逸
3、proc掛載
4、docker remote api 未授權
5、掛載sock文件
6、臟牛內核逃逸(此條劃掉,沒符合漏洞版本的linux,而且在實戰中這個漏洞也很少遇到,大家直接靶場lampiao復現一下這個洞就可以)
7、1day runc
8、k8s鑒權配置不當
9、k8s日志掛載
10、k8s賬戶權限配置不當
二、準備工作
使用huawei歐拉服務器V2.0 SP5
https://euleros2019.obs.cn-north-1.myhuaweicloud.com/ict/site-euleros/euleros/repo/yum/2.5/os/x86_64/iso/EulerOS-V2.0SP5-x86_64-dvd.iso
準備兩臺服務器(虛擬機)
master主服務器 192.168.88.180 k8s-master
node節點服務器 192.168.88.181 k8s-node
集群服務器至少2h2g30G
注意事項:
1、查看hostname如果是localhost,請使用以下命令進行修改
hostnamectl set-hostname test101
2、兩臺服務器的/etc/hosts文件中需添加如下配置
master主機IP 主機名
node節點IP 主機名
3、保持兩臺機器時間同步
利用date命令查看,不一致使用命令進行同步
ntpdate ntp1.aliyun.com

若更新源沒問題,則進行第三步:安裝,若有問題則看第四步:遇到的問題及解決方案。
三、安裝
1、安裝依賴
yum install go
yum install git
yum -y install gcc automake autoconf libtool make

把container-selinux-2.107-3.el7.noarch.rpm、runc.sh、privileged.sh放到兩臺機器上
http://ftp.riken.jp/Linux/cern/centos/7/extras/x86_64/Packages/container-selinux-2.107-3.el7.noarch.rpm
#runc.sh #!/bin/bash #sunian echo "查找原有libseccomp進行刪除" results=`yum list installed|grep libseccomp` if test ! -z "$results";then yum -y remove $results echo "刪除完畢" else echo "已刪除,進行下一步" fi `mkdir -p /home/runc` `cd /home/runc` `wget http://mirror.centos.org/centos/7/os/x86_64/Packages/libseccomp-2.3.1-4.el7.x86_64.rpm` rpm -ivh libseccomp-2.3.1-4.el7.x86_64.rpm `wget http://mirror.centos.org/centos/7/os/x86_64/Packages/libseccomp-devel-2.3.1-4.el7.x86_64.rpm` rpm -ivh libseccomp-devel-2.3.1-4.el7.x86_64.rpm `mkdir -p $HOME/go/src/github.com` `go get github.com/opencontainers/runc` cd $HOME/go/src/github.com/opencontainers/runc git checkout v1.0.0-rc5 echo -e "\033[31m `git status` \033[0m" make make install echo -e "\033[31m `runc -v` \033[0m"
注釋:查找原有libseccomp進行刪除,從官網下載所需libseccomp和libseccomp-devel進行rpm安裝,利用go從github下載帶有漏洞版本的runc v1.0.0-rc5并編譯。

#privileged.sh #!/bin/bash #sunian echo "查找原有docker進行刪除" results=`yum list installed|grep docker` if test ! -z "$results";then yum -y remove $results echo "刪除完畢" else echo "已刪除,進行下一步" fi #開始安裝存在漏洞版本docker echo "創建并安裝docker的依賴" echo "安裝在/home/privileged目錄下" mkdir -p /home/privileged mv /root/container-selinux-2.107-3.el7.noarch.rpm /home/privileged cd /home/privileged rpm --force --nodeps -ivh container-selinux-2.107-3.el7.noarch.rpm `wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-18.06.0.ce-3.el7.x86_64.rpm` rpm -ivh docker-ce-18.06.0.ce-3.el7.x86_64.rpm `service docker start` echo `docker version` echo -e "\033[31m `docker images` \033[0m"
注釋:刪除原有的docker,安裝docker的依賴 即container-selinux,安裝存在漏洞版本的docker,執行docker images并輸出,查看是否安裝成功。

2、配置k8s及安裝
(1)系統設置:
安裝nfs以及wget
yum install -y nfs-utils
yum install -y wget
關閉防火墻
systemctl stop firewalld
systemctl disable firewalld
關閉selinux
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
關閉swap分區
swapoff -a
yes | cp /etc/fstab /etc/fstab_bak
cat /etc/fstab_bak |grep -v swap > /etc/fstab
將橋接的IPv4 IPv6流量傳遞到iptables的鏈(直接shell腳本):
# 如果有配置,則修改 sed -i "s#^net.ipv4.ip_forward.*#net.ipv4.ip_forward=1#g" /etc/sysctl.conf sed -i "s#^net.bridge.bridge-nf-call-ip6tables.*#net.bridge.bridge-nf-call-ip6tables=1#g" /etc/sysctl.conf sed -i "s#^net.bridge.bridge-nf-call-iptables.*#net.bridge.bridge-nf-call-iptables=1#g" /etc/sysctl.conf sed -i "s#^net.ipv6.conf.all.disable_ipv6.*#net.ipv6.conf.all.disable_ipv6=1#g" /etc/sysctl.conf sed -i "s#^net.ipv6.conf.default.disable_ipv6.*#net.ipv6.conf.default.disable_ipv6=1#g" /etc/sysctl.conf sed -i "s#^net.ipv6.conf.lo.disable_ipv6.*#net.ipv6.conf.lo.disable_ipv6=1#g" /etc/sysctl.conf sed -i "s#^net.ipv6.conf.all.forwarding.*#net.ipv6.conf.all.forwarding=1#g" /etc/sysctl.conf # 可能沒有,追加 echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.conf echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.conf echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf echo "net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.conf echo "net.ipv6.conf.all.forwarding = 1" >> /etc/sysctl.conf # 執行命令以應用 sysctl -p

(2)配置k8s源
cat < /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
(3)安裝k8s
yum remove -y kubelet kubeadm kubectl
yum install -y kubelet-1.18.2 kubeadm-1.18.2 kubectl-1.18.2
sudo systemctl daemon-reload
sudo systemctl enable docker
sudo systemctl restart docker
systemctl enable kubelet && systemctl start kubelet
(到此兩臺機器都有runc、docker、k8s工具(kubeadm kubectl kubelet))

(4)接下來開始初始化服務器master節點(node服務器無需操作)
# 只在 master 節點執行
# export 命令只在當前 shell 會話中有效,開啟新的 shell 窗口后,如果要繼續安裝過程,請重新執行此處的 export 命令
export MASTER_IP=192.168.88.180
# 替換 apiserver.demo 為 您想要的 dnsName
export APISERVER_NAME=apiserver.demo
# Kubernetes 容器組所在的網段,該網段安裝完成后,由 kubernetes 創建,事先并不存在于您的物理網絡中
export POD_SUBNET=172.18.0.1/16
echo "${MASTER_IP} ${APISERVER_NAME}" >> /etc/hosts
創建init_master.sh
#!/bin/bash
# 腳本出錯時終止執行
set -e
if [ ${#POD_SUBNET} -eq 0 ] || [ ${#APISERVER_NAME} -eq 0 ]; then
echo -e "\033[31;1m請確保您已經設置了環境變量 POD_SUBNET 和 APISERVER_NAME \033[0m"
echo 當前POD_SUBNET=$POD_SUBNET
echo 當前APISERVER_NAME=$APISERVER_NAME
exit 1
fi
# 查看完整配置選項 https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2
rm -f ./kubeadm-config.yaml
cat < ./kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.18.2
imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers
controlPlaneEndpoint: "${APISERVER_NAME}:6443"
networking:
serviceSubnet: "10.96.0.0/16"
podSubnet: "${POD_SUBNET}"
dnsDomain: "cluster.local"
EOF
# kubeadm init
# 根據您服務器網速的情況,您需要等候 3 - 10 分鐘
kubeadm init --config=kubeadm-config.yaml --upload-certs
# 配置 kubectl
rm -rf /root/.kube/
mkdir /root/.kube/
cp -i /etc/kubernetes/admin.conf /root/.kube/config
# 安裝 calico 網絡插件
# 參考文檔 https://docs.projectcalico.org/v3.13/getting-started/kubernetes/self-managed-onprem/onpremises
echo "安裝calico-3.13.1"
rm -f calico-3.13.1.yaml
wget https://kuboard.cn/install-script/calico/calico-3.13.1.yaml
kubectl apply -f calico-3.13.1.yaml
加權限執行進行初始化,預檢有點慢,等一等
之后執行如下命令,直到所有的容器組處于 Running 狀態
watch kubectl get pod -n kube-system -o wide

節點初始化狀態為ready
kubectl get nodes -o wide

至此主機服務器安裝完成
(5)配置node節點
在master服務器上獲取join命令
kubeadm token create --print-join-command

node節點服務器執行該命令
# 只在 node 節點執行
# 192.168.88.180 為 master 節點的內網 IP
export MASTER_IP=192.168.88.180
# 替換 apiserver.demo 為初始化 master 節點時所使用的 APISERVER_NAME
export APISERVER_NAME=apiserver.demo
echo "${MASTER_IP} ${APISERVER_NAME}" >> /etc/hosts
執行master服務器獲取的join命令

最后master服務器執行命令
kubectl get nodes -o wide

查看node節點服務器直到ready,至此安裝完成
四、遇到的問題及解決方案
1、若無更新源或源有問題(如 無想要的包),可使用以下更新源
Makefile 在/etc/yum.repos.d/目錄下,創建文件EulerOS.repo [base] name=EulerOS-2.0SP5 base baseurl=http://repo.huaweicloud.com/euler/2.5/os/x86_64/ enabled=1 gpgcheck=1 gpgkey=http://repo.huaweicloud.com/euler/2.5/os/RPM-GPG-KEY-EulerOS 執行yum clean all清除原有yum緩存。 執行yum makecache生成新的緩存。 yum update更新
2、go版本的問題,1.16以下的go版本不支持io/fs,但是該報錯不影響后續運行

3、若使用centos,期間可能出現的問題:
網絡問題,主要是go下載runc時請求github網絡問題,解決方案:臨時環境變量,使用aliyun go源
export GOPROXY=https://mirrors.aliyun.com/goproxy/
4、如果docker未啟動

service docker start
5、10250端口占用
[ERROR Port-10250]: Port 10250 is in use
解決方法:
kubeadm reset
6、修改docker為cgroups或systemd驅動
cat > /etc/docker/daemon.json <{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
EOF
systemctl daemon-reload && systemctl restart docker
7、rpm安裝docker缺少依賴
報錯如下
libcgroup is needed by docker-ce-18.06.0.ce-3.el7.x86_64
libltdl.so.7()(64bit) is needed by docker-ce-18.06.0.ce-3.el7.x86_64

解決方案:
http://mirror.centos.org/centos/7/os/x86_64/Packages/ 安裝以下依賴
libcgroup-0.41-21.el7.x86_64.rpm
libtool-ltdl-2.4.2-22.el7_3.x86_64.rpm
8、nodeRegistration.name: Invalid value

hostname或dnsname不能用下劃線,只能用'-'或'.'。