This directory contains an example deployment of the Tunnel Server on Kubernetes.
Note that this is an advanced task which requires some networking and Kubernetes know-how.
Deploying a private instance of the Tunnel Server allows for fine-grained control of:
- The URLs created for preview environments: e.g, use a custom domain.
- Geolocation of the server: Deploying the server on a network close to environments' can result better network performance.
- Security and privacy: deploy everything in your VPC, no traffic to 3rd parties.
- A Kubernetes cluster
- A TLS certificate for your domain
kubectl,kustomizeandssh-keygen(usually installed with the OpenSSH client).
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 and ALPN in the TLS handshake.
The connection is directed according to the following:
- If the ALPN protocol is
ssh, the connection is directed to the SSH server. - If the SNI servername is one of the
SSH_HOSTNAMES(default: the hostname of theBASE_URL, e.g,yourdomain.example), the connection is directed to the SSH server. - Otherwise, the connection is directed to the HTTP server.
| Parameter | default | overridable using env var |
|---|---|---|
| SSH port | 2222 | SSH_PORT |
| HTTP port | 3000 | PORT |
| TLS port | 8443 | TLS_PORT |
| SNI servername for SSH connections | hostname of BASE_URL |
SSH_HOSTNAMES (comma separated) |
In this example deployment, the TLS port is exposed using a LoadBalancer-type K8S Service. Using the default configuration, we will specify the base hostname yourdomain.example as the -t flag in the Preevy CLI for tunneling SSH connections, and the resulting env URLs will have the hostname from the subdomain *.yourdomain.example (e.g, my-service-my-env-abc123456.yourdomain.example) which will be used for client requests.
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
Copy config.env.example to config.env and set your domain in the BASE_URL variable.
The cookie secret is a simple text-based secret (like a password) in a file.
LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 40 > cookie_secretssh-keygen -t ed25519 -N "" -f ssh_host_keyReview the generated configuration:
kustomize build .To deploy to the K8S cluster:
kustomize build . | kubectl apply -f -Make sure the two deployments tunnel-server and tunnel-server-stunnel exist and that their pods are running.
This requires OpenSSH and a recent-enough OpenSSL CLI with support for the -quiet option.
To test the SSH endpoint (replace $MY_DOMAIN with your domain):
MY_DOMAIN=yourdomain.example
# 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 helloCreate two DNS records: *.yourdomain.example and yourdomain.example, both pointing to the external IP of the tunnel-server service.
The address is not guaranteed to be static. According to your Kubernetes provider, there could be multiple ways to define a DNS entry for it. Here are some guides:
The up and urls commands accept a -t flag which can be used to set the Tunnel Server SSH URL. Specify ssh+tls://yourdomain.example to use your instance.
To delete the deployed Tunnel Server and associated Kubernetes objects, run:
kustomize build . | kubectl delete -f -