Add headscale
This commit is contained in:
		
							parent
							
								
									3ff323ccca
								
							
						
					
					
						commit
						785b3c2b56
					
				
					 18 changed files with 271 additions and 21 deletions
				
			
		|  | @ -1 +1,2 @@ | |||
| HCLOUD_TOKEN="your_token_here" | ||||
| CLOUDFLARE_API_TOKEN="your_token_here" | ||||
|  |  | |||
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -4,5 +4,4 @@ | |||
| *.tfstate | ||||
| *.tfstate.backup | ||||
| *.tfstate.*.backup | ||||
| .terraform.lock.hcl | ||||
| result | ||||
|  |  | |||
							
								
								
									
										21
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								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 <HEADSCALE_URL> --auth-key <KEY> | ||||
| ``` | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
							
								
								
									
										37
									
								
								targets/.terraform.lock.hcl
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								targets/.terraform.lock.hcl
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -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=", | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										37
									
								
								targets/dns/.terraform.lock.hcl
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								targets/dns/.terraform.lock.hcl
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -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=", | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										30
									
								
								targets/dns/terraform.tf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								targets/dns/terraform.tf
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										14
									
								
								targets/terraform.tf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								targets/terraform.tf
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
| 
 | ||||
|  | @ -1,7 +0,0 @@ | |||
| #!/usr/bin/env bash | ||||
| set -euo pipefail | ||||
| 
 | ||||
| cd "$(dirname "$0")" | ||||
| rm -f .terraform.lock.hcl | ||||
| tofu init | ||||
| tofu apply "$@" | ||||
|  | @ -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 | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| {"ipv6_address":"2a01:4ff:1f0:ce35::1","ssh_keys":["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJWUVBj2uBVfXGjWwXmOTQmqP1oc2ZfDtylhTEox6JBm ssh@sudoer777.dev"]} | ||||
| {"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"]} | ||||
|  | @ -15,3 +15,7 @@ output "ipv4_address" { | |||
| output "ipv6_address" { | ||||
|   value = module.vpn.ipv6_address | ||||
| } | ||||
| 
 | ||||
| output "hostname" { | ||||
|   value = module.vpn.hostname | ||||
| } | ||||
|  |  | |||
							
								
								
									
										23
									
								
								terraform/dns/main.tf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								terraform/dns/main.tf
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										7
									
								
								terraform/dns/providers.tf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								terraform/dns/providers.tf
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| terraform { | ||||
|   required_providers { | ||||
|     cloudflare = { | ||||
|       source = "cloudflare/cloudflare" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										11
									
								
								terraform/dns/variables.tf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								terraform/dns/variables.tf
									
										
									
									
									
										Normal file
									
								
							|  | @ -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" | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue