diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..84fc8e5 --- /dev/null +++ b/.envrc @@ -0,0 +1,7 @@ +# Automatically sets up your devbox environment whenever you cd into this +# directory via our direnv integration: + +eval "$(devbox generate direnv --print-envrc)" + +# check out https://www.jetpack.io/devbox/docs/ide_configuration/direnv/ +# for more details diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bdfc77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# ---> Terraform +# Local .terraform directories +**/.terraform/* + +# .tfstate files +**/*.tfstate +**/*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +#*.tfvars +#*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +# ENV +.env +admin.*config + +# Downloaded ISO +iso diff --git a/README.md b/README.md index 90e8ac1..b49daec 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ -# network +# ingress -Networking \ No newline at end of file +Handling all Ingress traffic + +## Setting up + +### 1. Add new device to terraform.tfvars. + +### 2. Manually configure vmbr0 and use import to import it. + +### 3. Deploy terraform and manually install OPNsense via console. + +### 4. Manually configure Tailscale. +Ref: https://tailscale.com/kb/1097/install-opnsense + +### 5. Done! diff --git a/devbox.json b/devbox.json new file mode 100644 index 0000000..87ae00a --- /dev/null +++ b/devbox.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.12.0/.schema/devbox.schema.json", + "packages": [ + "tflint@latest", + "tenv@latest" + ], + "env": { + "TENV_AUTO_INSTALL": "true" + }, + "shell": { + "init_hook": [ + "echo 'Welcome to devbox!' > /dev/null" + ], + "scripts": { + "test": [ + "echo \"Error: no test specified\" && exit 1" + ] + } + } +} diff --git a/devbox.lock b/devbox.lock new file mode 100644 index 0000000..fcf0f43 --- /dev/null +++ b/devbox.lock @@ -0,0 +1,101 @@ +{ + "lockfile_version": "1", + "packages": { + "tenv@latest": { + "last_modified": "2024-08-18T12:44:29Z", + "resolved": "github:NixOS/nixpkgs/ff1c2669bbb4d0dd9e62cc94f0968cfa652ceec1#tenv", + "source": "devbox-search", + "version": "3.0.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/0zbz338z744cmpp0z2rmzygab4b8pili-tenv-3.0.0", + "default": true + } + ], + "store_path": "/nix/store/0zbz338z744cmpp0z2rmzygab4b8pili-tenv-3.0.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/fiwf4p798kamjy6waaqn0nl6dvzrz6r0-tenv-3.0.0", + "default": true + } + ], + "store_path": "/nix/store/fiwf4p798kamjy6waaqn0nl6dvzrz6r0-tenv-3.0.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/g87m0hmaqlb9gcg2c8ln1lx9fyzqx1m0-tenv-3.0.0", + "default": true + } + ], + "store_path": "/nix/store/g87m0hmaqlb9gcg2c8ln1lx9fyzqx1m0-tenv-3.0.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/x22lfx74nns131xxmnhpnnn7gz6hfp1r-tenv-3.0.0", + "default": true + } + ], + "store_path": "/nix/store/x22lfx74nns131xxmnhpnnn7gz6hfp1r-tenv-3.0.0" + } + } + }, + "tflint@latest": { + "last_modified": "2024-08-18T12:44:29Z", + "resolved": "github:NixOS/nixpkgs/ff1c2669bbb4d0dd9e62cc94f0968cfa652ceec1#tflint", + "source": "devbox-search", + "version": "0.52.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2kqp4ba1anc4c1vxm31criv2cnw7bm8i-tflint-0.52.0", + "default": true + } + ], + "store_path": "/nix/store/2kqp4ba1anc4c1vxm31criv2cnw7bm8i-tflint-0.52.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/zq3wrj237i9v6jvjfiiglhqrxnn5yfbl-tflint-0.52.0", + "default": true + } + ], + "store_path": "/nix/store/zq3wrj237i9v6jvjfiiglhqrxnn5yfbl-tflint-0.52.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/57xd6fdjgkhyi8p69bik9bhjz3nxz4gk-tflint-0.52.0", + "default": true + } + ], + "store_path": "/nix/store/57xd6fdjgkhyi8p69bik9bhjz3nxz4gk-tflint-0.52.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ldy3j1qq62nq1fm9q3rpmnd0glpzvfma-tflint-0.52.0", + "default": true + } + ], + "store_path": "/nix/store/ldy3j1qq62nq1fm9q3rpmnd0glpzvfma-tflint-0.52.0" + } + } + } + } +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..88b5efe --- /dev/null +++ b/justfile @@ -0,0 +1,22 @@ +# Always use devbox environment to run commands. +set shell := ["devbox", "run"] +# Load dotenv +set dotenv-load + +default: + @just --list + +lint: + @tofu fmt -check -recursive . + @tflint --recursive + +deploy: + #!/usr/bin/env sh + cd {{justfile_directory()}}/terraform + tofu init + tofu apply + +destroy: + #!/usr/bin/env sh + cd {{justfile_directory()}}/terraform + tofu destroy diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..55f9b40 --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/bpg/proxmox" { + version = "0.61.1" + constraints = "0.61.1" + hashes = [ + "h1:6kz2Rdjc8+TVq2aUxEQXLOwbb9OdhJJei0L1fC4K2R4=", + "zh:27d8b589a2dc1e0a5b0f8ab299b9f3704a2f0b69799d1d4d8845c68056986d1f", + "zh:46dfa6b33ddd7007a2144f38090457604eb56a59a303b37bb0ad1be5c84ddaca", + "zh:47a1b14a759393c5ecc76f2feb950677c418c910b8c677fde0dd3e4675c41579", + "zh:582e49d109d1c2b1f3b1268a7cbc43548f3c6d96a87c92a5428767097a5e383e", + "zh:5e98ad6afae5969a4c3ffb14c0484936550c66c8313d7686551c29b633ff32f2", + "zh:7b9e24b76f947ab8f1e571cf61beefc983b7d2aa1b85df35c4f015728fe37a38", + "zh:8255ca210f279a0f7b8ca2762df26d2ea1a01704298c5e3d5cf601bd39a743f0", + "zh:85d7655fdc95dedced9cf8105a0beeb0d7bc8f668c55f62019a7215a76d60300", + "zh:8aeea5a1d001b06baaf923b754e1a14d06c75eb8c8b87a7f65a3c8205fc8b079", + "zh:a9cfab6c06f613658c5fdd83742cd22c0eb7563778924b1407965ef8c36c1ce0", + "zh:ceaab67801d49a92eb5858b1ddae6df2569462e5ffbe31f9dbd79dcb684ea142", + "zh:dc25b506d5c55d1d78a335d3ebd03213c99b4b2a5859812349a955c2f746ff7e", + "zh:e04b477fd77a0d37a0bdb76a7cf69184dad9e7fbba9b4f3a378a8901b82b75e5", + "zh:f1e6838d9141557f73340df9b21fce5a82b41cc16ae36f063a920ccc36bc0758", + "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", + ] +} diff --git a/terraform/node.tf b/terraform/node.tf new file mode 100644 index 0000000..68c7b4c --- /dev/null +++ b/terraform/node.tf @@ -0,0 +1,152 @@ +locals { + domain = "network.tjo.cloud" + + nodes = { + for k, v in var.nodes : k => merge(v, { + domain = local.domain + id = 700 + index(keys(var.nodes), k) + hash = sha1(v.host) + + wan_mac_address = v.mac_address != null ? v.mac_address : "AA:BB:00:00:${format("%v:%v", substr(sha1(v.host), 0, 2), substr(sha1(v.host), 2, 2))}" + private_mac_address = "AA:BB:00:11:${format("%v:%v", substr(sha1(v.host), 0, 2), substr(sha1(v.host), 2, 2))}" + internal_mac_address = "AA:BB:00:22:${format("%v:%v", substr(sha1(v.host), 0, 2), substr(sha1(v.host), 2, 2))}" + }) + } +} + +resource "proxmox_virtual_environment_network_linux_bridge" "vmbr0" { + for_each = local.nodes + + node_name = each.value.host + name = "vmbr0" + comment = "Main interface bridge for VMs." + + address = each.value.address + gateway = each.value.gateway + ports = each.value.bridge_ports +} + +import { + id = "jakku:vmbr0" + to = proxmox_virtual_environment_network_linux_bridge.vmbr0["jakku"] +} + +import { + id = "batuu:vmbr0" + to = proxmox_virtual_environment_network_linux_bridge.vmbr0["batuu"] +} + +import { + id = "nevaroo:vmbr0" + to = proxmox_virtual_environment_network_linux_bridge.vmbr0["nevaroo"] +} + +moved { + from = proxmox_virtual_environment_network_linux_bridge.vmprivate + to = proxmox_virtual_environment_network_linux_bridge.vmbr1 +} + +moved { + from = proxmox_virtual_environment_network_linux_bridge.vminternal + to = proxmox_virtual_environment_network_linux_bridge.vmbr2 +} + +resource "proxmox_virtual_environment_network_linux_bridge" "vmbr1" { + for_each = local.nodes + + node_name = each.value.host + name = "vmbr1" + comment = "Private network for VMs." +} + +resource "proxmox_virtual_environment_network_linux_bridge" "vmbr2" { + for_each = local.nodes + + node_name = each.value.host + name = "vmbr2" + comment = "Internal network for VMs." +} + +resource "proxmox_virtual_environment_file" "iso" { + for_each = local.nodes + + content_type = "iso" + datastore_id = each.value.iso_storage + node_name = each.value.host + + source_file { + path = "${path.module}/../iso/OPNsense-24.7-dvd-amd64.iso" + } +} + +resource "proxmox_virtual_environment_vm" "nodes" { + for_each = local.nodes + + vm_id = each.value.id + name = "${each.value.host}.${each.value.domain}" + node_name = each.value.host + + description = "OPNsense instance for ${each.value.host}." + tags = [each.value.domain] + + stop_on_destroy = true + timeout_start_vm = 60 + timeout_stop_vm = 60 + timeout_shutdown_vm = 60 + timeout_reboot = 60 + timeout_create = 600 + + cpu { + cores = each.value.cores + type = "host" + } + memory { + dedicated = each.value.memory + } + + bios = "ovmf" + efi_disk { + datastore_id = each.value.boot_storage + } + + operating_system { + type = "l26" + } + + agent { + enabled = false + } + + network_device { + bridge = "vmbr0" + mac_address = each.value.wan_mac_address + } + + network_device { + bridge = proxmox_virtual_environment_network_linux_bridge.vmbr1[each.key].name + mac_address = each.value.private_mac_address + } + + network_device { + bridge = proxmox_virtual_environment_network_linux_bridge.vmbr2[each.key].name + mac_address = each.value.internal_mac_address + } + + scsi_hardware = "virtio-scsi-single" + + cdrom { + enabled = each.value.iso_enabled + file_id = proxmox_virtual_environment_file.iso[each.key].id + interface = "ide0" + } + + disk { + interface = "scsi0" + datastore_id = each.value.boot_storage + size = 16 + backup = true + cache = "none" + iothread = true + file_format = "raw" + } +} diff --git a/terraform/terraform.tf b/terraform/terraform.tf new file mode 100644 index 0000000..6275489 --- /dev/null +++ b/terraform/terraform.tf @@ -0,0 +1,40 @@ +terraform { + required_providers { + proxmox = { + source = "bpg/proxmox" + version = "0.61.1" + } + } + + required_version = "~> 1.7.3" +} + +provider "proxmox" { + # FIXME: Traefik/NGINX breaks this! 500 ERROR + endpoint = "https://batuu.system.tjo.cloud:8006/api2/json" + insecure = true + api_token = var.proxmox_token + + ssh { + agent = true + username = "root" + + node { + name = "batuu" + address = "batuu.system.tjo.cloud" + port = 22 + } + + node { + name = "jakku" + address = "jakku.system.tjo.cloud" + port = 22 + } + + node { + name = "nevaroo" + address = "nevaroo.system.tjo.cloud" + port = 22 + } + } +} diff --git a/terraform/terraform.tfvars b/terraform/terraform.tfvars new file mode 100644 index 0000000..bd1ab56 --- /dev/null +++ b/terraform/terraform.tfvars @@ -0,0 +1,36 @@ +nodes = { + batuu = { + host = "batuu" + boot_storage = "local-nvme" + iso_storage = "local" + + bridge_ports = ["enp1s0", "enp2s0"] + gateway = "192.168.1.1" + address = "192.168.1.161/24" + + iso_enabled = false + } + jakku = { + host = "jakku" + boot_storage = "local-nvme" + iso_storage = "local" + + bridge_ports = ["enp1s0", "enp2s0"] + gateway = "192.168.1.1" + address = "192.168.1.187/24" + + iso_enabled = false + } + nevaroo = { + host = "nevaroo" + boot_storage = "local" + iso_storage = "local" + + mac_address = "00:50:56:00:97:FD" + bridge_ports = ["eno1"] + gateway = "178.63.49.193" + address = "178.63.49.225/26" + + iso_enabled = false + } +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..550dc92 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,23 @@ +variable "nodes" { + type = map(object({ + host = string + + cores = optional(number, 1) + memory = optional(number, 512) + + mac_address = optional(string) + bridge_ports = list(string) + gateway = string + address = string + + iso_storage = string + boot_storage = string + + iso_enabled = bool + })) +} + +variable "proxmox_token" { + type = string + sensitive = true +}