From 862e8e087f00ff0f9b16a6d2d7497cb8ebd99c5f Mon Sep 17 00:00:00 2001 From: Jeremy Nguyen Date: Wed, 1 Jul 2026 00:40:06 -0700 Subject: [PATCH] feat: add WireGuard support with wg-easy - Add wg-easy Docker container to docker-compose.yml - Configure wg-easy environment variables in wg-easy.env.tftpl - Update cloudflare.config.yml to include wireguard subdomain - Update authelia.configuration.yml to add wg-easy OIDC client - Update gatus-config.yaml to monitor WireGuard endpoints --- dns.tf | 11 ++++++++++- files/authelia.configuration.yml | 14 ++++++++++++++ files/gatus-config.yaml | 8 ++++++++ main.tf | 12 +++++++++++- network.tf | 11 +++++++++++ templates/authelia.env.tftpl | 2 ++ templates/cloudflare.config.yml.tftpl | 4 +++- templates/docker-compose.yml.tftpl | 18 ++++++++++++++++++ templates/wg-easy.env.tftpl | 15 +++++++++++++++ 9 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 templates/wg-easy.env.tftpl diff --git a/dns.tf b/dns.tf index 4fd26d6..bae73fa 100644 --- a/dns.tf +++ b/dns.tf @@ -17,7 +17,7 @@ resource "cloudflare_dns_record" "www" { } resource "cloudflare_dns_record" "tunnels" { - for_each = toset(["@", "webdav", "vault", "rss", "status", "auth"]) + for_each = toset(["@", "webdav", "vault", "rss", "status", "auth", "wireguard"]) content = "${cloudflare_zero_trust_tunnel_cloudflared.main.id}.cfargotunnel.com" name = each.value proxied = true @@ -35,6 +35,15 @@ resource "cloudflare_dns_record" "ssh" { zone_id = cloudflare_zone.main.id } +resource "cloudflare_dns_record" "vpn" { + content = oci_core_instance.main.public_ip + name = "vpn" + proxied = false + ttl = 1 + type = "A" + zone_id = cloudflare_zone.main.id +} + resource "cloudflare_dns_record" "mail" { content = oci_email_email_return_path.main.cname_record_value name = oci_email_email_return_path.main.dns_subdomain_name diff --git a/files/authelia.configuration.yml b/files/authelia.configuration.yml index b43e1ae..84b3e91 100644 --- a/files/authelia.configuration.yml +++ b/files/authelia.configuration.yml @@ -147,3 +147,17 @@ identity_providers: access_token_signed_response_alg: "none" userinfo_signed_response_alg: "none" token_endpoint_auth_method: "client_secret_basic" + - client_id: '{{ env "WG_EASY_CLIENT_ID" }}' + client_name: wg-easy + client_secret: '{{ env "WG_EASY_CLIENT_SECRET" }}' + redirect_uris: + - 'https://wireguard.{{ env "SERVER_DOMAIN" }}/api/auth/oidc/callback' + - 'https://wireguard.{{ env "SERVER_DOMAIN" }}/api/auth/oidc/link' + scopes: + - openid + - profile + - email + authorization_policy: one_factor + pre_configured_consent_duration: 1 week + require_pkce: true + token_endpoint_auth_method: client_secret_post diff --git a/files/gatus-config.yaml b/files/gatus-config.yaml index 2f80451..a2f3d40 100644 --- a/files/gatus-config.yaml +++ b/files/gatus-config.yaml @@ -82,6 +82,10 @@ endpoints: group: core url: "https://auth.${SERVER_DOMAIN}" <<: *status-200 + - name: Wireguard + group: core + url: "https://wireguard.${SERVER_DOMAIN}" + <<: *status-200 # Internal - name: Vaultwarden (Internal) group: internal @@ -108,6 +112,10 @@ endpoints: group: internal url: "http://authelia:9091" <<: *status-200 + - name: Wireguard + group: internal + url: "http://wg-easy:51821" + <<: *status-200 - name: Instance Metadata group: internal url: "http://169.254.169.254/opc/v2/instance/" diff --git a/main.tf b/main.tf index 07e0eaf..b73ef4b 100644 --- a/main.tf +++ b/main.tf @@ -44,7 +44,7 @@ resource "oci_vault_secret" "cloudflare_tunnel_secret" { } locals { - auth_clients = ["gatus", "vaultwarden", "freshrss"] + auth_clients = ["gatus", "vaultwarden", "freshrss", "wg_easy"] } resource "oci_vault_secret" "auth_client_secrets" { @@ -336,6 +336,16 @@ locals { smtp_from = oci_email_sender.senders["auth"].email_address }) }, + { + path = "/home/jeremy/wg-easy.env", + content = templatefile("${path.module}/templates/wg-easy.env.tftpl", { + auth = { + client_id = local.auth_client_ids.wg_easy + client_secret = local.auth_client_secrets.wg_easy + } + server_domain = var.server_domain + }) + }, { path = "/home/jeremy/vaultwarden-database-url", content = join("", [ diff --git a/network.tf b/network.tf index 300ba7f..0a2dea1 100644 --- a/network.tf +++ b/network.tf @@ -24,6 +24,17 @@ resource "oci_core_security_list" "instance" { } } + ingress_security_rules { + description = "Allow WireGuard traffic" + source = "0.0.0.0/0" + source_type = "CIDR_BLOCK" + protocol = 17 # UDP + udp_options { + min = 51820 + max = 51820 + } + } + # TODO: Limit egress traffic to only necessary destinations (e.g. Cloudflare, # Ubuntu, Docker, Oracle, etc.) egress_security_rules { diff --git a/templates/authelia.env.tftpl b/templates/authelia.env.tftpl index 79f7678..dad93c0 100644 --- a/templates/authelia.env.tftpl +++ b/templates/authelia.env.tftpl @@ -6,6 +6,8 @@ VAULTWARDEN_CLIENT_ID=${auth.client_ids.vaultwarden} VAULTWARDEN_CLIENT_SECRET='${auth.client_secrets.vaultwarden}' FRESHRSS_CLIENT_ID=${auth.client_ids.freshrss} FRESHRSS_CLIENT_SECRET='${auth.client_secrets.freshrss}' +WG_EASY_CLIENT_ID='${auth.client_ids.wg_easy}' +WG_EASY_CLIENT_SECRET='${auth.client_secrets.wg_easy}' SMTP_USERNAME=$${SMTP_USERNAME} SMTP_PASSWORD=$${SMTP_PASSWORD} SMTP_HOST=$${SMTP_HOST} diff --git a/templates/cloudflare.config.yml.tftpl b/templates/cloudflare.config.yml.tftpl index cef1f4e..b90ad81 100644 --- a/templates/cloudflare.config.yml.tftpl +++ b/templates/cloudflare.config.yml.tftpl @@ -9,7 +9,7 @@ edge-ip-version: auto region: us ingress: - %{~ for subdomain in ["vault.", "webdav.", "status.", "rss.", "auth.", ""] ~} + %{~ for subdomain in ["vault.", "webdav.", "status.", "rss.", "auth.", "wireguard.", ""] ~} - hostname: ${subdomain}${server_domain}/robots.txt service: http://robots-txt:8008 %{~ endfor ~} @@ -23,4 +23,6 @@ ingress: service: http://freshrss:80 - hostname: auth.${server_domain} service: http://authelia:9091 + - hostname: wireguard.${server_domain} + service: http://wg-easy:51821 - service: http_status:404 diff --git a/templates/docker-compose.yml.tftpl b/templates/docker-compose.yml.tftpl index 2a2452f..76fe242 100644 --- a/templates/docker-compose.yml.tftpl +++ b/templates/docker-compose.yml.tftpl @@ -80,6 +80,7 @@ services: - foldingathome - freshrss - authelia + - wg-easy environment: SERVER_DOMAIN: "${server_domain}" EMAIL_FROM: "${email.gatus}" @@ -107,6 +108,7 @@ services: - gatus - freshrss - authelia + - wg-easy restart: always volumes: - "/mnt/oraclevdb/cloudflared:/user/nonroot/.cloudflared/" @@ -187,6 +189,22 @@ services: - "./users_database.yml:/config/users_database.yml" - "/mnt/oraclevdb/authelia/:/config/" + wg-easy: + container_name: wg-easy + image: ghcr.io/wg-easy/wg-easy:edge + restart: always + cap_add: + - NET_ADMIN + - SYS_MODULE + sysctls: + - net.ipv4.ip_forward=1 + - net.ipv4.conf.all.src_valid_mark=1 + volumes: + - /mnt/oraclevdb/wireguard/:/etc/wireguard/ + ports: + - 51820:51820/udp + env_file: ./wg-easy.env + configs: rclone-config: file: ./rclone.conf diff --git a/templates/wg-easy.env.tftpl b/templates/wg-easy.env.tftpl new file mode 100644 index 0000000..aabe86d --- /dev/null +++ b/templates/wg-easy.env.tftpl @@ -0,0 +1,15 @@ +HOST="wg-easy" +PORT=51821 +WG_HOST="vpn.${server_domain}" +DISABLE_IPV6=true +OAUTH_PROVIDERS="oidc" +OAUTH_AUTO_REGISTER=true +OAUTH_OIDC_SERVER="https://auth.${server_domain}" +OAUTH_OIDC_CLIENT_ID="${auth.client_id}" +OAUTH_OIDC_CLIENT_SECRET="${auth.client_secret}" +OAUTH_OIDC_NAME="Authelia" +INIT_ENABLED=true +INIT_USERNAME=admin +INIT_PASSWORD="${auth.client_secret}" +INIT_HOST="vpn.${server_domain}" +INIT_PORT=51820