mirror of
https://github.com/khuedoan/homelab.git
synced 2024-12-23 01:14:38 +07:00
feat: add ZeroTier for remote access
- Fully open source - Has free hosted version (my.zerotier.com) - Can be automated with Terraform - Pretty good performance with UDP hole punching
This commit is contained in:
parent
1d2d73a7a3
commit
8447502d54
@ -47,6 +47,7 @@ More information can be found in [the roadmap](#roadmap) below.
|
||||
- [x] Modular architecture, easy to add or remove features/components
|
||||
- [x] Automated certificate management
|
||||
- [x] Automatically update DNS records for exposed services
|
||||
- [x] VPN
|
||||
- [x] Expose services to the internet securely with [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/)
|
||||
- [x] CI/CD platform
|
||||
- [x] Private container registry
|
||||
@ -195,6 +196,11 @@ They can't capture all the project's features, but they are sufficient to get a
|
||||
<td><a href="https://www.vaultproject.io">Vault</a></td>
|
||||
<td>Secrets and encryption management system</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img width="32" src="https://docs.zerotier.com/img/ZeroTierIcon.png"></td>
|
||||
<td><a href="https://zerotier.com">ZeroTier</a></td>
|
||||
<td>VPN without port-forwarding</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Get Started
|
||||
|
@ -7,11 +7,12 @@
|
||||
Although I try to keep the amount of external resources to the minimum, there's still need for a few of them.
|
||||
Below is a list of external resources and why we need them (also see some [alternatives](#alternatives) below).
|
||||
|
||||
| Provider | Resource | Purpose |
|
||||
| -------- | -------- | ------- |
|
||||
| Terraform Cloud | Workspace | Terraform state backend |
|
||||
| Cloudflare | DNS | DNS and [DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) for certificates |
|
||||
| Cloudflare | Tunnel | Public services to the internet without port-forwarding |
|
||||
| Provider | Resource | Purpose |
|
||||
| -------- | -------- | ------- |
|
||||
| Terraform Cloud | Workspace | Terraform state backend |
|
||||
| Cloudflare | DNS | DNS and [DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) for certificates |
|
||||
| Cloudflare | Tunnel | Public services to the internet without port-forwarding |
|
||||
| ZeroTier | Virtual network | Use as VPN to access home network from anywhere (with [UDP hole punching](https://en.wikipedia.org/wiki/UDP_hole_punching)) |
|
||||
<!-- | Minio | Bucket | Onsite backup | -->
|
||||
<!-- | AWS | S3 Glacier | Offsite backup | -->
|
||||
|
||||
@ -49,6 +50,11 @@ If you decide to use a [different Terraform backend](https://www.terraform.io/la
|
||||
<!-- └── Is in - 117.xxx.xxx.xxx, 2402:xxx:xxx:xxx:xxx:xxx:xxx:xxx -->
|
||||
<!-- ``` -->
|
||||
|
||||
### ZeroTier
|
||||
|
||||
- Create a ZeroTier account <https://my.zerotier.com>
|
||||
- Generate a new API Token at <https://my.zerotier.com/account>
|
||||
|
||||
<!-- ### Create Minio keys -->
|
||||
|
||||
<!-- TODO: skip this for now -->
|
||||
@ -59,7 +65,21 @@ If you decide to use a [different Terraform backend](https://www.terraform.io/la
|
||||
|
||||
## Alternatives
|
||||
|
||||
- Terraform Cloud: any other [Terraform backends](https://www.terraform.io/language/settings/backends)
|
||||
- Cloudflare DNS: see [manual DNS setup](../../tutorials/manual-dns-setup.md)
|
||||
- Cloudflare Tunnel: you can create a small VPS in the cloud and utilize Wireguard and HAProxy to route traffic via it, or just use simple port-forwarding if it's available (see also [awesome tunneling](https://github.com/anderspitman/awesome-tunneling))
|
||||
To avoid vendor lock-in, each external provider must have an equivalent alternative that is easy to replace:
|
||||
|
||||
- Terraform Cloud:
|
||||
- Any other [Terraform backends](https://www.terraform.io/language/settings/backends)
|
||||
- Cloudflare DNS:
|
||||
- Update cert-manager and external-dns to use a different provider
|
||||
- [Manual DNS setup](../../tutorials/manual-dns-setup.md)
|
||||
- Cloudflare Tunnel:
|
||||
- Use port-forwarding if it's available
|
||||
- Create a small VPS in the cloud and utilize Wireguard and HAProxy to route traffic via it
|
||||
- Access everything via VPN
|
||||
- See also [awesome tunneling](https://github.com/anderspitman/awesome-tunneling)
|
||||
- ZeroTier virtual network:
|
||||
- [Host your own ZeroTier](https://docs.zerotier.com/self-hosting/introduction)
|
||||
- [Tailscale](https://tailscale.com) (closed source, but you can use [Headscale](https://github.com/juanfont/headscale) to host your own Tailscale control server)
|
||||
- [Netmaker](https://www.netmaker.org) (there's no hosted version, you'll need to host your own server)
|
||||
- Wireguard server (requires port-forwarding)
|
||||
<!-- - Minio and S3 Glacier: any S3 compatible object storage, such as Backblaze B2, Minio... -->
|
||||
|
@ -4,3 +4,11 @@ module "cloudflare" {
|
||||
cloudflare_email = var.cloudflare_email
|
||||
cloudflare_api_key = var.cloudflare_api_key
|
||||
}
|
||||
|
||||
module "zerotier" {
|
||||
source = "./modules/zerotier"
|
||||
zerotier_central_token = var.zerotier_central_token
|
||||
bridged_routes = [
|
||||
"192.168.1.0/24" # TODO add this to configure script
|
||||
]
|
||||
}
|
||||
|
53
external/modules/zerotier/main.tf
Normal file
53
external/modules/zerotier/main.tf
Normal file
@ -0,0 +1,53 @@
|
||||
locals {
|
||||
router_ip = cidrhost(var.managed_route, 1) # Use the second IP in the VPN subnet as the router
|
||||
}
|
||||
|
||||
resource "zerotier_network" "network" {
|
||||
name = var.name
|
||||
description = var.description
|
||||
private = true
|
||||
|
||||
route {
|
||||
target = var.managed_route
|
||||
}
|
||||
|
||||
dynamic "route" {
|
||||
for_each = var.bridged_routes
|
||||
|
||||
content {
|
||||
target = route.value
|
||||
via = local.router_ip
|
||||
}
|
||||
}
|
||||
|
||||
assignment_pool {
|
||||
start = cidrhost(var.managed_route, 0)
|
||||
end = cidrhost(var.managed_route, -1)
|
||||
}
|
||||
}
|
||||
|
||||
resource "zerotier_identity" "router" {}
|
||||
|
||||
resource "zerotier_member" "router" {
|
||||
network_id = zerotier_network.network.id
|
||||
name = "router"
|
||||
member_id = zerotier_identity.router.id
|
||||
allow_ethernet_bridging = true
|
||||
no_auto_assign_ips = true
|
||||
ip_assignments = [
|
||||
local.router_ip
|
||||
]
|
||||
}
|
||||
|
||||
resource "kubernetes_secret" "router" {
|
||||
metadata {
|
||||
name = "zerotier-router"
|
||||
namespace = "zerotier"
|
||||
}
|
||||
|
||||
data = {
|
||||
ZEROTIER_NETWORK_ID = zerotier_network.network.id
|
||||
ZEROTIER_IDENTITY_PUBLIC = zerotier_identity.router.public_key
|
||||
ZEROTIER_IDENTITY_SECRET = zerotier_identity.router.private_key
|
||||
}
|
||||
}
|
34
external/modules/zerotier/variables.tf
Normal file
34
external/modules/zerotier/variables.tf
Normal file
@ -0,0 +1,34 @@
|
||||
variable "zerotier_central_url" {
|
||||
description = "ZeroTier Central API endpoint"
|
||||
type = string
|
||||
default = "https://my.zerotier.com/api" # https://github.com/zerotier/go-ztcentral/blob/4d397d1e82c043a5376789177ad55536044d69ce/client.go#L44
|
||||
}
|
||||
|
||||
variable "zerotier_central_token" {
|
||||
description = "ZeroTier Central API Token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
description = "Network name"
|
||||
type = string
|
||||
default = "homelab"
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
description = "Network description"
|
||||
type = string
|
||||
default = "Homelab network"
|
||||
}
|
||||
|
||||
variable "managed_route" {
|
||||
description = "ZeroTier managed route"
|
||||
type = string
|
||||
default = "10.147.17.0/24"
|
||||
}
|
||||
|
||||
variable "bridged_routes" {
|
||||
description = "List of bridged routes" # TODO
|
||||
type = list(string)
|
||||
}
|
13
external/modules/zerotier/versions.tf
Normal file
13
external/modules/zerotier/versions.tf
Normal file
@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
zerotier = {
|
||||
source = "zerotier/zerotier"
|
||||
version = "~> 1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "zerotier" {
|
||||
zerotier_central_url = var.zerotier_central_url
|
||||
zerotier_central_token = var.zerotier_central_token
|
||||
}
|
@ -12,3 +12,4 @@
|
||||
- external-dns
|
||||
- k8up-operator
|
||||
- tekton-pipelines
|
||||
- zerotier
|
||||
|
@ -1,3 +1,4 @@
|
||||
cloudflare_email = "{{ cloudflare_email }}"
|
||||
cloudflare_api_key = "{{ cloudflare_api_key }}"
|
||||
cloudflare_account_id = "{{ cloudflare_account_id }}"
|
||||
zerotier_central_token = "{{ zerotier_central_token }}"
|
||||
|
@ -9,6 +9,8 @@
|
||||
- name: cloudflare_account_id
|
||||
prompt: Enter Cloudflare account ID
|
||||
private: false
|
||||
- name: zerotier_central_token
|
||||
prompt: Enter ZeroTier Central API Token
|
||||
tasks:
|
||||
- name: Render environment file
|
||||
template:
|
||||
|
@ -10,3 +10,7 @@ variable "cloudflare_api_key" {
|
||||
variable "cloudflare_account_id" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "zerotier_central_token" {
|
||||
type = string
|
||||
}
|
||||
|
65
platform/zerotier/deployment.yaml
Normal file
65
platform/zerotier/deployment.yaml
Normal file
@ -0,0 +1,65 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zerotier-router
|
||||
namespace: zerotier
|
||||
labels:
|
||||
app: zerotier-router
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: zerotier-router
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: zerotier-router
|
||||
spec:
|
||||
containers:
|
||||
- name: zerotier-router
|
||||
image: zerotier/zerotier:latest # TODO use tag
|
||||
command:
|
||||
- "sh"
|
||||
- "-c"
|
||||
args: # TODO optimize this
|
||||
- |
|
||||
# TODO install this on upstream image?
|
||||
apt-get install -y iptables
|
||||
|
||||
# TODO is there a better way to get the interface name?
|
||||
export PHY_IFACE="$(ip route | grep ${POD_IP} | cut -d ' ' -f 3)"
|
||||
export ZT_IFACE=zt0
|
||||
|
||||
# Override the default random interface name
|
||||
mkdir -p /var/lib/zerotier-one
|
||||
echo "${ZEROTIER_NETWORK_ID}=${ZT_IFACE}" >> /var/lib/zerotier-one/devicemap
|
||||
|
||||
iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE
|
||||
iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT
|
||||
|
||||
/entrypoint.sh "${ZEROTIER_NETWORK_ID}"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
env:
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: zerotier-router
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
volumeMounts:
|
||||
- name: dev-net-tun
|
||||
mountPath: /dev/net/tun
|
||||
volumes:
|
||||
- name: dev-net-tun
|
||||
hostPath:
|
||||
path: /dev/net/tun
|
||||
type: CharDevice
|
5
platform/zerotier/kustomization.yaml
Normal file
5
platform/zerotier/kustomization.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
Loading…
Reference in New Issue
Block a user