利用容器逃逸實現遠端登入k8s叢集節點

李國寶發表於2021-01-21

某天,

某魚說要吃瞄,

於是......

李國寶:邊緣計算k8s叢集SuperEdge初體驗

zhuanlan.zhihu.com
圖示

照著上一篇文章來說,我這邊邊緣計算叢集有一堆節點。

每個節點都在不同的網路環境下。

他們的共同點都是可以訪問內網,

部分是某雲學生主機,

部分是跑在家庭網路環境下的虛擬機器,

甚至假設中還有一些是樹莓派之類的機器。

所以他們另一個共同點是,基本都沒有公網IP。

這樣一來,我要實現遠端登入到某些節點搞事的時候,

只有內網穿透這一條路子了。

使用frp進行內網穿透 - 少數派

sspai.com
圖示
https://github.com/fatedier/frp

github.com

內網穿透倒沒什麼,在公司使用這貨長期跑了兩年垮大洋穿透也很穩定。

只是...

只是...

只是...

要一臺臺機器配置一次,要維護一個穩定的公網伺服器作為橋接。

就是...麻煩了點。

然後想了下。

當前的kube superedge邊緣計算叢集本身就實現了4層和7層的內網穿透,

理論上直接使用它的能力也可以做到遠端登入的。

於是開始研究了一下怎麼實現在只有kubectl環境的機器上,

直接登入k8s容器叢集的node節點。

搜了一波之後首先發現的是這個專案。

A kubectl plugin to SSH into Kubernetes nodes using a SSH jump host Pod

github.com
看描述和需求來說,完全符合我的要求。

$ kubectl krew install ssh-jump
照著教程配置好外掛,裝好環境之後實踐了一下。

....

一切都好,就是連不上去。

蛋疼了...

接著又找了一波,發現了一個Redhat老哥的部落格。

A consistent, provider-agnostic way to SSH into any Kubernetes node

完美。

我想要的就是這個。

看了下外掛程式碼。luksa/kubectl-plugins看了下外掛程式碼。

https://github.com/luksa/kubectl-plugins/blob/master/kubectl-ssh

github.com

#!/usr/bin/env bash

set -e

ssh_node() {
  node=$1
  if [ "$node" = "" ]; then
    node=$(kubectl get node -o name | sed 's/node\///' | tr '\n' ' ')
    node=${node::-1}

    if [[ "$node" =~ " " ]]; then
      echo "Node name must be specified. Choose one of: [$node]"
      exit 1
    else
      echo "Single-node cluster detected. Defaulting to node $node"
    fi
  fi

  pod=$(
    kubectl create -o name -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  generateName: ssh-node-
  labels:
    plugin: ssh-node
spec:
  nodeName: $node
  containers:
  - name: ssh-node
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ["chroot", "/host"]
    tty: true
    stdin: true
    stdinOnce: true
    securityContext:
      privileged: true
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
  hostNetwork: true
  hostIPC: true
  hostPID: true
  restartPolicy: Never
EOF
  )

  deletePod() {
    kubectl delete $pod --wait=false
  }
  trap deletePod EXIT

  echo "Created $pod"
  echo "Waiting for container to start..."
  kubectl wait --for=condition=Ready $pod >/dev/null
  kubectl attach -it $pod -c ssh-node

}

ssh_pod() {
  # TODO: improve this
  if [ "$1" == "" ]; then
    echo "Pod name must be specified."
    exit 1
  fi
  kubectl exec -it "$@" bash || (
    echo "Running bash in pod failed; trying with sh"
    kubectl exec -it "$@" sh
  )
}

print_usage() {
  echo "Provider-agnostic way of opening a remote shell to a Kubernetes node."
  echo
  echo "Enables you to access a node even when it doesn't run an SSH server or"
  echo "when you don't have the required credentials. Also, the way you log in"
  echo "is always the same, regardless of what provides the Kubernetes cluster"
  echo "(e.g. Minikube, Kind, Docker Desktop, GKE, AKS, EKS, ...)"
  echo
  echo "You must have cluster-admin rights to use this plugin."
  echo
  echo "The primary focus of this plugin is to provide access to nodes, but it"
  echo "also provides a quick way of running a shell inside a pod."
  echo
  echo "Examples: "
  echo "  # Open a shell to node of a single-node cluster (e.g. Docker Desktop)"
  echo "  kubectl ssh node"
  echo
  echo "  # Open a shell to node of a multi-node cluster (e.g. GKE)"
  echo "  kubectl ssh node my-worker-node-1"
  echo
  echo "  # Open a shell to a pod"
  echo "  kubectl ssh pod my-pod"
  echo
  echo "Usage:"
  echo "  kubectl ssh node [nodeName]"
  echo "  kubectl ssh pod [podName] [-n namespace] [-c container]"
  exit 0
}

if [ "$1" == "--help" ]; then
  print_usage
fi

if [[ "$1" == node/* ]]; then
  ssh_node ${1:5}
elif [ "$1" == "node" ]; then
  ssh_node $2
elif [[ "$1" == pod/* ]]; then
  ssh_pod "$@"
elif [ "$1" == "pod" ]; then
  shift
  ssh_pod "$@"
else
  print_usage
fi


認真看了一下這個指令碼。

直呼人才啊。

果然是玩Linux的老哥啊。

牛逼啊。

太牛逼了。

太有趣了。

額。

講人話。

這個指令碼使用busybox映象啟動了容器例項,

通過chroot到 /host + 把宿主機所有檔案掛在到容器例項的方式,

實現了在容器例項直接對宿主機系統一對一“Copy”(可能表達不太準確),

進而實現直接在這個容器例項中操作宿主機的所有資源。

是的,所有資源。

是的,所有資源。

是的,所有資源。

著這裡直接能看到其他程式的程式,

免密碼直接操作其他使用者的資料。

所謂,

這就是容器逃逸。

然後....

我們的目的確實也達到了。

通過這種方式確實可以直接實現登入任意一臺k8s node節點,

再也不需要密碼和授權。

總結。

很好玩。

不明映象確實有風險。

這個世界一直都不太安全。

參考資料:

docker 容器逃逸漏洞(CVE-2020-15257)風險通告

容器逃逸技術概覽 - DockOne.io

rambo1412:容器逃逸技術概覽

相關文章