Skip to content

Commit 2fb5b90

Browse files
author
Wu Wenxiang
committed
docs: add labs & tips
1 parent a57d219 commit 2fb5b90

File tree

1 file changed

+293
-11
lines changed

1 file changed

+293
-11
lines changed

doc/class-04-Kubernetes-Security-Specialist.md

Lines changed: 293 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ metadata:
842842
name: test-ns
843843
labels:
844844
gk-ns: xxx
845-
EOF
845+
EOF
846846

847847
# 创建 namespace , 因为这个 namespace 有 gk-ns: xxx 的 label 所以这个请求可以被允许
848848
$ kubectl apply -f test-ns.yaml
@@ -852,7 +852,7 @@ $ cat <<EOF > gk-ns-constraintTemplate.yaml
852852
apiVersion: templates.gatekeeper.sh/v1beta1
853853
kind: ConstraintTemplate
854854
metadata:
855-
name: k8srequiredregistry
855+
name: k8srequiredregistry
856856
spec:
857857
crd:
858858
spec:
@@ -933,6 +933,93 @@ kube-apiserver 是整个 k8s 的 api 接口,所有和 k8s 打交道的服务
933933
- ImagePolicyWebhook -- 这个准入控制器是用来检查 pod 的 image 是否符合自定义的安全策略,即将验证 image 的合法性委托给一个外置的服务来完成,通常会和 `Open Policy Agent GateKeeper` 配合来使用。
934934
- --enable-bootstrap-token-auth -- 当这个设置为 true 时会增加 kube-apiserver 认证的风险,攻击者可以利用这个 token 加入恶意的节点到集群中,所以这个参数的设置应该是 false。
935935

936+
#### 5.2.1 lab:启用 API server 认证
937+
938+
操作如下:
939+
940+
- 使用 Node,RBAC 授权模式和 NodeRestriction 准入控制器
941+
942+
```yaml
943+
vi /etc/kubernetes/manifests/kube-apiserver.yaml
944+
# 确保以下内容
945+
- --authorization-mode=Node,RBAC
946+
- --enable-admission-plugins=NodeRestriction
947+
- --client-ca-file=/etc/kubernetes/pki/ca.crt
948+
- --enable-bootstrap-token-auth=true
949+
```
950+
951+
- 删除 system:anonymous 的 ClusterRolebinding 角色绑定,取消匿名用户的集群管理员权限
952+
953+
```bash
954+
kubectl delete clusterrolebinding system:anonymous
955+
```
956+
957+
#### 5.2.2 lab:ImagePolicyWebhook
958+
959+
实验如下:
960+
961+
1. 修改控制器配置文件,将未找到有效后端时的默认拒绝改为默认不拒绝
962+
963+
`vi /etc/kubernetes/epconfig/admission_configuration.json`
964+
965+
```json
966+
{
967+
968+
"imagePolicy": {
969+
"kubeConfigFile": "/etc/kubernetes/epconfig/kubeconfig.yaml",
970+
"allowTTL": 50,
971+
"denyTTL": 50,
972+
"retryBackoff": 500,
973+
"defaultAllow": false
974+
}
975+
}
976+
```
977+
978+
2. 修改控制器访问 webhook server 的 kubeconfig
979+
980+
`vi /etc/kubernetes/epconfig/kubeconfig.yaml`
981+
982+
修改如下内容
983+
984+
```yaml
985+
apiVersion: v1
986+
kind: Config
987+
clusters:
988+
- cluster:
989+
certificate-authority: /etc/kubernetes/epconfig/webhook.pem
990+
server: https://acme.local:8082/image_policy # web hook server 的地址
991+
name: bouncer_webhook
992+
# 以下省略
993+
```
994+
995+
3. 启用 ImagePolicyWebhook
996+
997+
`vi /etc/kubernetes/manifests/kube-apiserver.yaml`
998+
999+
```yaml
1000+
# 启用 ImagePolicyWebhook
1001+
- --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
1002+
# 指定准入控制器配置文件
1003+
- --admission-control-config-file=/etc/kubernetes/epconfig/admission_configuration.json
1004+
# mount
1005+
volumeMounts:
1006+
- mountPath: /etc/kubernetes/epconfig
1007+
name: epconfig
1008+
# 映射 volumes
1009+
volumes:
1010+
- name: epconfig
1011+
hostPath:
1012+
path: /etc/kubernetes/epconfig
1013+
```
1014+
1015+
4. 测试是否生效
1016+
1017+
```bash
1018+
systemctl daemon-reload
1019+
systemctl restart kubelet
1020+
kubectl apply -f /cks/img/web1.yaml
1021+
```
1022+
9361023
### 5.3 审计日志
9371024

