diff --git a/.env.example b/.env.example index 6a241a8..b5a7ed2 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ HCLOUD_TOKEN="your_token_here" +CLOUDFLARE_API_TOKEN="your_token_here" diff --git a/.gitignore b/.gitignore index 4e5db05..af23183 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ *.tfstate *.tfstate.backup *.tfstate.*.backup -.terraform.lock.hcl result diff --git a/README.md b/README.md index 5087831..4a51729 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# OpenTofu server configuration +# VPN server configuration -This is an experimental configuration for my Hetzner VPS using OpenTofu and Nix, based on [NixOS/nixos-wiki-infra on Github](https://github.com/NixOS/nixos-wiki-infra). +This is an experimental configuration for my Hetzner VPS and Cloudflare to run a VPN using OpenTofu and Nix, based on [NixOS/nixos-wiki-infra on Github](https://github.com/NixOS/nixos-wiki-infra). ## How to use @@ -14,4 +14,19 @@ For cross-compiling, you will need to add a builder by visiting the following re Run `nix develop` at the root of the project directory to access a shell where OpenTofu is accessible. -In the `targets` directory, go to the system and run the corresponding shell files to make modifications. +In the `targets` directory, run `./apply.sh` to update the configurations. + +## VPN + +To set up the VPN, on the VPS run: + +```bash +headscale users create default +headscale preauthkeys create --user default --reusable +``` + +On the client run: + +```bash +tailscale up --login-server --auth-key +``` diff --git a/flake.nix b/flake.nix index 3f0fcda..f59fed3 100644 --- a/flake.nix +++ b/flake.nix @@ -29,6 +29,7 @@ pkgs.terraform-providers.null pkgs.terraform-providers.external pkgs.terraform-providers.local + pkgs.terraform-providers.cloudflare ]); in { devShells.default = pkgs.mkShell { diff --git a/targets/.terraform.lock.hcl b/targets/.terraform.lock.hcl new file mode 100644 index 0000000..28e5f0f --- /dev/null +++ b/targets/.terraform.lock.hcl @@ -0,0 +1,37 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/cloudflare/cloudflare" { + version = "4.51.0" + hashes = [ + "h1:lRBARGOEAeuBm5aC1P0bAAvs+F8+kSxV/UWiOWOIm44=", + ] +} + +provider "registry.opentofu.org/hashicorp/external" { + version = "2.3.4" + hashes = [ + "h1:i0CiDzSau8J/NcGlv6A3luRuYkqbnuO2c+XVrJ6YOoA=", + ] +} + +provider "registry.opentofu.org/hashicorp/local" { + version = "2.5.2" + hashes = [ + "h1:eWrRygqR0Pmcg61LyF+vADOO3oewcqeHasTJ6niHGNk=", + ] +} + +provider "registry.opentofu.org/hashicorp/null" { + version = "3.2.3" + hashes = [ + "h1:tIPswUCP63F9jN+FulrFOJfVriHAMtLUPEkalbwa+Ys=", + ] +} + +provider "registry.opentofu.org/hetznercloud/hcloud" { + version = "1.49.1" + hashes = [ + "h1:dyK3/rOb8IJOM0trh328NovbYb+Rz33qui2/fg85hU8=", + ] +} diff --git a/targets/admins/apply.sh b/targets/apply.sh similarity index 100% rename from targets/admins/apply.sh rename to targets/apply.sh diff --git a/targets/dns/.terraform.lock.hcl b/targets/dns/.terraform.lock.hcl new file mode 100644 index 0000000..28e5f0f --- /dev/null +++ b/targets/dns/.terraform.lock.hcl @@ -0,0 +1,37 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/cloudflare/cloudflare" { + version = "4.51.0" + hashes = [ + "h1:lRBARGOEAeuBm5aC1P0bAAvs+F8+kSxV/UWiOWOIm44=", + ] +} + +provider "registry.opentofu.org/hashicorp/external" { + version = "2.3.4" + hashes = [ + "h1:i0CiDzSau8J/NcGlv6A3luRuYkqbnuO2c+XVrJ6YOoA=", + ] +} + +provider "registry.opentofu.org/hashicorp/local" { + version = "2.5.2" + hashes = [ + "h1:eWrRygqR0Pmcg61LyF+vADOO3oewcqeHasTJ6niHGNk=", + ] +} + +provider "registry.opentofu.org/hashicorp/null" { + version = "3.2.3" + hashes = [ + "h1:tIPswUCP63F9jN+FulrFOJfVriHAMtLUPEkalbwa+Ys=", + ] +} + +provider "registry.opentofu.org/hetznercloud/hcloud" { + version = "1.49.1" + hashes = [ + "h1:dyK3/rOb8IJOM0trh328NovbYb+Rz33qui2/fg85hU8=", + ] +} diff --git a/targets/dns/terraform.tf b/targets/dns/terraform.tf new file mode 100644 index 0000000..5937ee2 --- /dev/null +++ b/targets/dns/terraform.tf @@ -0,0 +1,30 @@ +terraform { + required_providers { + cloudflare = { + source = "cloudflare/cloudflare" + } + } +} + +module "dns" { + source = "../../terraform/dns" +} + +variable "vpn_ipv4" { + type = string + description = "IPv4 address for VPN" +} + +variable "vpn_hostname" { + type = string + description = "Hostname for VPN" +} + +resource "cloudflare_record" "vpn" { + zone_id = module.dns.zone_id_netname + name = "${var.vpn_hostname}.${module.dns.domain_netname}" + value = var.vpn_ipv4 + type = "A" + ttl = 3600 + proxied = false +} diff --git a/targets/terraform.tf b/targets/terraform.tf new file mode 100644 index 0000000..09ff4f4 --- /dev/null +++ b/targets/terraform.tf @@ -0,0 +1,14 @@ +module "admins" { + source = "./admins" +} + +module "vpn" { + source = "./vpn" +} + +module "dns" { + source = "./dns" + vpn_ipv4 = module.vpn.ipv4_address + vpn_hostname = module.vpn.hostname +} + diff --git a/targets/vpn/apply.sh b/targets/vpn/apply.sh deleted file mode 100755 index 0072874..0000000 --- a/targets/vpn/apply.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cd "$(dirname "$0")" -rm -f .terraform.lock.hcl -tofu init -tofu apply "$@" diff --git a/targets/vpn/configuration.nix b/targets/vpn/configuration.nix index 9176fe9..6013a09 100644 --- a/targets/vpn/configuration.nix +++ b/targets/vpn/configuration.nix @@ -12,23 +12,73 @@ in imports = [ self.nixosModules.hcloud ]; + users.users.root = { openssh.authorizedKeys.keys = nixosVars.ssh_keys; initialPassword = "nixos"; }; + system.stateVersion = "23.11"; networking = { - hostName = "vpn"; - domain = "sudoer777.dev"; + hostName = nixosVars.hostname; + domain = nixosVars.domain_netname; + + firewall = { + allowedUDPPorts = [3478]; + allowedTCPPorts = [80 443]; + }; }; - services.openssh = { - enable = true; - settings.PasswordAuthentication = false; + services = { + openssh = { + enable = true; + settings.PasswordAuthentication = false; + }; + + cloud-init.enable = lib.mkForce false; + + headscale = { + enable = true; + address = "0.0.0.0"; + port = 8080; + settings = { + server_url = "https://${nixosVars.hostname}.${nixosVars.domain_netname}"; + logtail.enabled = false; + dns = { + base_domain = "ts.${nixosVars.domain_netname}"; + magic_dns = true; + search_domains = ["${nixosVars.domain_netname}"]; + nameservers.global = [ + "1.1.1.1" + "9.9.9.9" + ]; + }; + ip_prefixes = [ + "100.64.0.0/10" + ]; + }; + }; + + caddy = { + enable = true; + virtualHosts."${nixosVars.hostname}.${nixosVars.domain_netname}".extraConfig = '' + reverse_proxy * 127.0.0.1:8080 + ''; + }; }; - services.cloud-init.enable = lib.mkForce false; + systemd.network.networks."10-wan" = { + matchConfig.MACAddress = "96:00:04:16:ed:c5"; + address = ["${nixosVars.ipv4_address}/32"]; + routes = [ + { + Gateway = "172.31.1.1"; + GatewayOnLink = true; + } + ]; + linkConfig.RequiredForOnline = "routable"; + }; boot.supportedFilesystems = ["btrfs"]; environment.systemPackages = [ @@ -36,6 +86,9 @@ in pkgs.shadow pkgs.vim pkgs.speedtest-cli + pkgs.git + pkgs.hcloud + pkgs.dhcpcd ]; } diff --git a/targets/vpn/nixos-vars.json b/targets/vpn/nixos-vars.json index 77fb895..b05ccae 100644 --- a/targets/vpn/nixos-vars.json +++ b/targets/vpn/nixos-vars.json @@ -1 +1 @@ -{"ipv6_address":"2a01:4ff:1f0:ce35::1","ssh_keys":["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJWUVBj2uBVfXGjWwXmOTQmqP1oc2ZfDtylhTEox6JBm ssh@sudoer777.dev"]} \ No newline at end of file +{"domain_netname":"sudoer777.dev","domain_realname":"ethanreece.com","hostname":"vpn","ipv4_address":"5.78.133.184","ipv6_address":"2a01:4ff:1f0:ce35::1","ssh_keys":["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJWUVBj2uBVfXGjWwXmOTQmqP1oc2ZfDtylhTEox6JBm ssh@sudoer777.dev"]} \ No newline at end of file diff --git a/targets/vpn/terraform.tf b/targets/vpn/terraform.tf index 9914fb7..46fa16a 100644 --- a/targets/vpn/terraform.tf +++ b/targets/vpn/terraform.tf @@ -15,3 +15,7 @@ output "ipv4_address" { output "ipv6_address" { value = module.vpn.ipv6_address } + +output "hostname" { + value = module.vpn.hostname +} diff --git a/terraform/dns/main.tf b/terraform/dns/main.tf new file mode 100644 index 0000000..4ec6cf8 --- /dev/null +++ b/terraform/dns/main.tf @@ -0,0 +1,23 @@ +data "cloudflare_zone" "realname" { + name = var.domain_realname +} + +data "cloudflare_zone" "netname" { + name = var.domain_netname +} + +output "domain_realname" { + value = var.domain_realname +} + +output "domain_netname" { + value = var.domain_netname +} + +output "zone_id_realname" { + value = data.cloudflare_zone.realname.id +} + +output "zone_id_netname" { + value = data.cloudflare_zone.netname.id +} diff --git a/terraform/dns/providers.tf b/terraform/dns/providers.tf new file mode 100644 index 0000000..6d40668 --- /dev/null +++ b/terraform/dns/providers.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + cloudflare = { + source = "cloudflare/cloudflare" + } + } +} diff --git a/terraform/dns/variables.tf b/terraform/dns/variables.tf new file mode 100644 index 0000000..8116731 --- /dev/null +++ b/terraform/dns/variables.tf @@ -0,0 +1,11 @@ +variable "domain_realname" { + type = string + default = "ethanreece.com" + description = "Domain for real name" +} + +variable "domain_netname" { + type = string + default = "sudoer777.dev" + description = "Domain for internet name" +} diff --git a/terraform/nixos-vpn/main.tf b/terraform/nixos-vpn/main.tf index 87ceae1..29d732e 100644 --- a/terraform/nixos-vpn/main.tf +++ b/terraform/nixos-vpn/main.tf @@ -1,5 +1,8 @@ +module "dns" { + source = "../dns" +} + data "hcloud_ssh_keys" "nixos_vpn" { - //with_selector = "vpn=true" } resource "hcloud_server" "nixos_vpn" { @@ -29,8 +32,12 @@ module "deploy" { locals { nixos_vars = { - ipv6_address = hcloud_server.nixos_vpn.ipv6_address - ssh_keys = data.hcloud_ssh_keys.nixos_vpn.ssh_keys.*.public_key + hostname = var.hostname + domain_realname = module.dns.domain_realname + domain_netname = module.dns.domain_netname + ipv4_address = hcloud_server.nixos_vpn.ipv4_address + ipv6_address = hcloud_server.nixos_vpn.ipv6_address + ssh_keys = data.hcloud_ssh_keys.nixos_vpn.ssh_keys.*.public_key } } @@ -41,3 +48,15 @@ output "ipv4_address" { output "ipv6_address" { value = hcloud_server.nixos_vpn.ipv6_address } + +output "domain_realname" { + value = module.dns.domain_realname +} + +output "domain_netname" { + value = module.dns.domain_netname +} + +output "hostname" { + value = var.hostname +} diff --git a/terraform/nixos-vpn/variables.tf b/terraform/nixos-vpn/variables.tf index 785015e..e2fc775 100644 --- a/terraform/nixos-vpn/variables.tf +++ b/terraform/nixos-vpn/variables.tf @@ -10,6 +10,12 @@ variable "server_location" { description = "Hetzner cloud server location" } +variable "hostname" { + type = string + default = "vpn" + description = "Host name for server" +} + variable "nixos_vars_file" { type = string description = "File to write NixOS configuration variables to"