k8s-nodeshell实现

对于node我们一般会禁止ssh登录,但有时又不得不登录到node节点查看和debug,这时就可以通过node-shell的方式获得对应node的root shell。

安装:
https://github.com/kvaps/kubectl-node-shell/tree/master

使用curl进行安装。

curl -LO https://github.com/kvaps/kubectl-node-shell/raw/master/kubectl-node_shell
chmod +x ./kubectl-node_shell
sudo mv ./kubectl-node_shell /usr/local/bin/kubectl-node_shell

使用


# Get standard bash shell
kubectl node-shell <node>

# Use X-mode (mount /host, and do not enter host namespace)
kubectl node-shell -x <node>

# Execute custom command
kubectl node-shell <node> -- echo 123

# Use stdin
cat /etc/passwd | kubectl node-shell <node> -- sh -c 'cat > /tmp/passwd'

# Run oneliner script
kubectl node-shell <node> -- sh -c 'cat /tmp/passwd; rm -f /tmp/passwd'

原理:
其实就是一个bash脚本,所做的就是起一个特权容器,然后使用nsenter进入到宿主机,从而获取到root shell。
nsenter就是namespace enter的意思,它可以进入到目标程序所在的namespace中,因此可以用来调试容器程序。我们都知道目前存在的几个namespace,比如网络,用户,pid等,nsenter都有对应的参数可以指定,从而进入该namespace。
对于容器中有bash程序的场景,我们通过exec的方式就能进入容器内部,此时并不需要nsenter,但是对于那些没有bash程序的,就没法通过exec进入容器了,我们首先通过docker inspect获得对应容器的pid,然后通过nsenter进入utc、net、和pid namespace,这样我们就能使用宿主机的调试工具,比如tcpdump,ip等命令。

扩展
创建nodeshell的daemonset:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nsenter-daemonset
  namespace: default
  labels:
    app: nsenter
spec:
  selector:
    matchLabels:
      app: nsenter
  template:
    metadata:
      labels:
        app: nsenter
    spec:
      hostPID: true
      hostNetwork: true
      tolerations:
        - key: "CriticalAddonsOnly"
          operator: "Exists"
        - effect: "NoExecute"
          operator: "Exists"
      containers:
        - name: nsenter
          image: docker.io/library/alpine
          securityContext:
            privileged: true
          stdin: true
          stdinOnce: true
          tty: true
          command: [ "nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid", "--", "bash", "-l" ]
          resources:
            limits:
              cpu: "100m"
              memory: "256Mi"
            requests:
              cpu: "100m"
              memory: "256Mi"
      nodeSelector:
        kubernetes.io/hostname: node1
        

创建nodeshell的pod:

kubectl  --namespace= xxx  run --image docker.io/library/alpine --restart=Never '--overrides={
  "spec": {
    "nodeName": "node1",  #更改nodename
    "hostPID": true,
    "hostNetwork": true,
    "containers": [
      {
        "securityContext": {
          "privileged": true
        },
        "image": "docker.io/library/alpine",
        "name": "nsenter",
        "stdin": true,
        "stdinOnce": true,
        "tty": true,
        "command": [ "nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid", "--", "bash", "-l" ],
        "resources": {
          "limits": {
            "cpu": "100m",
            "memory": "256Mi"
          },
          "requests": {
            "cpu": "100m",
            "memory": "256Mi"
          }
        }
      }
    ],
    "tolerations": [
      {
        "key": "CriticalAddonsOnly",
        "operator": "Exists"
      },
      {
        "effect": "NoExecute",
        "operator": "Exists"
      }
    ]
  }
}' --labels= -t -i nsenter-h3m5fy