一、为什么权限管控是Kubeflow Notebook Server绕不开的一道坎
Kubeflow Notebook Server这个东西,说白了就是让数据科学家在Kubernetes集群里像用本地Jupyter一样写代码、跑实验。它很方便,但方便归方便,权限管控这事儿如果没做扎实,分分钟能让你的集群变成"人人皆可访问"的透明仓库。
想象一下这个场景:你们团队有十个开发者,每人都有一个Notebook。结果某天有人发现,自己的Notebook里偷偷多了一个不认识的文件夹,里面的模型参数文件被删了。或者更糟的,有人通过Notebook的终端,直接kubectl到了集群的核心Namespace,把生产环境的配置给改了。
这些问题归根结底都是两件事没做好:环境隔离没到位,资源边界没管住。环境隔离是指每个人的Notebook应该跑在独立的容器里,彼此看不见、摸不着。资源越界访问是指Notebook里的进程不应该有能力去访问它不该碰的Kubernetes资源。
这篇文章就来聊聊怎么排查这两个问题,以及怎么加固。我会用实际案例和可运行的命令带你走一遍,不是那种"理论上应该这样"的空谈。
二、环境隔离机制的工作原理
2.1 隔离是层层递进的
Kubeflow Notebook Server的环境隔离不是一步到位的,而是分了好几层。理解这层关系,你排查问题时才能知道该从哪一层入手。
第一层是容器级隔离。每个Notebook运行在一个独立的Pod里,Pod里的容器由Docker或containerd管理。这是最基础的隔离,操作系统内核通过cgroups和namespace提供隔离能力。
第二层是Namespace级隔离。Kubeflow会给每个用户或者每个Notebook分配独立的Kubernetes Namespace。Namespace是Kubernetes里最核心的逻辑隔离单位,不同Namespace之间的资源默认是不互通的。
第三层是RBAC级隔离。RoleBased Access Control(RBAC)控制着谁能对Namespace里的资源做什么操作。Notebook Server会给每个用户的ServiceAccount绑定特定的Role或ClusterRole。
第四层是NetworkPolicy级隔离。NetworkPolicy控制Pod之间的网络访问。没有配置NetworkPolicy的集群,默认所有Pod都可以互相访问。
这四层里面,任何一层出了问题,都会导致隔离失败。排查的时候,你需要逐层检查。
2.2 一个典型的Notebook Pod结构长什么样
# 技术栈:Kubernetes + Kubeflow
# 以下是一个Kubeflow Notebook Server生成的Pod配置示例
# 注意:这是简化后的结构,实际生成的YAML会更复杂
apiVersion: v1
kind: Pod
metadata:
name: my-notebook-7b8f9c
namespace: user-alice-workspace # 每个用户有自己的Namespace
labels:
app.kubernetes.io/component: notebook
user: alice
annotations:
kubeflow.org/notebook-uri: "http://user-alice-workspace.svc.cluster.local"
spec:
# ServiceAccount决定了Notebook进程能访问哪些Kubernetes API资源
serviceAccountName: notebook-sa-alice
containers:
- name: notebook
image: gcr.io/kubeflow-images-public/notebook-servers/jupyter-pytorch:latest
ports:
- containerPort: 8888
name: http
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "4Gi"
volumeMounts:
- name: jupyter-home
mountPath: /home/jovyan
- name: persistent-volume
mountPath: /mnt/data
readOnly: false # 读写挂载,注意这里不是只读
volumes:
- name: jupyter-home
emptyDir: {}
- name: persistent-volume
persistentVolumeClaim:
claimName: alice-data-pvc
# 安全上下文:限制容器内进程的特权
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 3000
allowPrivilegeEscalation: false # 禁止提权
上面这个配置里,有几个关键点值得注意:
serviceAccountName: notebook-sa-alice决定了Notebook里的进程能以什么身份调用Kubernetes API。如果这个ServiceAccount绑定了过大的权限,Notebook就能干出很多不该干的事。allowPrivilegeEscalation: false是一个安全加固点。如果没设置,容器里的进程可能会通过提权方式获取更高权限。readOnly: false表示挂载的数据卷是可读写的。如果你的场景不需要写入,改成true能减少风险面。
三、资源越界访问的常见场景与排查方法
3.1 场景一:Notebook能访问其他用户的Pod
这是最常见的问题。两个用户各自有Namespace,但其中一个Notebook的进程能通过Kubernetes API List或者Get另一个Namespace里的资源。
排查方法如下:
# 技术栈:Kubernetes + Kubeflow
# 第一步:确认当前用户的ServiceAccount及其绑定的权限
# 查看alice的Notebook使用的ServiceAccount
kubectl get pod -n user-alice-workspace my-notebook -o jsonpath='{.spec.serviceAccountName}'
# 查看这个ServiceAccount绑定了哪些Role/ClusterRole
kubectl get rolebinding -n user-alice-workspace -o wide | grep notebook-sa-alice
kubectl get clusterrolebinding -o wide | grep notebook-sa-alice
如果输出里看到了cluster-admin或者类似的过大权限,问题基本就找到了。
进一步验证,直接到Notebook里执行命令看能访问到什么:
# 技术栈:Kubernetes + Kubeflow
# 进入Notebook的容器终端,执行以下命令测试权限边界
# 列出当前能访问的Namespace
kubectl get namespaces
# 尝试访问另一个用户的Namespace
kubectl get pods -n user-bob-workspace
# 尝试创建资源(如果权限过大,这一步就能成功,那就危险了)
kubectl create deployment test-pod -n user-bob-workspace --image=nginx --dry-run=server
如果Notebook能List或者Get其他Namespace的资源,说明RBAC配置出了问题。正常情况下,每个Notebook的ServiceAccount只应该能访问自己Namespace内的资源。
3.2 场景二:Notebook能读写其他用户的持久化存储
PVC(PersistentVolumeClaim)的权限管控如果没做好,用户A的Notebook可能挂载到用户B的数据卷上。这比访问Pod更严重,因为数据是持久化的,泄露出去可能回不来了。
排查方法:
# 技术栈:Kubernetes + Kubeflow
# 第一步:查看所有PVC的绑定关系和访问模式
# 列出所有PVC
kubectl get pvc -A -o wide
# 查看PVC的访问模式,ReadWriteMany(RWX)意味着多个Pod可以同时读写
kubectl get pvc -n user-alice-workspace -o jsonpath='{.items[*].spec.accessModes}'
kubectl get pvc -n user-bob-workspace -o jsonpath='{.items[*].spec.accessModes}'
# 检查是否有多人共享的PVC被挂载到了不同Namespace的Pod里
kubectl get pods -A -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for pod in data['items']:
ns = pod['metadata']['namespace']
name = pod['metadata']['name']
if 'notebook' not in name:
continue
# 收集该Pod挂载的所有PVC名称
volumes = pod['spec'].get('volumes', [])
for v in volumes:
if 'persistentVolumeClaim' in v:
pvc = v['persistentVolumeClaim']['claimName']
print(f'Namespace: {ns:30s} | Pod: {name:30s} | PVC: {pvc}')
"
如果输出里出现了同一个PVC被不同Namespace的Pod挂载,而且访问模式是ReadWriteMany,那就说明存储隔离有问题。理想情况下,每个用户应该有自己的PVC,或者至少PVC的挂载应该被RBAC限制在自己Namespace内。
3.3 场景三:通过Notebook的终端命令直接操作Kubernetes集群
这个场景比较隐蔽。用户在Notebook的Terminal里直接运行kubectl命令,如果能执行成功,说明Notebook容器内的kubectl配置和ServiceAccount权限过大。
# 技术栈:Kubernetes + Kubeflow
# 检查Notebook容器内是否预置了kubeconfig文件
# 进入Notebook容器
kubectl exec -it -n user-alice-workspace my-notebook -- sh
# 在容器内执行:
# 检查HOME目录下的kubeconfig
ls -la ~/.kube/
# 检查环境变量中是否有kubeconfig相关变量
env | grep -i kube
# 检查默认上下文
kubectl config current-context
kubectl config view
如果Notebook里预置了~/.kube/config文件,或者环境变量里设置了KUBECONFIG,那么用户在Notebook终端里直接运行kubectl就会生效。更危险的是,如果这个kubeconfig对应的用户权限是cluster-admin,用户就能操作整个集群。
四、实操排查:一步步定位权限漏洞
4.1 第一步:审计现有Notebook的ServiceAccount权限
在动手加固之前,先搞清楚现在到底有多少Notebook,每个Notebook绑定了什么权限。
# 技术栈:Kubernetes + Kubeflow
# 第一步:列出所有Kubeflow Notebook的Pod
# 查找所有带有kubeflow标签的Pod
kubectl get pods -A -l app.kubernetes.io/component=notebook -o wide
# 输出示例:
# NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE READY
# user-alice-workspace my-notebook-7b8f9c 1/1 Running 0 3h 10.244.1.15 node-1 1/1
# user-bob-workspace pytorch-notebook-5f4e3 1/1 Running 0 1d 10.244.2.32 node-2 1/1
# user-charlie-workspace tf-notebook-8c7d6b 1/1 Running 0 2d 10.244.3.45 node-3 1/1
拿到Pod列表后,检查每个Pod的ServiceAccount权限:
# 技术栈:Kubernetes + Kubeflow
# 对每个Notebook Pod,检查其ServiceAccount的权限
for pod in $(kubectl get pods -A -l app.kubernetes.io/component=notebook -o jsonpath='{.items[*].metadata.namespace}/{.items[*].metadata.name}'); do
ns=${pod%%/*}
name=${pod#*/}
sa=$(kubectl get pod -n "$ns" "$name" -o jsonpath='{.spec.serviceAccountName}')
echo "=== Namespace: $ns | Pod: $name | ServiceAccount: $sa ==="
# 查看该SA在Namespace内的RoleBinding
kubectl get rolebinding -n "$ns" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null | \
tr ' ' '\n' | while read rb; do
if kubectl get rolebinding "$rb" -n "$ns" -o jsonpath='{.subjects[*].name}' | grep -q "$sa"; then
role=$(kubectl get rolebinding "$rb" -n "$ns" -o jsonpath='{.roleRef.name}')
echo " RoleBinding: $rb -> Role: $role"
# 打印Role的权限详情
kubectl get role "$role" -n "$ns" -o jsonpath='{range .rules[*]}{.verbs[*]} {""} {.resources[*]}{""} {""} {.apiGroups[*]}\n{end}'
fi
done
# 查看该SA的ClusterRoleBinding(如果有)
kubectl get clusterrolebinding -o jsonpath='{.items[*].metadata.name}' 2>/dev/null | \
tr ' ' '\n' | while read crb; do
if kubectl get clusterrolebinding "$crb" -o jsonpath='{.subjects[*].name}' | grep -q "$sa"; then
crole=$(kubectl get clusterrolebinding "$crb" -o jsonpath='{.roleRef.name}')
echo " ClusterRoleBinding: $crb -> ClusterRole: $crole"
fi
done
echo ""
done
这个脚本会输出每个Notebook Pod的ServiceAccount、绑定的Role/ClusterRole以及具体权限规则。如果看到某个Notebook绑定了cluster-admin,那就立刻去处理。
4.2 第二步:检测NetworkPolicy配置
没有NetworkPolicy的集群,所有Pod之间默认是互通的。也就是说,alice的Notebook可以直接访问bob的Notebook的HTTP端口,拿到bob的Notebook页面内容。
# 技术栈:Kubernetes + Kubeflow
# 检查每个用户的Namespace下是否有NetworkPolicy
# 列出所有Namespace的NetworkPolicy
kubectl get networkpolicy -A
# 如果输出为空,说明整个集群没有NetworkPolicy,所有Pod互通
# 如果只有部分Namespace有,其他Namespace仍然处于"默认可达"状态
排查某个Namespace是否需要NetworkPolicy:
# 技术栈:Kubernetes + Kubeflow
# 查看特定Namespace的Pod网络连通性
# 列出user-alice-workspace下的所有Pod IP
alice_pods=$(kubectl get pods -n user-alice-workspace -o jsonpath='{.items[*].status.podIP}')
# 列出user-bob-workspace下的所有Pod IP
bob_pods=$(kubectl get pods -n user-bob-workspace -o jsonpath='{.items[*].status.podIP}')
echo "Alice的Pod IP: $alice_pods"
echo "Bob的Pod IP: $bob_pods"
# 如果两个Namespace之间没有NetworkPolicy限制
# 从alice的Pod可以直接ping或者curl bob的Pod
# 在集群内部执行(需要有调试Pod):
# kubectl exec -it -n user-alice-workspace my-notebook -- curl http://bob-notebook-ip:8888/
# 如果能返回Notebook页面,说明网络隔离没做好
4.3 第三步:检查安全上下文配置
容器级的安全配置如果没设对,用户在容器内可以执行提权操作,获取超出预期的能力。
# 技术栈:Kubernetes + Kubeflow
# 检查所有Notebook Pod的安全上下文配置
kubectl get pods -A -l app.kubernetes.io/component=notebook -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
print('========== 安全上下文检查报告 ==========')
print()
for pod in data['items']:
ns = pod['metadata']['namespace']
name = pod['metadata']['name']
spec = pod['spec']
print(f'Pod: {ns}/{name}')
# 检查Pod级别的securityContext
pod_sc = spec.get('securityContext', {})
if not pod_sc:
print(' [警告] 未配置Pod级别securityContext')
for container in spec.get('containers', []):
cname = container['name']
c_sc = container.get('securityContext', {})
# 检查是否允许提权
if not c_sc.get('allowPrivilegeEscalation', True):
print(f' [安全] 容器{cname}: 禁止提权')
else:
print(f' [警告] 容器{cname}: 允许提权(allowPrivilegeEscalation未设为false)')
# 检查是否以root运行
run_user = c_sc.get('runAsUser', 'default')
if run_user == 0 or run_user == '0':
print(f' [警告] 容器{cname}: 以root用户运行(runAsUser=0)')
else:
print(f' [安全] 容器{cname}: 以非root用户运行(runAsUser={run_user})')
# 检查capabilities
caps = c_sc.get('capabilities', {})
if 'drop' in caps:
print(f' [安全] 容器{cname}: 已丢弃capabilities: {caps[\"drop\"]}')
else:
print(f' [警告] 容器{cname}: 未丢弃任何capabilities')
print()
print('========================================')
"
这段脚本会输出一份安全检查报告,告诉你哪些Pod存在安全风险。重点关注那些"以root用户运行"和"允许提权"的容器。
五、权限管控最佳实践与加固方案
5.1 RBAC最小权限原则配置
RBAC加固的核心思路是:每个Notebook的ServiceAccount只拥有完成其工作所需的最小权限集合。
# 技术栈:Kubernetes + Kubeflow
# RoleDefinition:定义Notebook容器需要的最小权限
# 只允许读取和写入自己Namespace内的资源
# 不允许任何集群级别的资源操作
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: notebook-minimal-role
namespace: user-alice-workspace
rules:
# 允许List和Get自己Namespace内的所有资源
# 这是Notebook正常运行所需的基础权限
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets", "persistentvolumeclaims"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets"]
verbs: ["get", "list"]
# 允许创建和更新Job(用于数据任务提交)
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "create", "update", "delete"]
# 允许更新自己Namespace内的Service(用于暴露端口)
- apiGroups: [""]
resources: ["services"]
verbs: ["create", "patch"]
# 不允许ClusterRoleBinding级别的任何权限
# 不允许读取其他Namespace的资源
---
# RoleBinding:将Role绑定到用户的ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: notebook-minimal-role-binding
namespace: user-alice-workspace
subjects:
- kind: ServiceAccount
name: notebook-sa-alice
namespace: user-alice-workspace
roleRef:
kind: Role
name: notebook-minimal-role
apiGroup: rbac.authorization.k8s.io
注意上面的Role规则里,没有包含cluster-admin相关的规则,也没有允许跨Namespace的操作。所有权限都被限制在user-alice-workspace这个Namespace内部。
5.2 NetworkPolicy加固配置
# 技术栈:Kubernetes + Kubeflow
# 默认拒绝所有入站和出站流量
# 只开放Notebook需要的端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: notebook-default-deny
namespace: user-alice-workspace
spec:
# 选择所有Notebook Pod
podSelector:
matchLabels:
app.kubernetes.io/component: notebook
# 默认拒绝所有入站和出站流量
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: notebook-allow-traffic
namespace: user-alice-workspace
spec:
podSelector:
matchLabels:
app.kubernetes.io/component: notebook
policyTypes:
- Ingress
- Egress
ingress:
# 允许HTTP端口8888的入站流量(用户浏览器访问)
- from:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 8888
# 允许从API Server的入站流量(用于Webhook)
- from:
- podSelector:
matchLabels:
k8s-app: kube-apiserver
ports:
- protocol: TCP
port: 8888
egress:
# 允许DNS解析
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
# 允许访问Kubernetes API Server(端口6443)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 6443
# 允许访问持久存储的网络端口(根据存储后端不同而不同)
- to:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 2049 # NFS
这份配置的效果是:Notebook Pod之间默认不可互访,只允许用户浏览器访问HTTP端口,只允许DNS解析和Kubernetes API调用。如果Notebook需要访问外部网络(比如下载依赖包),你需要额外添加对应的出站规则。
5.3 安全上下文加固配置
# 技术栈:Kubernetes + Kubeflow
# 在Notebook的Deployment或StatefulSet中配置安全上下文
# 以Jupyter Notebook为例
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-notebook
namespace: user-alice-workspace
spec:
replicas: 1
selector:
matchLabels:
app: notebook
template:
metadata:
labels:
app: notebook
app.kubernetes.io/component: notebook
spec:
serviceAccountName: notebook-sa-alice
# Pod级别的securityContext
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 3000
runAsNonRoot: true # 强制以非root用户运行
seccompProfile:
type: RuntimeDefault # 启用seccomp安全配置文件
containers:
- name: notebook
image: gcr.io/kubeflow-images-public/notebook-servers/jupyter-pytorch:latest
ports:
- containerPort: 8888
# 容器级别的securityContext
securityContext:
allowPrivilegeEscalation: false # 禁止提权
readOnlyRootFilesystem: true # 根文件系统只读
capabilities:
drop:
- ALL # 丢弃所有Linux capabilities
volumeMounts:
- name: writable-home
mountPath: /home/jovyan
# 允许写入的目录挂载为可写卷
volumes:
- name: writable-home
emptyDir: {}
关键加固点说明:
runAsNonRoot: true确保容器不会以root身份运行allowPrivilegeEscalation: false阻止进程通过setuid等方式提权readOnlyRootFilesystem: true让根文件系统只读,防止恶意写入capabilities: drop: ALL丢弃所有Linux capabilities,减少攻击面
六、应用场景与技术优缺点分析
6.1 应用场景
权限管控这套东西,在以下几种场景里尤其需要认真对待。
第一种是多团队共享集群。你们公司可能有数据科学组、机器学习组、工程组,大家都用同一个Kubernetes集群跑Notebook。这种情况下,组间隔离是必须做的,否则A组能偷偷改B组的实验数据。
第二种是客户隔离场景。如果你的Kubeflow是给多个外部客户用的(比如SaaS服务),每个客户的Notebook必须严格隔离,不能有任何越界访问的可能性。这涉及到数据合规和法律责任。
第三种是合规审计场景。如果你的公司通过了等保、SOC2、ISO27001之类的合规认证,审计的时候会检查你的集群权限配置。Notebook的权限管控是审计的必检项。
6.2 技术优缺点
优点:
Kubeflow Notebook Server的权限管控机制有几个明显的好处。第一,它利用了Kubernetes原生的Namespace和RBAC机制,不需要引入额外的第三方组件,运维成本低。第二,权限配置以YAML文件形式存在,可以纳入Git版本管理,做到可追溯、可审计。第三,NetworkPolicy和安全上下文是声明式的,一旦配置好就自动生效,不依赖人工操作。
缺点:
但也有不少坑。第一,RBAC配置比较复杂,尤其是当你需要精细控制每个用户的权限时,Role和RoleBinding的数量会爆炸,维护起来很头疼。第二,NetworkPolicy的配置容易出错,一个错误的规则可能导致Notebook完全无法访问外部资源(比如没法下载pip包)。第三,安全上下文加固过度可能会影响Notebook的正常运行,比如某些Python库需要特定的系统调用,drop ALL capabilities可能会导致库加载失败。
6.3 注意事项
几个容易踩的坑提前说清楚:
坑一:ServiceAccount Token挂载。 默认情况下,Kubernetes会把ServiceAccount Token挂载到容器的/var/run/secrets/kubernetes.io/serviceaccount/目录。如果Notebook的ServiceAccount权限过大,容器内的任何进程都能读取这个Token并调用Kubernetes API。加固方式是在PodSpec里设置automountServiceAccountToken: false,然后通过InitContainer或者Webhook动态注入只包含必要权限的Token。
坑二:Sidecar容器的权限。 有些Notebook镜像会启动Sidecar容器(比如日志采集器、监控代理)。这些Sidecar的权限配置经常被忽略。一个权限过大的Sidecar,效果等同于整个Notebook都获得了过大权限。
坑三:PVC的权限配置。 上面提到的PVC隔离问题,除了访问模式配置,还有StorageClass的权限。某些StorageClass会创建共享的PV,导致多个Namespace的Pod挂载到同一个存储后端。
坑四:Kubeflow自身的权限配置。 Kubeflow的Jupyter WebApp组件(也就是Notebook的管理界面)有自己的ServiceAccount,这个SA的权限也需要仔细审查。如果这个SA能访问所有用户的Namespace,WebApp被攻破就意味着所有Notebook都不安全。
七、补充说明:关联技术详解
7.1 Kubernetes RBAC的工作原理
RBAC是Kubernetes权限控制的基石。它由四个核心对象组成:Role、ClusterRole、RoleBinding、ClusterRoleBinding。
Role定义了在某个Namespace内能做什么操作。ClusterRole定义的是集群级别的权限。RoleBinding把Role绑定到用户(User、ServiceAccount或Group),只在某个Namespace内生效。ClusterRoleBinding把ClusterRole绑定到用户,在整个集群范围内生效。
# 技术栈:Kubernetes + Kubeflow
# RBAC权限验证的实操示例
# 检查某个ServiceAccount是否有特定权限
# 验证notebook-sa-alice是否能List user-bob-workspace的Pods
# 预期结果:应该被拒绝(因为alice的SA不应该有跨Namespace权限)
kubectl auth can-i list pods -n user-bob-workspace \
--as=system:serviceaccount:user-alice-workspace:notebook-sa-alice
# 验证notebook-sa-alice是否能List自己Namespace的Pods
# 预期结果:应该被允许
kubectl auth can-i list pods -n user-alice-workspace \
--as=system:serviceaccount:user-alice-workspace:notebook-sa-alice
# 批量检查所有Notebook ServiceAccount的跨Namespace访问能力
for ns in $(kubectl get ns -o name | grep user-); do
for sa in $(kubectl get sa -n $ns -o name | grep notebook-sa); do
sa_name=$(echo $sa | sed 's|/||')
for target_ns in $(kubectl get ns -o name | grep user-); do
if [ $ns != $target_ns ]; then
result=$(kubectl auth can-i get pods -n $target_ns \
--as=system:serviceaccount:${ns##*/}:$sa_name 2>/dev/null)
if [ "$result" == "yes" ]; then
echo "[越界] $sa_name can access $target_ns/pods"
fi
fi
done
done
done
这个脚本会扫描所有Notebook的ServiceAccount,检查是否存在跨Namespace访问权限。如果有输出,说明存在越界风险。
7.2 动态审计日志
除了主动排查,还可以配置Kubernetes的审计日志来被动监控异常操作。
# 技术栈:Kubernetes + Kubeflow
# 配置Kubernetes审计日志,记录Notebook相关的敏感操作
# 添加到API Server的配置文件(如kubeadm生成的/apiserver.yaml)
apiVersion: apiserver.config.k8s.io/v1
kind: AuditPolicy
rules:
# 记录所有对Notebook资源的修改操作
- level: RequestResponse
verbs: ["create", "update", "patch", "delete"]
resources:
- pods
- deployments
- statefulsets
- persistentvolumeclaims
- services
namespaces: ["user-alice-workspace", "user-bob-workspace"]
# 记录RBAC相关的操作
- level: RequestResponse
verbs: ["create", "update", "delete"]
resources:
- roles
- rolebindings
- clusterroles
- clusterrolebindings
# 记录对secrets的读取操作(可能被用于窃取Token)
- level: Metadata
verbs: ["get", "list"]
resources:
- secrets
配置好审计日志后,用Fluentd或者其他日志工具采集审计日志,设置告警规则。比如:某个Notebook的ServiceAccount在短时间内大量List其他Namespace的Pod,可以触发告警。
八、文章总结
Kubeflow Notebook Server的权限管控,本质上就是在"方便开发者使用"和"保证安全隔离"之间找平衡。完全放开权限,Notebook好用但集群不安全;管得太死,Notebook能跑但开发者被限制得很难受。
核心要点回顾:
第一,环境隔离要逐层检查。从容器级到Namespace级到RBAC级到NetworkPolicy级,每一层都要确认配置正确。不能只做一层就以为安全了。
第二,RBAC遵循最小权限原则。Notebook的ServiceAccount只应该能访问自己Namespace内的资源,不要给集群级别的权限。定期用kubectl auth can-i命令验证权限边界。
第三,NetworkPolicy必须配置。没有NetworkPolicy的集群,Pod之间默认全通。至少要给Notebook所在的Namespace加上默认拒绝策略,再按需开放必要端口。
第四,安全上下文不要省略。runAsNonRoot、allowPrivilegeEscalation: false、readOnlyRootFilesystem: true这几个配置建议作为Notebook部署的默认值。
第五,定期审计。用脚本定期扫描所有Notebook的权限配置,检查是否有越界风险。配合审计日志做被动监控,异常操作能及时发现。
第六,存储隔离别忘。PVC的访问模式和StorageClass配置也要检查,避免不同用户的Notebook挂载到同一个存储后端。
权限管控不是一次性的工作,而是一个持续的过程。随着团队规模扩大、Notebook数量增加,权限配置会越来越复杂。建立一套自动化的权限检查和审计流程,比每次人工排查要高效得多。
评论
围绕“Kubeflow Notebook Server权限管控:排查用户环境隔离与资源越界访问风险”参与讨论