确认在OpenShift上无法正常运行监听TCP80端口的容器

目标

OpenShift 默认情况下不允许运行监听端口80的容器(我有点觉得这是指1023以下的众所周知的端口),我尝试去谷歌了一下,但是没有找到相关信息(找不到),所以我将再次确认。

使用过的文件和文件夹

使用从以下存储库中构建的内容

 

确认事情不可动的实验

创建实验用容器

请构建以下 Dockerfile

# FROM registry.access.redhat.com/ubi8/ubi:latest
FROM redhat/ubi8
MAINTAINER "yuhkih" 

RUN dnf install -y nginx
COPY index.html /usr/share/nginx/html/index.html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

CMD ["-g","daemon off;"]
ENTRYPOINT ["nginx"]

建筑

docker build . -t yuhkih/nginx-port80:latest 

将文件推送到Docker Hub

 docker push yuhkih/nginx-port80 

以上是实验用容器的准备工作已完成。

跑 OpenShift 功能。

创建一个新的OpenShift项目

用普通用户身份登录。

oc login https://api.f5cluster.m51o.p1.openshiftapps.com:6443  -u yuhkih -p xxxxxxx 

建立一个项目。

oc new-project testp

使用oc new-app部署容器。

oc new-app --image=docker.io/yuhkih/nginx-port80 -l=app=test

Pod 出现错误。

$ oc get pods
NAME                            READY   STATUS             RESTARTS       AGE
nginx-port80-78f75798bc-qjftq   0/1     CrashLoopBackOff   7 (5m2s ago)   15m
$ 

查看日志。权限被拒绝。

$ oc logs nginx-port80-78f75798bc-qjftq
2023/06/26 02:43:10 [emerg] 1#0: bind() to 0.0.0.0:80 failed (13: Permission denied)
nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)
$ 

给予权限并试着运行一下

在cluster-admin中的操作

我們的目標是讓另一個端口進行監聽,但在運行時提供合適的安全上下文約束(Security Context Contrain)來進行測試。

將權限授予項目的管理員是集群管理員的工作,因此我們將使用具有cluster-admin權限的用戶進行操作。

Pod的权限与启动Pod的Service Account相关联。如果不做任何操作,默认使用名为”default”的Service Account运行。

首先,创建一个新的服务帐户来运行此 Pod。

用 cluster-admin 用户登录。

oc login https://api.f5cluster.m51o.p1.openshiftapps.com:6443  -u cluster-admin -p xxxxxxx 

将当前工程切换到testp项目中。

oc project testp

创建一个名为 testsa 的服务账户。

oc create sa testsa

给服务账户分配 SCC 的任意权限。

$ oc adm policy add-scc-to-user anyuid -z testsa
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "saport80"
$ 

SCC和SA的关联在clusterrolebindings资源中定义。不太直观。

$ oc describe clusterrolebindings  system:openshift:scc:anyuid
Name:         system:openshift:scc:anyuid
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  system:openshift:scc:anyuid
Subjects:
  Kind            Name      Namespace
  ----            ----      ---------
  ServiceAccount  testsa    testp
$ 

在项目管理用户上的操作

从这里开始,不要使用 cluster-admin 而是使用项目的管理用户(即项目创建者)进行操作。

以项目创建者身份登录。

oc login https://api.f5cluster.m51o.p1.openshiftapps.com:6443  -u yuhkih -p xxxxxxx 

确认一下 Deployment 的名称。(使用 oc new-app 命令会自动创建以下的 Deployment,很方便。)

$ oc get deployment
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
nginx-port80   0/1     1            0           22m
$ 

将在部署中创建的SA与saport80密切关联。

oc set serviceaccount deployment nginx-port80 saport80

确认容器已启动。能够通过anyuid启动。

$ oc get pods
NAME                            READY   STATUS              RESTARTS        AGE
nginx-port80-54746447ff-n4k5t   0/1     ContainerCreating   0               9s
nginx-port80-78f75798bc-qjftq   0/1     CrashLoopBackOff    11 (113s ago)   32m
$ oc get pods
NAME                            READY   STATUS    RESTARTS   AGE
nginx-port80-54746447ff-n4k5t   1/1     Running   0          32s
$ 

