From 2046f62a4f8372d9d1699c8a6da10d15f8bdd01e Mon Sep 17 00:00:00 2001
From: Matthew Baggett <matthew@baggett.me>
Date: Thu, 16 Jan 2025 19:40:02 +0100
Subject: [PATCH] Headscale

---
 products/headscale/admin.tf         | 35 ++++++++++++++-----------
 products/headscale/build/Dockerfile |  9 +++++++
 products/headscale/config.tf        | 40 +++++++++++++++++------------
 products/headscale/headscale.tf     | 27 +++++++++----------
 products/headscale/inputs.tf        |  2 +-
 products/headscale/outputs.tf       |  9 +++++++
 products/headscale/postgres.tf      |  1 -
 products/headscale/secrets.tf       | 12 +++++++++
 8 files changed, 86 insertions(+), 49 deletions(-)
 create mode 100644 products/headscale/build/Dockerfile
 create mode 100644 products/headscale/secrets.tf

diff --git a/products/headscale/admin.tf b/products/headscale/admin.tf
index 3d059d4..bc77490 100644
--- a/products/headscale/admin.tf
+++ b/products/headscale/admin.tf
@@ -1,18 +1,23 @@
 module "admin" {
-  source       = "../../docker/service"
-  image        = var.admin_image
-  service_name = "admin"
-  stack_name   = var.stack_name
-  volumes = {
-    "headscale-config" = "/var/lib/headscale"
-  }
-  networks        = [module.network]
-  converge_enable = false
-  traefik = {
-    domain = var.domain
-    ssl    = true
-    rule   = "Host(`${var.domain}`) && PathPrefix(`/admin`)"
-    port   = 80
-  }
+  source                = "../../docker/service"
+  image                 = var.admin_image
+  service_name          = "admin"
+  stack_name            = var.stack_name
+  configs               = { "/etc/headscale/config.yaml" = yamlencode(local.config) }
   placement_constraints = var.placement_constraints
+  networks              = [module.network]
+  converge_enable       = false
+  ports                 = [{ container = 80 }]
+  traefik = {
+    domain  = var.domain
+    ssl     = true
+    non-ssl = true
+    rule    = "Host(`${var.domain}`) && PathPrefix(`/manager`)"
+    port    = 80
+  }
+  labels = {
+    #"traefik.http.middlewares.stripprefix.stripprefix.prefixes" = "/manager"
+    #"traefik.http.routers.headscale-admin-ssl.middlewares"      = "stripprefix"
+
+  }
 }
