kilo/docs/vpn-only.md
2020-02-11 13:12:24 +01:00

125 lines
4.0 KiB
Markdown

# Kilo as VPN Server to connect into the cluster
Use Case: Connect into the Kubernetes Cluster via WireGuard VPN, so that a WireGuard client has direct
access to Pods and Services.
## Prerequisites
- Ensure Wireguard is installed on the host system
- UDP port 51820 must be externally reachable to the cluster
## Deployment
- For this case, it is enough to deploy a single instance of Kilo into the cluster. Kilo should be
**pinned** to a single Node, because the WireGuard private key is host-specific.
- CNI has to be disabled, because you are keeping the existing CNI plugin.
- You can use in-cluster Kubernetes configuration, and do not need to mount the host Kubernetes config.
(The latter is only needed to extract in the CNI case to detect the outside-visible Hostname for
each node).
- We only tested this with Flannel or Flannel+Calico (=Canal) so far.
You can still access all Kubernetes Services and Pods, no matter where they run, in this configuration.
The full configuration can be found at [manifests/kilo-vpn-only-example.yaml](../manifests/kilo-vpn-only-example.yaml).
**Make sure to adjust the `nodeSelector` in the `DaemonSet`**.
## Registering a new VPN client
1. Create a new WireGuard keypair on the VPN Client. Remember the public key.
2. For the client, create a Kubernetes `peer` resource:
- pick a new, unique, IP address for the client from the `10.5.0.*` IP range.
Based on this IP, we can lateron decide what the client can access.
- add the public key from the VPN client (see step 1) to the `peer` resource.
Example:
```
apiVersion: kilo.squat.ai/v1alpha1
kind: Peer
metadata:
name: squat
spec:
allowedIPs:
# desired IP address of the client's interface.
- 10.5.0.1/32
# Public Key of the client
publicKey: A......................................=
persistentKeepalive: 10
```
3. Configure your local VPN client in the following way:
```ini
[Interface]
PrivateKey = (already filled)
# IP address of VPN client; from the "peer" kubernetes resource as configured above
Address = 10.5.0.1/32
[Peer]
# from within the "kilo" Pod, run "wg" - that outputs the persistent, public key for
# the server
PublicKey = B......................................=
# Add the Pod and Service networks, e.g. if 10.42.* is the Pod Network; and 10.43.*
# is the Service network:
AllowedIPs = 10.42.0.0/16, 10.43.0.0/16
# public IP address of the Kilo node + Wireguard/Kilo UDP Port
Endpoint = 138.201.76.122:51820
# the server is always reachable, so we do not need PersistentKeepalive in
# this direction.
PersistentKeepalive = 0
```
## Optional: Using Calico / Canal to restrict the NetworkPolicy in the Cluster
If you are running Calico + Flannel (=Canal), you can use `HostEndpoint` combined
with a `GlobalNetworkPolicy` to restrict what each VPN client can access in the cluster:
```yaml
---
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
name: wireguard-kilo
labels:
interface: wireguard-kilo
spec:
node: THE_KILO_NODE_HERE
expectedIPs:
# in the "kilo" container, we run "ip address show kilo0",
# and this is the IP of the "inner" side of the wireguard interface.
# thus, we need to match on this IP here.
- 10.4.0.1
---
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: wireguard-kilo
spec:
selector: interface == 'wireguard-kilo'
# we want to apply the policy as it enters our cluster (exits the wireguard
# interface). On the application pods, we could not apply it anymore, because
# the IP address gets rewritten to the Flannel interface IP.
applyOnForward: true
types:
- Ingress
ingress:
- action: Allow
source:
nets:
- 10.5.0.1/32 # a certain VPN client ...
destination:
# ... can access a certain app
namespaceSelector: network-policy-namespace == "cattle-prometheus"
selector: app == "grafana"
# anything which is not whitelisted explicitly is forbidden
- action: Deny
```