mirror of
https://github.com/khuedoan/homelab.git
synced 2024-12-23 01:34:33 +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] Modular architecture, easy to add or remove features/components
|
||||||
- [x] Automated certificate management
|
- [x] Automated certificate management
|
||||||
- [x] Automatically update DNS records for exposed services
|
- [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] Expose services to the internet securely with [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/)
|
||||||
- [x] CI/CD platform
|
- [x] CI/CD platform
|
||||||
- [x] Private container registry
|
- [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><a href="https://www.vaultproject.io">Vault</a></td>
|
||||||
<td>Secrets and encryption management system</td>
|
<td>Secrets and encryption management system</td>
|
||||||
</tr>
|
</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>
|
</table>
|
||||||
|
|
||||||
## Get Started
|
## 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.
|
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).
|
Below is a list of external resources and why we need them (also see some [alternatives](#alternatives) below).
|
||||||
|
|
||||||
| Provider | Resource | Purpose |
|
| Provider | Resource | Purpose |
|
||||||
| -------- | -------- | ------- |
|
| -------- | -------- | ------- |
|
||||||
| Terraform Cloud | Workspace | Terraform state backend |
|
| 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 | 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 |
|
| 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 | -->
|
<!-- | Minio | Bucket | Onsite backup | -->
|
||||||
<!-- | AWS | S3 Glacier | Offsite 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 -->
|
<!-- └── 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 -->
|
<!-- ### Create Minio keys -->
|
||||||
|
|
||||||
<!-- TODO: skip this for now -->
|
<!-- TODO: skip this for now -->
|
||||||
@ -59,7 +65,21 @@ If you decide to use a [different Terraform backend](https://www.terraform.io/la
|
|||||||
|
|
||||||
## Alternatives
|
## Alternatives
|
||||||
|
|
||||||
- Terraform Cloud: any other [Terraform backends](https://www.terraform.io/language/settings/backends)
|
To avoid vendor lock-in, each external provider must have an equivalent alternative that is easy to replace:
|
||||||
- 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))
|
- 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... -->
|
<!-- - 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_email = var.cloudflare_email
|
||||||
cloudflare_api_key = var.cloudflare_api_key
|
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
|
- external-dns
|
||||||
- k8up-operator
|
- k8up-operator
|
||||||
- tekton-pipelines
|
- tekton-pipelines
|
||||||
|
- zerotier
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
cloudflare_email = "{{ cloudflare_email }}"
|
cloudflare_email = "{{ cloudflare_email }}"
|
||||||
cloudflare_api_key = "{{ cloudflare_api_key }}"
|
cloudflare_api_key = "{{ cloudflare_api_key }}"
|
||||||
cloudflare_account_id = "{{ cloudflare_account_id }}"
|
cloudflare_account_id = "{{ cloudflare_account_id }}"
|
||||||
|
zerotier_central_token = "{{ zerotier_central_token }}"
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
- name: cloudflare_account_id
|
- name: cloudflare_account_id
|
||||||
prompt: Enter Cloudflare account ID
|
prompt: Enter Cloudflare account ID
|
||||||
private: false
|
private: false
|
||||||
|
- name: zerotier_central_token
|
||||||
|
prompt: Enter ZeroTier Central API Token
|
||||||
tasks:
|
tasks:
|
||||||
- name: Render environment file
|
- name: Render environment file
|
||||||
template:
|
template:
|
||||||
|
@ -10,3 +10,7 @@ variable "cloudflare_api_key" {
|
|||||||
variable "cloudflare_account_id" {
|
variable "cloudflare_account_id" {
|
||||||
type = string
|
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