feat: tailscale + dns
This commit is contained in:
parent
b1b8633945
commit
2a2b98f3fe
8 changed files with 110 additions and 74 deletions
|
@ -6,7 +6,7 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
ngx_http_geoip2_module = pkgs.stdenv.mkDerivation rec {
|
ngx_http_geoip2_module = pkgs.stdenv.mkDerivation {
|
||||||
name = "ngx_http_geoip2_module-a28ceff";
|
name = "ngx_http_geoip2_module-a28ceff";
|
||||||
src = pkgs.fetchgit {
|
src = pkgs.fetchgit {
|
||||||
url = "https://github.com/leev/ngx_http_geoip2_module";
|
url = "https://github.com/leev/ngx_http_geoip2_module";
|
||||||
|
@ -28,23 +28,45 @@ in
|
||||||
|
|
||||||
# PROXMOX
|
# PROXMOX
|
||||||
services.qemuGuest.enable = true;
|
services.qemuGuest.enable = true;
|
||||||
|
services.cloud-init = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
# USER MANAGEMENT
|
# USER MANAGEMENT
|
||||||
|
# TODO: Should this be in cloud-init?
|
||||||
nix.settings.trusted-users = [ "nixos" ];
|
nix.settings.trusted-users = [ "nixos" ];
|
||||||
users.users.nixos = {
|
users.users.nixos = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
|
password = "nixos";
|
||||||
extraGroups = [ "wheel" ];
|
extraGroups = [ "wheel" ];
|
||||||
openssh.authorizedKeys.keys = [
|
openssh.authorizedKeys.keys = [
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXAlzwziqfUUb2qmFwNF/nrBYc5MNT1MMOx81ohBmB+ tine@little.sys.tjo.space"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXAlzwziqfUUb2qmFwNF/nrBYc5MNT1MMOx81ohBmB+ tine@little.sys.tjo.space"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
|
# SSH
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings.PasswordAuthentication = false;
|
settings.PasswordAuthentication = false;
|
||||||
settings.KbdInteractiveAuthentication = false;
|
settings.KbdInteractiveAuthentication = false;
|
||||||
settings.PermitRootLogin = "no";
|
settings.PermitRootLogin = "no";
|
||||||
};
|
};
|
||||||
security.sudo.wheelNeedsPassword = false;
|
|
||||||
|
# TAILSCALE
|
||||||
|
services.tailscale = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# FIREWALL
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
trustedInterfaces = [ "tailscale0" ];
|
||||||
|
|
||||||
|
allowedUDPPorts = [ config.services.tailscale.port ];
|
||||||
|
allowedTCPPorts = [ 22 ];
|
||||||
|
};
|
||||||
|
|
||||||
# NGINX
|
# NGINX
|
||||||
services.nginx = {
|
services.nginx = {
|
||||||
|
|
4
justfile
4
justfile
|
@ -28,11 +28,13 @@ push:
|
||||||
--upload-file $NIXOS_IMAGE \
|
--upload-file $NIXOS_IMAGE \
|
||||||
https://code.tjo.space/api/packages/tjo-cloud/generic/ingress/${VERSION}/nixos.qcow2
|
https://code.tjo.space/api/packages/tjo-cloud/generic/ingress/${VERSION}/nixos.qcow2
|
||||||
|
|
||||||
deploy:
|
deploy: build
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
export NIXOS_IMAGE=$(nix path-info --quiet .#vm)/nixos.qcow2
|
export NIXOS_IMAGE=$(nix path-info --quiet .#vm)/nixos.qcow2
|
||||||
export TF_VAR_image_path=$NIXOS_IMAGE
|
export TF_VAR_image_path=$NIXOS_IMAGE
|
||||||
|
|
||||||
|
echo "Deploying $NIXOS_IMAGE"
|
||||||
|
|
||||||
cd {{justfile_directory()}}/terraform
|
cd {{justfile_directory()}}/terraform
|
||||||
tofu init
|
tofu init
|
||||||
tofu apply
|
tofu apply
|
||||||
|
|
53
terraform/dns.tf
Normal file
53
terraform/dns.tf
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
data "digitalocean_domain" "ingress" {
|
||||||
|
name = "ingress.tjo.cloud"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_record" "public" {
|
||||||
|
for_each = merge(
|
||||||
|
{
|
||||||
|
for key, node in local.nodes_with_address : key => {
|
||||||
|
node = node.name,
|
||||||
|
ip = node.public_ipv4,
|
||||||
|
type = "A",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
for key, node in local.nodes_with_address : key => {
|
||||||
|
node = node.name,
|
||||||
|
ip = node.public_ipv6,
|
||||||
|
type = "AAAA",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
domain = data.digitalocean_domain.ingress.id
|
||||||
|
type = each.value.type
|
||||||
|
name = lower(each.value.node)
|
||||||
|
value = each.value.ip
|
||||||
|
ttl = 60
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_record" "internal" {
|
||||||
|
for_each = merge(
|
||||||
|
{
|
||||||
|
for key, node in local.nodes_with_address : key => {
|
||||||
|
node = node.name,
|
||||||
|
ip = node.internal_ipv4,
|
||||||
|
type = "A",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
for key, node in local.nodes_with_address : key => {
|
||||||
|
node = node.name,
|
||||||
|
ip = node.internal_ipv6,
|
||||||
|
type = "AAAA",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
domain = data.digitalocean_domain.ingress.id
|
||||||
|
type = each.value.type
|
||||||
|
name = lower(each.value.node)
|
||||||
|
value = each.value.ip
|
||||||
|
ttl = 60
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
locals {
|
locals {
|
||||||
nodes_with_names = {
|
nodes_with_names = {
|
||||||
for k, v in var.nodes : k => merge(v, {
|
for k, v in var.nodes : k => merge(v, {
|
||||||
id = 700 + index(keys(var.nodes), k)
|
id = 700 + index(keys(var.nodes), k)
|
||||||
name = "${v.name}.ingress.tjo.cloud"
|
hostname = "${v.name}.ingress.tjo.cloud"
|
||||||
hash = sha1(v.name)
|
hash = sha1(v.name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
nodes = {
|
nodes = {
|
||||||
|
@ -28,12 +28,22 @@ locals {
|
||||||
nodes_with_address = {
|
nodes_with_address = {
|
||||||
for k, v in local.nodes :
|
for k, v in local.nodes :
|
||||||
k => merge(v, {
|
k => merge(v, {
|
||||||
ipv4 = local.ipv4_addresses[k]["eth0"][0]
|
public_ipv4 = local.ipv4_addresses[k]["eth0"][0]
|
||||||
ipv6 = local.ipv6_addresses[k]["eth0"][0]
|
public_ipv6 = local.ipv6_addresses[k]["eth0"][0]
|
||||||
|
internal_ipv4 = local.ipv4_addresses[k]["tailscale0"][0]
|
||||||
|
internal_ipv6 = local.ipv6_addresses[k]["tailscale0"][0]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource "tailscale_tailnet_key" "ingress" {
|
||||||
|
reusable = true
|
||||||
|
ephemeral = true
|
||||||
|
preauthorized = true
|
||||||
|
tags = ["tag:ingress-tjo-cloud"]
|
||||||
|
description = "tailscale key for ingress-tjo-cloud nodes"
|
||||||
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_file" "ingress" {
|
resource "proxmox_virtual_environment_file" "ingress" {
|
||||||
content_type = "iso"
|
content_type = "iso"
|
||||||
datastore_id = var.common_storage
|
datastore_id = var.common_storage
|
||||||
|
@ -54,9 +64,14 @@ resource "proxmox_virtual_environment_file" "cloudinit" {
|
||||||
|
|
||||||
source_raw {
|
source_raw {
|
||||||
data = <<-EOF
|
data = <<-EOF
|
||||||
|
#cloud-config
|
||||||
|
bootcmd:
|
||||||
|
- [ 'tailscale', 'up', '--authkey', '${tailscale_tailnet_key.ingress.key}',
|
||||||
|
'--hostname', '${each.value.name}',
|
||||||
|
'--accept-routes', 'true',
|
||||||
|
'--ssh' ]
|
||||||
EOF
|
EOF
|
||||||
file_name = "${each.value.name}.cloudinit.yaml"
|
file_name = "${each.value.hostname}.cloudinit.yaml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +79,7 @@ resource "proxmox_virtual_environment_vm" "nodes" {
|
||||||
for_each = local.nodes
|
for_each = local.nodes
|
||||||
|
|
||||||
vm_id = each.value.id
|
vm_id = each.value.id
|
||||||
name = each.value.name
|
name = each.value.hostname
|
||||||
node_name = each.value.host
|
node_name = each.value.host
|
||||||
|
|
||||||
description = "Node ${each.value.name} for ingress.tjo.cloud."
|
description = "Node ${each.value.name} for ingress.tjo.cloud."
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
|
|
||||||
locals {
|
|
||||||
locations = {
|
|
||||||
DE = ["46.4.88.62", "2a01:4f8:202:2395::"]
|
|
||||||
SI = ["93.103.125.118", "2a01:261:455:6c00:21e:6ff:fe45:c34"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data "digitalocean_domain" "ingress" {
|
|
||||||
name = "ingress.tjo.cloud"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "digitalocean_record" "locations" {
|
|
||||||
for_each = merge([
|
|
||||||
for location, ips in local.locations : {
|
|
||||||
for ip in ips : "${location} at ${ip}" => {
|
|
||||||
location = location,
|
|
||||||
ip = ip,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]...)
|
|
||||||
|
|
||||||
domain = data.digitalocean_domain.ingress.id
|
|
||||||
type = strcontains(each.value.ip, ":") ? "AAAA" : "A"
|
|
||||||
name = lower(each.value.location)
|
|
||||||
value = each.value.ip
|
|
||||||
ttl = 60
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
locals {
|
|
||||||
listeners = [
|
|
||||||
{
|
|
||||||
domain = "k8s.tjo.cloud"
|
|
||||||
name = "api"
|
|
||||||
locations = ["SI", "DE"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
domain = "k8s.tjo.cloud"
|
|
||||||
name = "dashboard"
|
|
||||||
locations = ["SI", "DE"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "digitalocean_record" "listeners" {
|
|
||||||
for_each = merge(flatten([
|
|
||||||
for listener in local.listeners :
|
|
||||||
[
|
|
||||||
for location in listener.locations : {
|
|
||||||
for ip in local.locations[location] : "${ip} for ${listener.name}.${listener.domain} at ${location}" => {
|
|
||||||
ip = ip
|
|
||||||
domain = listener.domain
|
|
||||||
name = listener.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
])...)
|
|
||||||
|
|
||||||
domain = each.value.domain
|
|
||||||
type = strcontains(each.value.ip, ":") ? "AAAA" : "A"
|
|
||||||
name = each.value.name
|
|
||||||
value = each.value.ip
|
|
||||||
ttl = 60
|
|
||||||
}
|
|
|
@ -25,6 +25,9 @@ provider "digitalocean" {
|
||||||
token = var.digitalocean_token
|
token = var.digitalocean_token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider "tailscale" {
|
||||||
|
api_key = var.tailscale_apikey
|
||||||
|
}
|
||||||
|
|
||||||
provider "proxmox" {
|
provider "proxmox" {
|
||||||
# FIXME: Traefik/NGINX breaks this! 500 ERROR
|
# FIXME: Traefik/NGINX breaks this! 500 ERROR
|
||||||
|
|
|
@ -27,6 +27,10 @@ variable "proxmox_token" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "tailscale_apikey" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
variable "image_path" {
|
variable "image_path" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue