How to get SSH running on Kubernetes?

5/24/2021

I'm trying to get sshd running in my Kubernetes cluster so that I can set up a reverse proxy/ngrok tunnel per this blog post.

I've got nginx running, but I can't seem to connect to SSH.

Here's the complete Kubernetes config:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: dev-example-ingress
  namespace: dev-example
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts:
        - dev.example.ca
      secretName: dev-example-tls
  rules:
    - host: dev.example.ca
      http:
        paths:
          - backend:
              serviceName: app-service
              servicePort: 80
          - backend:
              serviceName: app-service
              servicePort: 2222
---
apiVersion: v1
kind: Service
metadata:
  name: app-service
  namespace: dev-example
spec:
  selector:
    pod: my-pod-label
  ports:
    - name: http
      protocol: TCP
      port: 80
    - name: ssh-tcp
      protocol: TCP
      port: 2222
#    - name: ssh-udp
#      protocol: UDP
#      port: 2222
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
  namespace: dev-example
spec:
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      pod: my-pod-label
  template:
    metadata:
      labels:
        pod: my-pod-label
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
        - name: nginx
          image: dreg.example.ca/example/dev.example.ca
          ports:
            - name: http
              containerPort: 80
        - name: sshd
          image: linuxserver/openssh-server
          ports:
            - name: ssh
              containerPort: 2222
          env:
            - name: PUID
              value: '1000'
            - name: PGID
              value: '1000'
            - name: TZ
              value: America/Los_Angeles
            - name: USER_NAME
              value: example
            - name: USER_PASSWORD
              value: test

But when I try to connect it says "connection refused":

❯ ssh -p 2222 example@dev.example.ca
ssh: connect to host dev.example.ca port 2222: Connection refused

I don't know if that means I didn't expose the port properly or what.

I'm pretty sure linuxserver/openssh-server is running on port 2222. If I run ps -aux in that container I get:

^@root@app-deployment-5d9567dcc5-f5hq6:/# ps -aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0    200     4 ?        Ss   19:26   0:00 s6-svscan -t0 /var/run/s6/services
root        34  0.0  0.0    200     4 ?        S    19:26   0:00 s6-supervise s6-fdholderd
root       277  0.0  0.0    200     4 ?        S    19:26   0:00 s6-supervise openssh-server/log
root       278  0.0  0.0    200     4 ?        S    19:26   0:00 s6-supervise openssh-server
mpen       280  0.0  0.0    256     4 ?        Ss   19:26   0:00 s6-log n30 s10000000 S30000000 T !gzip -nq9 /config/logs/openssh
mpen       281  0.0  0.0   4384  3480 ?        Ss   19:26   0:00 sshd: /usr/sbin/sshd -D -e -p 2222 [listener] 0 of 10-100 startups
root       298  0.5  0.0   2580  2304 pts/0    Ss   19:32   0:00 bash
root       307  0.0  0.0   1640   868 pts/0    R+   19:32   0:00 ps -aux

What am I missing?

I'm open to using other ssh docker images if they work better/are easier. This is just for dev.

-- mpen
kubernetes
openssh
ssh
sshd

2 Answers

5/24/2021

The ingress system only works for HTTP (some Ingress Controllers support basic TCP routing as a custom extension but you're just using the basics). SSH is not an HTTP-based protocol :) What you want instead is a Service with type LoadBalancer.

-- coderanger
Source: StackOverflow

5/24/2021

The Kubernetes Ingress resource is not meant to support TCP or UDP services. Nonetheless, some Ingress implementations provide mechanisms to support TCP or UDP on different ports. If you are using NGINX Ingress Controller, you can expose TCP or UDP ports by modifying ConfigMaps.

To configure NGINX Ingress Controller to use these ConfigMaps you need to configure them while deploying the Controller using the arguments --tcp-services-configmap and --udp-services-configmap. Each ConfigMap should already be available before deploying the Controller.

You can then define your custom ports by defining a map of ports to services.

For example, if you were defining the NGINX Ingress Controller through manifest files, you should configure the DaemonSet for the Controller like this:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  # metadata configuration...
spec:
  # spec configuration...
  containers:
    - image: k8s.gcr.io/ingress-nginx/controller:$TAG
      name: nginx-ingress-microk8s
      # container configuration...
      args:
        - /nginx-ingress-controller
        - --tcp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-tcp-services
        - --udp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-udp-services
        # other arguments...

Here is an example of a ConfigMap taken directly from the docs:

apiVersion: v1
kind: ConfigMap
metadata:
  name: tcp-services
  namespace: ingress-nginx
data:
  9000: "default/example-go:8080"

You can define any number of ports that can be exposed using this mechanic. However, I've never used this configuration with SSH to expose Syslog ports.

-- guzmonne
Source: StackOverflow