\ No newline at end of file
diff --git a/products/headscale/build/Dockerfile b/products/headscale/build/Dockerfile
new file mode 100644
index 0000000..9bda747
--- /dev/null
+++ b/products/headscale/build/Dockerfile
@@ -0,0 +1,9 @@
+FROM alpine:3.18 AS headscale-alpine
+RUN apk add --no-cache \
+      ca-certificates \
+      bash
+ENTRYPOINT ["/usr/bin/headscale"]
+CMD ["/usr/bin/headscale", "serve"]
+COPY --from=headscale/headscale:stable /ko-app/headscale /usr/bin/headscale
+RUN chmod +x /usr/bin/headscale && \
+    headscale version
diff --git a/products/headscale/config.tf b/products/headscale/config.tf
index 49806eb..08c0909 100644
--- a/products/headscale/config.tf
+++ b/products/headscale/config.tf
@@ -1,6 +1,6 @@
 locals {
   config = {
-    server_url          = "https://${var.domain}"
+    server_url          = "http://${var.domain}"
     listen_addr         = "0.0.0.0:8080"
     metrics_listen_addr = "0.0.0.0:9090"
     grpc_listen_addr    = "0.0.0.0:50443"
@@ -9,10 +9,11 @@ locals {
     noise = {
       private_key_path = "/var/lib/headscale/noise_private.key"
     }
-    ip_prefixes = [
-      #"fd7a:115c:a1e0::/48",
-      "100.64.0.0/10",
-    ]
+    prefixes = {
+      #v6 = "fd7a:115c:a1e0::/48"
+      v4         = "100.64.0.0/10"
+      allocation = "sequential"
+    }
     derp = {
       server = {
         enabled          = false
@@ -33,12 +34,16 @@ locals {
     node_update_check_interval        = "10s"
 
     # Database bits
-    db_type = "postgres"
-    db_host = module.postgres.service_name
-    db_port = "5432"
-    db_name = module.postgres.database
-    db_user = module.postgres.username
-    db_pass = module.postgres.password
+    database = {
+      type = "postgres"
+      postgres = {
+        host = module.postgres.service_name
+        port = 5432
+        name = module.postgres.database
+        user = module.postgres.username
+        pass = module.postgres.password
+      }
+    }
 
     # Lets encrypt bits
     #acme_url = "https://acme-v02.api.letsencrypt.org/directory"
@@ -57,14 +62,15 @@ locals {
     }
 
     # ACL
-    acl_policy_path = ""
+    policy = {
+      path = ""
+    }
 
     # DNS
-    dns_config = {
-      override_local_dns = true
-      nameservers        = ["1.1.1.1"]
-      magic_dns          = true
-      base_domain        = var.domain
+    dns = {
+      nameservers = ["1.1.1.1"]
+      magic_dns   = true
+      base_domain = "ts.${var.domain}"
     }
 
     unix_socket            = "/var/run/headscale.sock"
diff --git a/products/headscale/headscale.tf b/products/headscale/headscale.tf
index 17881fa..8444692 100644
--- a/products/headscale/headscale.tf
+++ b/products/headscale/headscale.tf
@@ -1,22 +1,19 @@
 module "headscale" {
-  source       = "../../docker/service"
-  image        = var.image
-  service_name = "headscale"
-  stack_name   = var.stack_name
-  volumes = {
-    "headscale-config" = "/var/lib/headscale"
-  }
-  configs = {
-    "/etc/headscale/config.yaml" = yamlencode(local.config)
-  }
-  networks        = [module.network]
-  converge_enable = false
-  command         = ["headscale", "serve"]
+  source                = "../../docker/service"
+  image                 = "matthewbaggett/headscale-alpine:latest"
+  service_name          = "headscale"
+  stack_name            = var.stack_name
+  volumes               = { "headscale-config" = "/var/lib/headscale" }
+  configs               = { "/etc/headscale/config.yaml" = yamlencode(local.config) }
+  networks              = [module.network]
+  converge_enable       = false
+  command               = ["headscale", "serve"]
+  placement_constraints = var.placement_constraints
+  ports                 = [{ container = 9090 }, { container = 8080 }]
   traefik = {
     domain = var.domain
     ssl    = true
-    rule   = "Host(`${var.domain}`) && !PathPrefix(`/admin`)"
+    rule   = "Host(`${var.domain}`) && !PathPrefix(`/manager`)"
     port   = 8080
   }
-  placement_constraints = var.placement_constraints
 }
\ No newline at end of file
diff --git a/products/headscale/inputs.tf b/products/headscale/inputs.tf
index f444858..2963c8f 100644
--- a/products/headscale/inputs.tf
+++ b/products/headscale/inputs.tf
@@ -4,7 +4,7 @@ variable "image" {
 }
 variable "admin_image" {
   description = "The headscale admin image to deploy"
-  default     = "goodieshq/headscale-admin:0.1.7b"
+  default     = "simcu/headscale-ui"
 }
 variable "stack_name" {
   description = "The name of the stack"
diff --git a/products/headscale/outputs.tf b/products/headscale/outputs.tf
index e69de29..83bc8c4 100644
--- a/products/headscale/outputs.tf
+++ b/products/headscale/outputs.tf
@@ -0,0 +1,9 @@
+output "postgres" {
+  value = module.postgres.endpoint
+}
+output "auth" {
+  value = {
+    username = random_pet.user.id
+    password = nonsensitive(random_password.password.result)
+  }
+}
\ No newline at end of file
diff --git a/products/headscale/postgres.tf b/products/headscale/postgres.tf
index 6b04ded..6ee37ad 100644
--- a/products/headscale/postgres.tf
+++ b/products/headscale/postgres.tf
@@ -1,6 +1,5 @@
 module "postgres" {
   source                = "../postgres"
-  postgres_version      = "16"
   stack_name            = var.stack_name
   networks              = [module.network]
   placement_constraints = var.placement_constraints
diff --git a/products/headscale/secrets.tf b/products/headscale/secrets.tf
new file mode 100644
index 0000000..9cc1803
--- /dev/null
+++ b/products/headscale/secrets.tf
@@ -0,0 +1,12 @@
+resource "random_pet" "user" {
+  length    = 2
+  separator = ""
+}
+resource "random_password" "password" {
+  length  = 32
+  special = true
+}
+resource "random_password" "key" {
+  length  = 32
+  special = false
+}
\ No newline at end of file