🖇️ k8s-linkup – Linkup Kubernetes pods to a remote site
k8s-linkup is a utility for linking up Kubernetes pods to remote sites by means of a Virtual Private Network connection (VPN).
It is intended to be used through its Helm chart, mostly as a subchart of an application. For example, if an external database is intended to be used for some applicaiton, k8s-linkup can be used to make it available to the pods running in Kubernetes.
This utility is developed alongside the AlekSIS® project, to be able to link up SaaS-hosted instances to databases hosted elsewhere.
💡 General idea
The general idea of how the container works is:
- Choose a backend connection type (e.g. OpenVPN)
- Create a configuration file for the connection (e.g.
openvpn.conf
)- Deploy this configuration file in a ConfigMap, or mount as volume manually
- Deploy all secrets needed in a Secret, or mount as volume manually
- Configure the backend connection in Helm values, or manually through the environment
- Configure a set of port mappings in Helm values, or mount a HAProxy configuration manually
- The container will start the backend connection, plus HAProxy to make configured services available to the outside
⚙️ How to deploy (using Helm)
Making the chart available
To make the chart available, add the chart repository and update the Helm cache:
helm repo add k8s-linkup https://edugit.org/api/v4/projects/882/packages/helm/stable
helm update
If using k8s-linkup as a subchart, add the following to Chart.yaml
:
- name: k8s-linkup
version: "^2.0.0"
repository: https://edugit.org/api/v4/projects/882/packages/helm/stable
condition: k8s-linkup.enabled
Configuring linkups
Linkups, that is, one or several containers connecting to a backend, can be
configured by filling the linkups
array in your Helm values. When
deploying as a stand-alone chart, the array is on the top level; when using
k8s-linkup as a subchart, it is below the k8s-linkup
namespace.
An example linkup might look like this:
linkups:
- name: linkup1
connection: openvpn
serviceType: ClusterIP
replicaCount: 1
configMap:
name: openvpn-config
file: openvpn.conf
secret:
name: openvpn-secret
healthCheckPort: 9000
portMappings: []
This example will spawn a container connecting to a remote OpenVPN server,
using openvpn.conf
from the ConfigMap names openvpn-config
, and
secrets from openvpn-secret
.
The Secret will be mounted under /etc/k8s-linkup/openvpn/secret/
, while
the ConfigMap will be mounted at /etc/k8s-linkup/openvpn
. That means
that all files in the Secret can be referenced relative to the
configuration file.
Multiple linkups can be configured. Each will spawn a separate pod, and make a separate Service available.
Mapping remote ports
To map ports to services running on the remote side, the portMappings
array of each linkup is filled:
portMappings:
- name: ldap
local:
port: 389
remote:
host: 172.17.0.5
port: 389
maxconn: 20
type: TCP
monitored: true
This example will add a matching frontend and backend in HAProxy mapping
the container port 389
to the same port on a remote host 172.17.0.5
. It
will allow 20 connections at most.
The monitored
flag defines whether this service should be monitored,
meaning that it will be added as a rule to HAProxy's health check. The
health check is queried in the pod's liveness probe, so the container will
be restarted if a port mapping which is monitored becomes and remains
unavailable. This behaviour is the default.
Using proxied services
Proxied services are made available as ports ona Kubernetes Service, named
with the Helm release name and linkup name
. For example, for a Helm
release named foobar
, and a linkup named linkup1
, the Service will be
called foobar-linkup1
.
Mind that, when using the chart as a subchart, the subchart name is appended
to the release name, so the full name becomes foobar-k8s-linkup-linkup1
.
You can use this service name as a hostname in your other pods.
🤗 Supported backend connections
The following connection types are supported (see below sections for hints on k8s-linkup specific configuration):
OpenVPN
OpenVPN is an enterprise-grade and well-known VPN solution, based around a client-server architecture.
In k8s-linkup, the configuration is expected in a single file in a
ConfigMap. The configuration file should reference all necessary
certificates and keys, with private keys and shared keys residing in the
Secret (they can be referenced with a relative path, like
secret/client.key
).
WireGuard
WireGuard is a, relatively new, offering much simpler configuration than OpenVPN and a mesh-like architecture. It is included in the Linux kernel.
WireGuard expects the configuration file name to match the interface name.
In k8s-linkup, the interface name is thus derived from the config file
name used in the ConfigMap. The private key needs to reside in a file
named <interface>.key
in the Secret, i.e. with the same base name as the
config file. It is recommended to use wg0.conf
and wg0.key
.
The relevant excerpt for your values, with the Secret containing a file
named wg0.key
, might look like:
linkups:
- name: linkup1
connection: wireguard
configMap:
name: wg-config
file: wg0.conf
secret:
name: wg-secret
😿 Limitations
k8s-linkup has a few (partially deliberate) limitations:
- Each container can only handle one backend connection. This is deliberate – to connect to multiple VPNs, spawn multiple linkup containers.
- Only one Service is created per linkup pod, which exposes all mapped ports. This will probably be improved in a future version.
- No full container networking – this is deliberate; the concept of this utility is to expose selected services only by mapping them into the cluster. Alternatives with full container networking exist.
⚖️ Copyright and licence
Copyright 2021 Dominik George <dominik.geoerge@teckids.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.