Tailscale
Tailscale is a secure network that works.
Zero config VPN. Installs on any device in minutes, manages firewall rules for you, and works from anywhere.
Tailscale on APPUiO Cloud is a perfect match. It runs in Userspace networking mode, which is the only way to run it on APPUiO Cloud.
Use-Cases on APPUiO Cloud
- Easy Service Access
-
With Tailscale, it’s straightforward to expose services from a Kubernetes namespace to your tailnet, which is especially handy if you need direct service access from a developer workstation without the hassle of
[kubectl|oc] port-forward
. - Service Access from Pods
-
Accessing services not running on (the same) APPUiO Cloud zone from a Pod isn’t easy. With Tailscale it’s possible to access any service available in the tailnet, which allows accessing firewalled external services securely.
Prerequisites
-
An account on Tailscale.com.
-
Understanding Tailscale and being familiar with its terminology and concepts, in particular, that of a "tailnet."
-
An Auth key
Installation
To deploy Tailscale into your namespace, choose our OpenShift template, available in the OpenShift developer catalog.
The only parameter you need to provide is your Tailscale auth key. We recommend using a standard key, as this is a long-running Tailscale connection. No need to use a temporary node key.
The developer catalog is available in the OpenShift web console in the "Developer" view when clicking on "+Add."
Access Services on an APPUiO Cloud zone (Ingress)
Supposing you’ve installed Tailscale in your Namespace as documented in the previous section, the deployment will automatically discover all services in your namespace and make them available in your tailnet. To access the services in your Namespace by their Cluster IP, you need to
-
Run
tailscale up --accept-routes
on your client -
Accept the advertised routes in the Tailscale admin console
To avoid having to accept the advertised routes every time you create a new service, you can add the following in your Tailscale policy
{ "autoApprovers": { "routes": { "172.30.0.0/16": ["you@example.com"], (1) }, }, }
1 Replace you@example.com
with your Tailscale account ID
If you want to use the cluster-internal DNS names to access services through the tailnet instead of the service cluster IPs, you need to:
-
Enable Tailscale’s "MagicDNS" in the admin console
-
Add the cluster DNS as an additional nameserver. To do so, click "Add nameserver," select "Custom," and enter the following details:
Nameserver 172.30.0.10
Restrict to search domain On
Search Domain svc.cluster.local
Multiple namespaces
Out of the box, the OpenShift template will only discover services in its namespace. Additionally, due to the restrictive default network policies on APPUiO Cloud, the Tailscale deployment won’t be able to access services in another namespace.
The easiest way to make services from multiple namespaces available in your tailnet is to deploy a Tailscale instance in each namespace in which you want to access services through the tailnet.
If you want to share a single Tailscale instance between namespaces, you need to
-
Configure a
NetworkPolicy
in the target namespace, which allows ingress traffic from the namespace in which Tailscale runs:apiVersion: networking.k8s.io/v1 kind: NetworkPolicy spec: ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: <tailscale-namespace> (1) - podSelector: matchLabels: app: tailscale-namespace-router (2) podSelector: {} policyTypes: - Ingress
1 Replace with the namespace in which the Tailscale deployment runs 2 Only allow the Tailscale deployment to access the target namespace -
Give the Tailscale deployment permissions to list services in the target namespace. Create the following
Role
in the target namespaceapiVersion: rbac.authorization.k8s.io/v1 kind: Role rules: - apiGroups: [""] resources: ["services"] verbs: ["get", "list", "watch"]
Grant the Tailscale ServiceAccount that role. Create the following
RoleBinding
in the target namespaceapiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding roleRef: kind: Role name: tailscale apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: tailscale namespace: <tailscale-namespace> (1)
1 Replace with the namespace in which the Tailscale deployment runs -
Edit the Tailscale deployment to configure
service-observer
sidecar to watch the additional namespace. Change the existing environment variableTARGET_NAMESPACE
to- name: TARGET_NAMESPACE value: <tailscale-namespace>,<target-namespace> (1)
1 Replace <tailscale-namespace>
with the namespace of the Tailscale deployment and<target-namespace>
with the target namespace in which you’ve created theNetworkPolicy
,Role
andRoleBinding
.
Our Tailscale Service Observer, running as a sidecar in the Tailscale Pod, performs the service discovery and dynamic route configuration.
Access services in the tailnet from Pods (Egress)
There are two ways to access services in the tailnet from a Pod running on APPUiO Cloud:
-
Tailscale-native proxy mode, either through a SOCKS5 or HTTP proxy, depending on what the application supports
-
A TCP-over-SOCKS5 middleman to emulate a plain TCP connection, especially useful for database connections
Tailscale-native proxy mode
For those applications which support SOCKS5 or HTTP proxies, it’s as easy as deploying Tailscale as documented in the installation section and setting the corresponding proxy environment variables.
When using our template to deploy Tailscale, a service named tailscale
gets created in the namespace where Tailscale runs.
The environment variables in your applications' deployment will need to look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
env:
- name: ALL_PROXY
value: socks5://tailscale:1055/
- name: HTTP_PROXY
value: http://tailscale:1055/
- name: http_proxy
value: http://tailscale:1055/
TCP-over-SOCKS5 middleman
We provide a simple TCP-over-SOCKS5 middleman, which allows tunneling TCP connections over a Tailscale SOCKS5 proxy.
Below is an example of deploying the tcp-over-socks
tool and making the TCP connection available as a Kubernetes service.
Remember to adapt the port and target address of the tailnet IP of the service you want to access.
Also, if you don’t deploy Tailscale with our template, ensure that the SOCKS5 address and port match your Tailscale deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-tcp-over-socks
spec:
minReadySeconds: 15
replicas: 1
selector:
matchLabels:
app: mysql-tcp-over-socks
strategy:
type: Recreate
template:
metadata:
labels:
app: tcp-socks
spec:
containers:
- name: tcp-socks
imagePullPolicy: Always
image: ghcr.io/appuio/tcp-over-socks:v1.0.0
ports:
- containerPort: 3306
name: mysql
protocol: TCP
args:
- /usr/local/bin/app
- --port
- "3306"
- --socks5
- tailscale:1055
- --target
- 100.66.5.47:3306
---
apiVersion: v1
kind: Service
metadata:
name: db
spec:
ports:
- name: db
port: 3306
protocol: TCP
targetPort: 3306
selector:
app: tcp-socks
type: ClusterIP
Now access the TCP service from your application via the Kubernetes service named db
.
You can find other examples on GitHub in the examples/
directory of the tcp-over-socks
utility.