将容器公开的端口进行更改是很重要的。即使在容器级别进行更改,也可以将端口设置为80/443,这样可以减少后续的麻烦。

限制-v2

SCC目前有14种类型。

 oc get scc
NAME                              PRIV    CAPS                      SELINUX     RUNASUSER          FSGROUP     SUPGROUP    PRIORITY     READONLYROOTFS   VOLUMES
anyuid                            false   <no value>                MustRunAs   RunAsAny           RunAsAny    RunAsAny    10           false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","persistentVolumeClaim","projected","secret"]
hostaccess                        false   <no value>                MustRunAs   MustRunAsRange     MustRunAs   RunAsAny    <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","hostPath","persistentVolumeClaim","projected","secret"]
hostmount-anyuid                  false   <no value>                MustRunAs   RunAsAny           RunAsAny    RunAsAny    <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","hostPath","nfs","persistentVolumeClaim","projected","secret"]
hostnetwork                       false   <no value>                MustRunAs   MustRunAsRange     MustRunAs   MustRunAs   <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","persistentVolumeClaim","projected","secret"]
hostnetwork-v2                    false   ["NET_BIND_SERVICE"]      MustRunAs   MustRunAsRange     MustRunAs   MustRunAs   <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","persistentVolumeClaim","projected","secret"]
machine-api-termination-handler   false   <no value>                MustRunAs   RunAsAny           MustRunAs   MustRunAs   <no value>   false            ["downwardAPI","hostPath"]
node-exporter                     true    <no value>                RunAsAny    RunAsAny           RunAsAny    RunAsAny    <no value>   false            ["*"]
nonroot                           false   <no value>                MustRunAs   MustRunAsNonRoot   RunAsAny    RunAsAny    <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","persistentVolumeClaim","projected","secret"]
nonroot-v2                        false   ["NET_BIND_SERVICE"]      MustRunAs   MustRunAsNonRoot   RunAsAny    RunAsAny    <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","persistentVolumeClaim","projected","secret"]
pcap-dedicated-admins             false   ["NET_ADMIN","NET_RAW"]   RunAsAny    RunAsAny           RunAsAny    RunAsAny    <no value>   false            ["awsElasticBlockStore","azureDisk","azureFile","cephFS","cinder","configMap","csi","downwardAPI","emptyDir","ephemeral","fc","flexVolume","flocker","gcePersistentDisk","gitRepo","glusterfs","iscsi","nfs","persistentVolumeClaim","photonPersistentDisk","portworxVolume","projected","quobyte","rbd","scaleIO","secret","storageOS","vsphere"]
privileged                        true    ["*"]                     RunAsAny    RunAsAny           RunAsAny    RunAsAny    <no value>   false            ["*"]
restricted                        false   <no value>                MustRunAs   MustRunAsRange     MustRunAs   RunAsAny    <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","persistentVolumeClaim","projected","secret"]
restricted-v2                     false   ["NET_BIND_SERVICE"]      MustRunAs   MustRunAsRange     MustRunAs   RunAsAny    <no value>   false            ["configMap","csi","downwardAPI","emptyDir","ephemeral","persistentVolumeClaim","projected","secret"]
splunkforwarder                   true    ["*"]                     RunAsAny    RunAsAny           RunAsAny    RunAsAny    <no value>   false            ["*"]
$ 

# 全部で 14種類ある
$ oc get scc | grep -v NAME | wc -l
14
$ 

在 restricted-v2 中,如果仔细观察,可以注意到 NET_BIND_SERVICE 被添加为一项权限(Capability)。

这个受限制的v2版本是OpenShift的默认版本,所以我以为无需做任何操作就可以启动一个监听端口80的Pod。但是由于下面的Kubernetes问题,似乎无法直接运行。

 

中身一看就知道经历了很长时间,所以感觉不会很容易解决…

如果想要允许 NET_BIND_SERVICE 操作,可以使用 spec.containers.securityContext.capabilities.add 等在 Deployment 中进行定义,以实现此操作。

<省略>
        app: test
        deployment: nginx-port80
    spec:
      containers:
      - image: docker.io/yuhkih/nginx-port80
        imagePullPolicy: IfNotPresent
        name: nginx-port80
        ports:
        - containerPort: 80
          protocol: TCP
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        capabilities:
          add: ["NET_BIND_SERVICE"]
<省略>