Tailscale

tailscale logo

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

  1. An account on Tailscale.com.

  2. Understanding Tailscale and being familiar with its terminology and concepts, in particular, that of a "tailnet."

  3. 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

  1. Run tailscale up --accept-routes on your client

  2. 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:

  1. Enable Tailscale’s "MagicDNS" in the admin console

  2. 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

  1. 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
  2. Give the Tailscale deployment permissions to list services in the target namespace. Create the following Role in the target namespace

    apiVersion: 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 namespace

    apiVersion: 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
  3. Edit the Tailscale deployment to configure service-observer sidecar to watch the additional namespace. Change the existing environment variable TARGET_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 the NetworkPolicy, Role and RoleBinding.

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
tailscale tcp socks.drawio
Figure 1. Relationship of Example config

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.