如何使用 Traefik 启用 PSP
2019 年 1 月 24 日 | Panagiotis Georgiadis | 无许可
如果您正在阅读本文,很可能是因为您已经了解 Traefik 是什么,并且希望在不以 root 用户身份(主进程 pid 1)或任何其他特权用户身份运行它时使用它。如果安全性是您非常关心的问题,启用 Kubernetes PodSecurityPolicy - PSP 被认为是安全机制的最佳实践之一。
为此,我们一直在尝试构建一个使用 libcap-progs 和 authbind 配置的 Traefik 容器镜像。如果您足够好奇,可以阅读我们的 源代码。最后但并非最不重要的一点,此镜像目前不是 openSUSE 的官方组成部分(尚未),但在进行了一些测试(是的,我们测试我们的容器镜像)之后,我们希望将其加入其中。
如何使用它
首先,您需要一个可用的 Kubernetes 集群。如果您想跟随本指南,您应该自己设置一个集群。这意味着使用 openSUSE Kubic 配合 kubeadm 或遵循上游 Traefik 指令。您也可以在您的机器上使用 minikube,因为它是获取本地 Kubernetes 集群进行实验和开发的最快方法。最终,我们假设您的系统中已经安装了 kubectl 二进制文件。
$ sudo zypper in minikube kubernetes-client
$ minikube start --vm-driver=kvm2
Starting local Kubernetes v1.13.2 cluster...
Starting VM...
Downloading Minikube ISO
181.48 MB / 181.48 MB [============================================] 100.00% 0s
Getting VM IP address...
Moving files into cluster...
Downloading kubeadm v1.13.2
Downloading kubelet v1.13.2
Finished Downloading kubeadm v1.13.2
Finished Downloading kubelet v1.13.2
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Stopping extra container runtimes...
Starting cluster components...
Verifying kubelet health ...
Verifying apiserver health ...
Kubectl is now configured to use the cluster.
Loading cached images from config file.
Everything looks great. Please enjoy minikube!
现在,您的客户端和集群应该已经配置好了
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 103s v1.13.2
授权 Traefik 使用您的 Kubernetes 集群
新的 kubernetes 版本正在使用 RBAC(基于角色的访问控制),它允许 Kubernetes 资源以受控方式与其 API 通信。有两种方法可以设置权限,以允许 Traefik 资源与 k8s API 通信
- 通过 RoleBinding(命名空间特定)
- 通过 ClusterRoleBinding(全局,适用于所有命名空间)
为了简单起见,我们将使用 ClusterRoleBinding 以在集群级别和所有命名空间中授予权限。以下 ClusterRoleBinding 允许属于 ServiceAccount 的任何用户或资源使用 traefik ingress controller。
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress-controller
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['traefik-ingress-controller']
- apiGroups:
- ""
resources:
- pods
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: kube-system
$ kubectl apply -f rbac.yaml
serviceaccount/traefik-ingress-controller created
clusterrole.rbac.authorization.k8s.io/traefik-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/traefik-ingress-controller created
请注意,ClusterRole 定义的一部分是强制其 use(动词)podsecuritypolicy。
启用 PSP
我们将启用一个 PodSecurityPolicy,禁止 root 用户运行我们的 Traefik 容器
---
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
name: traefik-ingress-controller
spec:
allowedCapabilities:
- NET_BIND_SERVICE
privileged: false
allowPrivilegeEscalation: true
# Allow core volume types.
volumes:
- 'configMap'
- 'secret'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
# Require the container to run without root privileges.
rule: 'MustRunAsNonRoot'
supplementalGroups:
rule: 'MustRunAs'
ranges:
# Forbid adding the root group.
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
# Forbid adding the root group.
- min: 1
max: 65535
readOnlyRootFilesystem: false
seLinux:
rule: 'RunAsAny'
hostPorts:
- max: 65535
min: 1
$ kubectl apply -f podsecuritypolicy.yaml
podsecuritypolicy.extensions/traefik-ingress-controller created
您可以通过输入以下命令来验证它是否已加载
$ kubectl get psp
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
traefik-ingress-controller false NET_BIND_SERVICE RunAsAny MustRunAsNonRoot MustRunAs MustRunAs false configMap,secret
部署我们的实验性 Traefik 镜像
我将使用 NodePort 作为 deployment 类型 部署 Traefik。
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: traefik-ingress-controller
namespace: kube-system
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 60
containers:
- image: registry.opensuse.org/devel/kubic/containers/container/kubic/traefik:1.7
name: traefik-ingress-lb
ports:
- name: http
containerPort: 80
- name: admin
containerPort: 8080
args:
- --api
- --kubernetes
- --logLevel=INFO
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 38
正如您所见,我们的 Traefik 镜像正在使用 authbind 和 setcap 来启用一个普通用户(具有 38 uid)来打开低于 1024 的端口。
$ kubectl apply -f deployment.yaml
deployment.extensions/traefik-ingress-controller created
要验证它是否正在运行,请列出 kube-system 命名空间中的 Pod
kubectl -n kube-system get pods | grep traefik
traefik-ingress-controller-87cbbbfb7-stlzm 1/1 Running 0 41s
此外,您还可以查询部署
$ kubectl -n kube-system get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
coredns 2/2 2 2 106m
traefik-ingress-controller 1/1 1 1 27m
要验证 Traefik 是否以普通用户(名称应为 traefik,UID 为 38)运行
traefikpod=$(kubectl -n kube-system get pods | grep traefik | awk '{ print $1 }')
kubectl -n kube-system exec -it $traefikpod -- whoami && id
traefik
uid=1000(tux) gid=100(users) groups=100(users),469(docker),472(libvirt),474(qemu),475(kvm),1003(osc)
到目前为止,我们还没有任何服务来访问它。它只是 Pod 的一部分,该 Pod 是部署的一部分。
$ kubectl -n kube-system expose deployment traefik-ingress-controller --target-port=80 --type=NodePort
service/traefik-ingress-controller exposed
您可以通过查询 kube-system 命名空间下的服务来验证这一点
$ kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 107m
traefik-ingress-controller NodePort 10.105.27.208 <none> 80:31308/TCP,8080:30815/TCP 2s
我们看到 traefik-ingress-controller 服务正在每个节点的端口 31308 上可用 – 端口号在您的集群中可能不同。因此,外部 IP 是我们集群中任何节点的 IP。您现在应该能够通过请求端口 31308 在 Minikube 集群的端口 80 上访问 Traefik
$ curl $(minikube ip):31308
404 page not found
注意:我们预计在这里会看到 404 响应,因为我们尚未为 Traefik 提供任何配置。
最后一步是创建一个 Service 和一个 Ingress,以暴露 Traefik Web UI。从现在开始,您可以实际使用官方 Traefik 文档
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/ui.yaml
现在让我们在 /etc/hosts 文件中设置一个条目,将 traefik-ui.minikube 路由到我们的集群。
在生产环境中,您需要设置真实的 DNS 条目。您可以通过运行 minikube ip 来获取 minikube 实例的 IP 地址
echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts
我们现在应该能够在浏览器中访问 traefik-ui.minikube:<NODEPORT> 并查看 Traefik web UI。

现在,您应该能够继续阅读 官方 traefik 文档 并执行所有很酷的事情,但具有更好的安全性。
更有趣吗?
如果您正在使用使用 Kubic 的完整 Kubernetes 集群(意味着:您可以使用多个节点),请随时在您的超visor 上设置一个 LoadBalancer,该超visor 托管您的 Kubic 虚拟机
sudo zypper in nginx
cat /etc/nginx/nginx.conf
load_module '/usr/lib64/nginx/modules/ngx_stream_module.so';
events {
worker_connections 1024;
}
stream {
upstream stream_backend {
# server <IP_ADDRESS_OF_KUBIC_NODE>:<TRAEFIK_NODEPORT>;
server worker-0.kubic-init.suse.net:31380;
server worker-1.kubic-init.suse.net:31380;
server worker-2.kubic-init.suse.net:31380;
}
server {
listen ultron.suse.de:80;
proxy_pass stream_backend;
}
}
然后启动负载均衡器:sudo systemctl start nginx。
这意味着访问我的机器的任何人(在本例中为 ultron.suse.de)将被重定向到我的 Kubernetes 节点之一,端口为 nodeport (31380)。
玩得开心
类别: 博客
标签