9381025
在被攻击后追溯攻击者的行为也是重要的一部分的工作,如果 kube-apiserver 没有开启 audit 那么基本就很难追溯攻击者到底在 k8s 中做了什么,所以开启 audit 是非常重要的。审计日志有以下级别:
@@ -1003,39 +1090,201 @@ pod 安全策略是用过批量修改满足特定条件的 pod 的安全选项
10031090

10041091
但是值得注意的是这个功能[在 1.21 被标记为 deprecated,在 K8S 1.25 以后的版本中被彻底移除](https://kubernetes.io/docs/concepts/security/pod-security-policy/),所以我们应该尽快的使用 Admission Controllers 来替代 [Pod Security Policies](https://kubernetes.io/docs/concepts/security/pod-security-admission/) 。目前这并不是考试的内容。
10051092

1093+
实验如下:
1094+
1095+
1. 创建 psp
1096+
1097+
```yaml
1098+
apiVersion: policy/v1beta1
1099+
kind: PodSecurityPolicy
1100+
metadata:
1101+
name: restrict-policy
1102+
spec:
1103+
privileged: false
1104+
seLinux:
1105+
rule: RunAsAny
1106+
supplementalGroups:
1107+
rule: RunAsAny
1108+
runAsUser:
1109+
rule: RunAsAny
1110+
fsGroup:
1111+
rule: RunAsAny
1112+
volumes:
1113+
- '*'
1114+
```
1115+
1116+
2. 创建 clusterrole,使用 psp
1117+
1118+
`kubectl create clusterrole restrict-access-role --verb=use --resource=psp --resource-name=restrict-policy`
1119+
1120+
3. 创建 serviceaccount
1121+
1122+
`kubectl create sa psp-denial-sa -n staging`
1123+
1124+
4. 绑定 clusterrole 到 serviceaccount
1125+
1126+
`kubectl create clusterrolebinding dany-access-bind --clusterrole=restrict-access-role --serviceaccount=staging:psp-denial-sa`
1127+
1128+
5. 启用 PSP
1129+
1130+
```yaml
1131+
vi /etc/kubernetes/manifests/kube-apiserver.yaml
1132+
# 确保有以下内容:
1133+
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
1134+
```
1135+
10061136
### 5.6 服务账户令牌(Service Account Token)
10071137

10081138
这个是用来授权 pod 访问 kube-apiserver 的,所以这个 token 的安全性也是非常重要的,如果这个 token 泄露了,那么攻击者就可以通过这个 token 来做任何事情,所以我们应该定期的轮换这个 token,比如每个月轮换一次,这样即使这个 token 泄露了,比如由于不正确的配置导致攻击者将有 cluster-admin 权限的 service account 的 token 挂载到了 `恶意的 pod` 中, 那么他很容易通过 InClusterConfig() 方法轻易获取到访问 kube-apiserver 中所有的 api group 的所有的权限,他可以轻易删掉 node 对象和其他任何对象,对集群造成很大的破坏。
10091139

10101140
```bash
1011-
# 生成一个模板
1141+
# 生成一个模板
10121142
kubectl create sa correct-sa -n task2 --dry-run=client -o yaml > task2-temp-sa.yaml
1013-
# 确保配置如下
1143+
# 确保配置如下
10141144
vi task2-temp-sa.yaml
10151145
```
10161146

1147+
serviceaccount 有个选项 automountServiceAccountToken, 这个选项决定是否自动挂载 secret 到 pod。
1148+
1149+
有这个选项,我们可以控制 pod 创建并绑定 serviceaccount 时,不自动挂载对应的 secret,这样 pod 就没有权限访问 apiserver,提高了业务 pod 的安全性。
1150+
1151+
可以在 serviceaccount 和 pod 的 spec 里设置。serviceaccount 里的设置是全局的,pod 的设置会覆盖 serviceaccount 里的设置。
1152+
10171153
```yaml
10181154
# task2-temp-sa.yaml
10191155
apiVersion: v1
10201156
kind: ServiceAccount
10211157
metadata:
10221158
name: correct-sa
10231159
namespace: task2
1024-
automountServiceAccountToken: false # 注意这里要是 false 默认是 true
1160+
automountServiceAccountToken: false # 注意这里要是 false默认是 true
10251161
```
10261162

10271163
```bash
10281164
# 创建 sa
10291165
kubectl create -f task2-temp-sa.yaml
10301166
1031-
# 修改改 pod
1032-
# 将 serviceAccountName: sa-not-exist 噶成 serviceAccountName: correct-sa
1033-
vi /tmp/task2/pod.yaml
1167+
# 修改 pod
1168+
# 将 serviceAccountName: sa-not-exist 改成 serviceAccountName: correct-sa
1169+
vi /tmp/task2/pod.yaml
1170+
1171+
```yaml
1172+
apiVersion: v1
1173+
kind: Pod
1174+
metadata:
1175+
name: backend
1176+
namespace: qa
1177+
spec:
1178+
serviceAccountName: correct-sa
1179+
containers:
1180+
- image: nginx:1.9
1181+
imagePullPolicy: IfNotPresent
1182+
name: backend
1183+
```
1184+
1185+
### 5.7 lab
1186+
1187+
#### 5.7.1 lab1 审计日志
1188+
1189+
编写日志审计策略文件
1190+
1191+
```yaml
1192+
apiVersion: audit.k8s.io/v1
1193+
kind: Policy
1194+
omitStages:
1195+
- "RequestReceived"
1196+
rules:
1197+
- level: RequestResponse
1198+
resources:
1199+
- group: ""
1200+
resources: ["namespaces"]
1201+
1202+
- level: Request
1203+
resources:
1204+
- group: ""
1205+
resources: ["persistentvolumes"]
1206+
namespaces: ["front-apps"]
1207+
1208+
- level: Metadata
1209+
resources:
1210+
- group: ""
1211+
resources: ["secrets", "configmaps"]
1212+
1213+
- level: Metadata
1214+
omitStages:
1215+
- "RequestReceived"
1216+
```
1217+
1218+
修改 kube-apiserver.yaml 配置文件,启用日志审计策略,日志策略配置文件位置、日志文件存储位置、循环周期。
1219+
1220+
```bash
1221+
vi /etc/kubernetes/manifests/kube-apiserver.yaml
1222+
```
1223+
1224+
```yaml
1225+
# 设置日志审计策略文件在 pod 里的 mount 位置
1226+
- --audit-policy-file=/etc/kubernetes/logpolicy/sample-policy.yaml
1227+
1228+
# 设置日志文件存储位置
1229+
- --audit-log-path=/var/log/kubernetes/audit-logs.txt
1230+
1231+
# 设置日志文件循环
1232+
- --audit-log-maxage=10
1233+
- --audit-log-maxbackup=2
1234+
1235+
# mount 日志策略和日志文件的
1236+
volumeMounts:
1237+
- mountPath: /etc/kubernetes/logpolicy/sample-policy.yaml
1238+
name: audit
1239+
readOnly: true
1240+
- mountPath: /var/log/kubernetes/audit-logs.txt
1241+
name: audit-log
1242+
readOnly: false
1243+
volumes:
1244+
- name: audit
1245+
hostPath:
1246+
path: /etc/kubernetes/logpolicy/sample-policy.yaml
1247+
type: File
1248+
- name: audit-log
1249+
hostPath:
1250+
path: /var/log/kubernetes/audit-logs.txt
1251+
type: FileOrCreate
1252+
```
1253+
1254+
重启 API Server 来检查 audit.log
1255+
1256+
```bash
1257+
cd /etc/kubernetes/manifests/
1258+
mv kube-apiserver.yaml ..
1259+
watch crictl ps # wait for apiserver gone
1260+
truncate -s 0 /etc/kubernetes/audit/logs/audit.log
1261+
mv ../kube-apiserver.yaml .
1262+
1263+
cat audit.log | tail | jq
1264+
1265+
# shows Secret entries
1266+
cat audit.log | grep '"resource":"secrets"' | wc -l
1267+
1268+
# confirms Secret entries are only of level Metadata
1269+
cat audit.log | grep '"resource":"secrets"' | grep -v '"level":"Metadata"' | wc -l
1270+
1271+
# shows RequestResponse level entries
1272+
cat audit.log | grep -v '"level":"RequestResponse"' | wc -l
1273+
1274+
# shows RequestResponse level entries are only for system:nodes
1275+
cat audit.log | grep '"level":"RequestResponse"' | grep -v "system:nodes" | wc -l
1276+
```
10341277

10351278
# 创建 pod
10361279
kubectl create -f /tmp/task2/pod.yaml
1280+
```
1281+
1282+
自动挂载的 service token 的 pod 可以做很多事,比如 get secret
10371283
1038-
# all set
1284+
```bash
1285+
k -n restricted get pod -o yaml | grep automountServiceAccountToken
1286+
1287+
curl https://kubernetes.default/api/v1/namespaces/restricted/secrets -H "Authorization: Bearer $(cat /run/secrets/kubernetes.io/serviceaccount/token)" -k
10391288
```
10401289

10411290
## 6. 网络
@@ -1410,6 +1659,20 @@ apparmor module is loaded.
14101659

14111660
#### 7.3.1 lab1-trivy
14121661

1662+
trivy 的操作,在考试和实际使用场景中都是三板斧:
1663+
1664+
```bash
1665+
# 获取镜像名
1666+
kubect get pod XXXX -n kamino -o yaml | grep image
1667+
# 扫描镜像
1668+
trivy imagename | grep (HIGH|CRITICAL)
1669+
trivy nginx:1.16.1-alpine | grep -E 'CVE-2020-10878|CVE-2020-1967'
1670+
# 删除 pod
1671+
kubectl delete po XXXX
1672+
```
1673+
1674+
完整的实验如下:
1675+
14131676
分别使用镜像 `nginx:latest` 和 `alpine:latest` 启动 2 个 pod 在 namespace chapter-7 下, 然后用 trivy 对他们的镜像进行一个扫描
14141677

14151678
```bash
@@ -1505,15 +1768,17 @@ profile k8s-apparmor-chapter7-pod3-deny-write flags=(attach_disconnected) {
15051768
deny /** w,
15061769
}
15071770
EOF'
1771+
```
15081772

1773+
```yaml
15091774
cat <<EOF > chapter7-pod3.yaml
15101775
apiVersion: v1
15111776
kind: Pod
15121777
metadata:
15131778
name: chapter7-pod3
15141779
namespace: chapter-7
15151780
annotations:
1516-
container.apparmor.security.beta.kubernetes.io/chapter7-container3: localhost/k8s-apparmor-chapter7-pod3-deny-write # 添加这一行
1781+
container.apparmor.security.beta.kubernetes.io/chapter7-container3: localhost/k8s-apparmor-chapter7-pod3-deny-write # 添加这一行,该注解表示加载对应的 apparmor profile。
15171782
spec:
15181783
containers:
15191784
- name: chapter7-container3
@@ -1562,5 +1827,22 @@ kill chain 攻击的生命周期的细化 -- 我们细化为 7 个详细的步
15621827
AI 反入侵工具:通过 AI 来检测入侵,就如果反到系统一样,会比我们人类反应更快,经过大量学习后这些 AI 反入侵工具可以自动的检测出入侵,比如:
15631828

15641829
- [NeuVector](https://neuvector.com/)
1565-
- [StackRox](https://www.stackrox.com/)
1830+
- [StackRox](https://www.stackrox.com/)
15661831
- 其它
1832+
1833+
## 9. Exam Tips
1834+
1835+
用 k 代替 kubectl,减少敲键盘
1836+
1837+
```bash
1838+
alias k=kubectl
1839+
```
1840+
1841+
其它小窍门:
1842+
1843+
- 善用考试软件提供的 notepad 功能,先把 yaml 文件或命令写到 notepad 里,再粘贴到 terminal 里
1844+
- 别忘了 `kubectl apply -f`
1845+
- 别忘了 set context & ssh,别忘了退出 ssh
1846+
- 别忘了 log 文件一般放在外面
1847+
- 修改文件之前先 cp
1848+
- 修改 k8s 对象之前,先导出 yaml

0 commit comments

Comments
 (0)