From 77de0723a2b5a72d5975132590901fcc3d600b88 Mon Sep 17 00:00:00 2001 From: Roy Razon Date: Sun, 11 Feb 2024 15:42:45 +0200 Subject: [PATCH] tunnel server: update the deployment guide --- tunnel-server/deployment/k8s/README.md | 27 ++++--- .../deployment/k8s/kustomization.yaml | 5 +- tunnel-server/deployment/k8s/sslh.conf | 16 ---- .../deployment/k8s/tunnel-server-stunnel.yaml | 77 ------------------- .../deployment/k8s/tunnel-server.yaml | 37 ++++----- 5 files changed, 35 insertions(+), 127 deletions(-) delete mode 100644 tunnel-server/deployment/k8s/sslh.conf delete mode 100644 tunnel-server/deployment/k8s/tunnel-server-stunnel.yaml diff --git a/tunnel-server/deployment/k8s/README.md b/tunnel-server/deployment/k8s/README.md index 6f678e4c..0c07f0fe 100644 --- a/tunnel-server/deployment/k8s/README.md +++ b/tunnel-server/deployment/k8s/README.md @@ -15,25 +15,25 @@ Deploying a private instance of the Tunnel Server allows for fine-grained contro ## Requirements - A Kubernetes cluster -- An ingress solution to make K8S Services accesible from your network (e.g, Traefik). In this example, we'll use your cloud provider's load balancer. - A TLS certificate for your domain - `kubectl` and `kustomize` ## Overview -The Tunnel Server natively listens on two ports: +The Tunnel Server natively listens on three ports: - A SSH port which accepts tunneling SSH connections from environments - A HTTP port which accepts requests from clients (browsers, etc) +- A TLS port which directs incoming connections to either the SSH server or the HTTP server according to the [SNI server name](https://en.wikipedia.org/wiki/Server_Name_Indication). -In this deployment scheme, both ports are wrapped with TLS using [`stunnel`](https://www.stunnel.org/). Both HTTP and SSH connections are accepted on a [single port](https://vadosware.io/post/stuffing-both-ssh-and-https-on-port-443-with-stunnel-ssh-and-traefik/) and routed using [`sslh`](https://github.com/yrutschle/sslh/) to the tunnel server ports. +The `SSH_HOSTNAMES` env var determines which hostnames are directed to the SSH server and the rest are directed to the HTTP server. If the env var is not specified, the hostname of the `BASE_URL` is used. -The `stunnel` port is then exposed using a [`LoadBalancer-type K8S Service`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer). +In this deployment scheme, the TLS port is exposed using a [`LoadBalancer-type K8S Service`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer). The base hostname `yourdomain.example` is used for tunneling SSH connections and any hostname on the subdomain `*.yourdomain.example` will be used for client requests. ## Instructions ### 1. Setup the domain and the TLS certificate -Make sure the certificate is for a wildcard subdomain, e.g, `*.yourdomain.example` +Make sure the certificate is for both the base domain and the wildcard subdomain, e.g, `yourdomain.example` and `*.yourdomain.example`. Put the cert and key (in PEM format) in the files `tls.crt` and `tls.key` @@ -78,12 +78,13 @@ To test the SSH endpoint (replace `$MY_DOMAIN` with your domain): ```bash MY_DOMAIN=yourdomain.example -EXTERNAL_IP=$(kubectl get service tunnel-server-tls -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +# NOTE: for EKS, replace .ip with .hostname below +EXTERNAL_IP=$(kubectl get service tunnel-server -o jsonpath='{.status.loadBalancer.ingress[0].ip}') ssh -nT -o "ProxyCommand openssl s_client -quiet -verify_quiet -servername $MY_DOMAIN -connect %h:%p" -p 443 foo@$EXTERNAL_IP hello ``` -### 6. Create DNS records for the `tunnel-server` Service external IP +### 6. Create DNS records for the `tunnel-server` Service external IP or hostname Create two DNS records: `*.yourdomain.example` and `yourdomain.example`, both pointing to the external IP of the `tunnel-server` service. @@ -93,8 +94,14 @@ The address is not guaranteed to be static. According to your Kubernetes provide - Google Cloud: [GKE](https://cloud.google.com/kubernetes-engine/docs/concepts/service-load-balancer) - Azure: [AKS](https://learn.microsoft.com/en-us/azure/aks/load-balancer-standard) -Another approach would be to use a 3rd-party ingress solution like [Traefik](https://doc.traefik.io/traefik/user-guides/crd-acme/). - -## Using your Tunnel Server instance with the Preevy CLI +### 7. Use your Tunnel Server instance with the Preevy CLI The `up` and `urls` commands accept a `-t` flag which can be used to set the Tunnel Server URL. Specify `ssh+tls://yourdomain.example` to use your instance. + +### 8. Cleanup: Delete the deployed objects + +To delete the deployed Tunnel Server and associated Kubernetes objects, run: + +```bash +kustomize build . | kubectl delete -f - +``` diff --git a/tunnel-server/deployment/k8s/kustomization.yaml b/tunnel-server/deployment/k8s/kustomization.yaml index bb234c5d..01738d8c 100644 --- a/tunnel-server/deployment/k8s/kustomization.yaml +++ b/tunnel-server/deployment/k8s/kustomization.yaml @@ -1,7 +1,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- tunnel-server-stunnel.yaml - tunnel-server.yaml secretGenerator: - files: @@ -19,8 +18,6 @@ secretGenerator: configMapGenerator: - name: tunnel-server-config envs: [config.env] -- name: tunnel-server-sslh-config - files: [sslh.conf] images: - name: ghcr.io/livecycle/preevy/tunnel-server - newTag: main-5f80bc0 + newTag: main-8a9d527 diff --git a/tunnel-server/deployment/k8s/sslh.conf b/tunnel-server/deployment/k8s/sslh.conf deleted file mode 100644 index e1d3079f..00000000 --- a/tunnel-server/deployment/k8s/sslh.conf +++ /dev/null @@ -1,16 +0,0 @@ -foreground: true; -verbose-config: 1; # print configuration at startup -verbose-config-error: 1; # print configuration errors -verbose-connections-error: 1; # connection errors -verbose-probe-error: 1; # failures and problems during probing -verbose-system-error: 1; # system call problem, i.e. malloc, fork, failing -verbose-int-error: 1; # internal errors, the kind that should never happen -listen: -( - { host: "0.0.0.0"; port: "2443"; } -); -protocols: -( - { name: "ssh"; service: "ssh"; host: "0.0.0.0"; port: "2222"; }, - { name: "http"; host: "0.0.0.0"; port: "3000"; }, -); \ No newline at end of file diff --git a/tunnel-server/deployment/k8s/tunnel-server-stunnel.yaml b/tunnel-server/deployment/k8s/tunnel-server-stunnel.yaml deleted file mode 100644 index 90146746..00000000 --- a/tunnel-server/deployment/k8s/tunnel-server-stunnel.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: tunnel-server-stunnel - namespace: default -spec: - progressDeadlineSeconds: 600 - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - app: tunnel-server-stunnel - template: - metadata: - labels: - app: tunnel-server-stunnel - spec: - containers: - - env: - - name: STUNNEL_ACCEPT - value: 0.0.0.0:443 - - name: STUNNEL_SERVICE - value: tunnel-server - - name: STUNNEL_CONNECT - value: tunnel-server:443 - - name: STUNNEL_KEY - value: /etc/livecycle-ssl/tls.key - - name: STUNNEL_CRT - value: /etc/livecycle-ssl/tls.crt - - name: STUNNEL_DEBUG - value: err - image: dweomer/stunnel - imagePullPolicy: IfNotPresent - name: stunnel - ports: - - containerPort: 443 - protocol: TCP - resources: - limits: - cpu: 500m - ephemeral-storage: 1Gi - memory: 2Gi - requests: - cpu: 500m - ephemeral-storage: 1Gi - memory: 2Gi - securityContext: - capabilities: - drop: - - NET_RAW - volumeMounts: - - mountPath: /etc/livecycle-ssl - name: tls-cert - readOnly: true - restartPolicy: Always - securityContext: - seccompProfile: - type: RuntimeDefault - terminationGracePeriodSeconds: 30 - volumes: - - name: tls-cert - secret: - defaultMode: 420 - secretName: tunnel-server-tls ---- -apiVersion: v1 -kind: Service -metadata: - name: tunnel-server-tls -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 443 - selector: - app: tunnel-server-stunnel - type: LoadBalancer diff --git a/tunnel-server/deployment/k8s/tunnel-server.yaml b/tunnel-server/deployment/k8s/tunnel-server.yaml index 02898393..adbe7874 100644 --- a/tunnel-server/deployment/k8s/tunnel-server.yaml +++ b/tunnel-server/deployment/k8s/tunnel-server.yaml @@ -17,10 +17,10 @@ spec: secret: defaultMode: 400 secretName: tunnel-server-ssh - - name: sslh-config - configMap: - defaultMode: 420 - name: tunnel-server-sslh-config + - name: tls-cert + secret: + defaultMode: 400 + secretName: tunnel-server-tls containers: - env: - name: COOKIE_SECRETS @@ -52,6 +52,9 @@ spec: - containerPort: 3000 name: http protocol: TCP + - containerPort: 8443 + name: tls + protocol: TCP resources: limits: cpu: "1" @@ -69,35 +72,29 @@ spec: - mountPath: /etc/livecycle-ssh name: ssh readOnly: true - - image: oorabona/sslh:v2.0-rc1 - imagePullPolicy: IfNotPresent - name: sslh - command: [sslh-ev, --config=/etc/sslh/sslh.conf] - volumeMounts: - - mountPath: /etc/sslh - name: sslh-config + - mountPath: /app/tls/cert.pem + name: tls-cert readOnly: true - ports: - - containerPort: 2443 - name: sslh - protocol: TCP - restartPolicy: Always - terminationGracePeriodSeconds: 30 + subPath: tls.crt + - mountPath: /app/tls/key.pem + name: tls-cert + readOnly: true + subPath: tls.key --- apiVersion: v1 kind: Service metadata: name: tunnel-server spec: + type: LoadBalancer ports: - - name: sslh + - name: tls port: 443 protocol: TCP - targetPort: 2443 + targetPort: 8443 - name: metrics port: 8888 protocol: TCP targetPort: 8888 selector: app: tunnel-server - type: ClusterIP