diff --git a/.forgejo/workflows/tofu.yml b/.forgejo/workflows/tofu.yml new file mode 100644 index 0000000..157dee6 --- /dev/null +++ b/.forgejo/workflows/tofu.yml @@ -0,0 +1,45 @@ +on: + push: + workflow_dispatch: + +jobs: + apply: + name: OpenTofu + # Ensure 'nixos-latest' runner has Docker, SSH client, and basic Nix tools installed. + # It seems it already does. + runs-on: nixos-latest + env: + PROXMOX_API_URL: https://192.168.1.205:8006/api2/json + # PROXMOX_API_TOKEN_ID: nixos-builder.lab + # PROXMOX_API_TOKEN_SECRET: nixos-builder.lab + + steps: + - name: Install dependencies + run: | + nix-env -iA nixpkgs.nodejs + nix-env -iA nixpkgs.openssh + nix-env -iA nixpkgs.opentofu + + - uses: actions/checkout@v3 + + - name: OpenTofu fmt + id: fmt + run: tofu fmt -check + working-directory: ${{ forge.workspace }}/infrastructure/proxmox + continue-on-error: true + + - name: OpenTofu Init + id: init + run: tofu init + working-directory: ${{ forge.workspace }}/infrastructure/proxmox + + - name: OpenTofu Validate + id: validate + run: tofu validate -no-color + working-directory: ${{ forge.workspace }}/infrastructure/proxmox + + - name: OpenTofu Plan + id: plan + run: tofu plan -no-color + working-directory: ${{ forge.workspace }}/infrastructure/proxmox + continue-on-error: true diff --git a/flake.lock b/flake.lock index 3d6d35c..9c2fdff 100644 --- a/flake.lock +++ b/flake.lock @@ -186,13 +186,29 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs-unstable": { "locked": { - "lastModified": 1752624097, - "narHash": "sha256-mQCof2VccFzF7cmXy43n3GCwSN2+m8TVhZpGLx9sxVc=", + "lastModified": 1752950548, + "narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d7c8095791ce3aafe97d9c16c1dc2f4e3d69a3ba", + "rev": "c87b95e25065c028d31a94f06a62927d18763fdf", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1753163915, + "narHash": "sha256-StbB6eUnHr8ExKwyq77c69csIKzVUNg1hLvTRAy7lxk=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ffc7ca9659d74d9d553e8a120c78170c7ad73525", "type": "github" }, "original": { @@ -205,6 +221,7 @@ "inputs": { "colmena": "colmena", "nixpkgs": "nixpkgs_2", + "nixpkgs-unstable": "nixpkgs-unstable", "simple-nixos-mailserver": "simple-nixos-mailserver", "sops-nix": "sops-nix" } diff --git a/flake.nix b/flake.nix index 9f15ca5..8918622 100644 --- a/flake.nix +++ b/flake.nix @@ -2,17 +2,18 @@ description = "Declarative NixOS HomeLab"; inputs = { + # Nixpkgs nixpkgs.url = "github:nixos/nixpkgs"; + # You can access packages and modules from different nixpkgs revs + # at the same time. Here's an working example: + nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; + # Also see the 'unstable-packages' overlay at 'overlays/default.nix'. # systems.url = "github:nix-systems/default"; - sops-nix = { - url = "github:Mic92/sops-nix"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + sops-nix.url = "github:Mic92/sops-nix"; + sops-nix.inputs.nixpkgs.follows = "nixpkgs"; - simple-nixos-mailserver = { - url = "gitlab:simple-nixos-mailserver/nixos-mailserver"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + simple-nixos-mailserver.url = "gitlab:simple-nixos-mailserver/nixos-mailserver"; + simple-nixos-mailserver.inputs.nixpkgs.follows = "nixpkgs"; # home-manager = { # url = "home-manager"; # inputs.nixpkgs.follows = "nixpkgs"; @@ -30,11 +31,56 @@ simple-nixos-mailserver, ... } @ inputs: let - overlays = [ - colmena.overlays.default + inherit (self) outputs; + # Supported systems for your flake packages, shell, etc. + systems = [ + "x86_64-linux" ]; + # This is a function that generates an attribute by calling a function you + # pass to it, with each system as an argument + forAllSystems = nixpkgs.lib.genAttrs systems; in { + # Custom packages + # Accessible through 'nix build', 'nix shell', etc + packages = forAllSystems (system: import ./pkgs nixpkgs.legacyPackages.${system}); + + # Formatter for your nix files, available through 'nix fmt' + # Other options beside 'alejandra' include 'nixpkgs-fmt' + formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.alejandra); + + # Custom packages and modifications, exported as overlays + overlays = import ./overlays {inherit inputs;}; + + # Reusable nixos modules + nixosModules = import ./modules/nixos; + colmenaHive = colmena.lib.makeHive self.outputs.colmena; - colmena = (import ./hive.nix) (inputs // {inherit overlays;}); + colmena = { + meta = { + nixpkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ + outputs.overlays.additions + outputs.overlays.modifications + outputs.overlays.unstable-packages + + colmena.overlays.default + ]; + config.allowUnfree = true; + }; + + specialArgs = { + inherit inputs outputs; + }; + }; + + defaults = import ./machines/_default/configuration.nix; + + sandbox = import ./machines/sandbox/configuration.nix; + auth = import ./machines/auth/configuration.nix; + mail = import ./machines/mail/configuration.nix; + monitor = import ./machines/monitor/configuration.nix; + photos = import ./machines/photos/configuration.nix; + }; }; } diff --git a/hive.nix b/hive.nix index 05f8896..e0fc390 100644 --- a/hive.nix +++ b/hive.nix @@ -4,36 +4,9 @@ inputs @ { sops-nix, simple-nixos-mailserver, # home-manager, - overlays, + outputs, ... }: { - meta = { - nixpkgs = import nixpkgs { - system = "x86_64-linux"; - }; - specialArgs.flakeInputs = inputs; - }; - - defaults = { - lib, - name, - config, - ... - }: { - imports = [ - ./machines/_default - ./machines/modules - sops-nix.nixosModules.sops - # home-manager.nixosModules.home-manager - ]; - nixpkgs = { - inherit overlays; - system = lib.mkDefault "x86_64-linux"; - config.allowUnfree = true; - }; - deployment.tags = [config.nixpkgs.system name]; - }; - sandbox = {name, ...}: { imports = [./machines/${name}/definition.nix]; deployment.tags = ["sandbox"]; @@ -56,4 +29,9 @@ inputs @ { ]; deployment.tags = ["mail"]; }; + + photos = {name, ...}: { + imports = [./machines/${name}/definition.nix]; + deployment.tags = ["ente"]; + }; } diff --git a/infrastructure/nixos-cloud-init/README.md b/infrastructure/nixos-cloud-init/README.md new file mode 100644 index 0000000..f2b880b --- /dev/null +++ b/infrastructure/nixos-cloud-init/README.md @@ -0,0 +1,18 @@ +# NixOS Cloud Init image + +## Create image zma.zst +``` +nix run github:nix-community/nixos-generators -- -f proxmox -c configuration.nix +``` + +## Update to proxmox +``` +scp /nix/store/jvwxp7agny9979fglf76s0ca9m2h6950-proxmox-nixos-cloud-init/vzdump-qemu-nixos-cloud-init.vma.zst root@192.168.1.206:/var/lib/vz/dump +``` + +## Init template +``` +qmrestore /var/lib/vz/dump/vzdump-qemu-nixos-cloud-init.vma.zst 9000 --unique true + +qm template 9000 +``` diff --git a/infrastructure/nixos-cloud-init/configuration.nix b/infrastructure/nixos-cloud-init/configuration.nix new file mode 100644 index 0000000..dfc0c50 --- /dev/null +++ b/infrastructure/nixos-cloud-init/configuration.nix @@ -0,0 +1,9 @@ +{ + proxmox = { + filenameSuffix = "nixos-cloud-init"; + + cloudInit = { + enable = true; + }; + }; +} diff --git a/infrastructure/proxmox/.terraform.lock.hcl b/infrastructure/proxmox/.terraform.lock.hcl new file mode 100644 index 0000000..978a610 --- /dev/null +++ b/infrastructure/proxmox/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/telmate/proxmox" { + version = "3.0.2-rc01" + constraints = "3.0.2-rc01" + hashes = [ + "h1:571ROPuTMC0w5lr9hbUXi7NVLsG3SpmZxXXZx8cAT+Q=", + "zh:34d264243a4513f4e30c01fb37cc6a3e592d7823dfd182c5edfb170ac7b7de3a", + "zh:544428311ad20fbb3ad2cd854e893bbf036023cb57c3acc5093d141976dac670", + "zh:5c2396b328edee8de7ac144c15a6b7e668e81063699bc8c110d7c39fb8da70e9", + "zh:5ca8e33476ad06a0259071120a59477e8f107f30c1178ea7b9f6cafe1a461ade", + "zh:5ea56eb8275edc754a01a0180750e9c939cd997d3a50659617770211f4337da9", + "zh:9dd3482df6bbe00a4a6152be3567b6c08d35c3644a327a1f5ac30fd95ccd449f", + "zh:a76075fafadcc94a825151aff169bae4e0c05e3c7717e16dcdcf16ffa61a0780", + "zh:b1d95f97b22f671db762f7adf428b409e6736c078bcf267d8391985b8847d6e3", + "zh:cc94255cd1b18e6a341c15089015c457c8c639c25c426b07f278d5ea9850b3b5", + "zh:ce991103cb69b0b3e275127e3ab92c88bb3b6b0f4e5a2cb082aeaef70a7f7d61", + "zh:d24838bce87b38e12544a1329f5ad30e2be045968e639a3f4ddd5c84aa648e04", + "zh:e106ebd4eea8d62d62e62f261a262febc615e17466b54ac18f7e65c7e79e0008", + "zh:e254ca76c95e6e92da973b7bddc36bfa0a1e31d7c7e758ef4b01315db969388b", + "zh:f1d1d5f4c39267cacebe0ab7e9e06caf9692707f3b5369685541b65bc8b840ce", + ] +} diff --git a/infrastructure/proxmox/.terraform/modules/modules.json b/infrastructure/proxmox/.terraform/modules/modules.json new file mode 100644 index 0000000..a525777 --- /dev/null +++ b/infrastructure/proxmox/.terraform/modules/modules.json @@ -0,0 +1 @@ +{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"nixos_vm","Source":"./modules/nixos-vm","Dir":"modules/nixos-vm"},{"Key":"sandbox_vm","Source":"./modules/nixos-vm","Dir":"modules/nixos-vm"}]} \ No newline at end of file diff --git a/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/LICENSE b/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/LICENSE new file mode 100644 index 0000000..57395f1 --- /dev/null +++ b/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/README.md b/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/README.md new file mode 100644 index 0000000..eb7c72c --- /dev/null +++ b/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/README.md @@ -0,0 +1,51 @@ +[![Build Status](https://travis-ci.com/Telmate/terraform-provider-proxmox.svg?branch=master)](https://travis-ci.com/Telmate/terraform-provider-proxmox) + +# Terraform provider plugin for Proxmox + +This repository provides a Terraform provider for +the [Proxmox virtualization platform](https://pve.proxmox.com/pve-docs/) and exposes Terraform resources to provision +QEMU VMs and LXC Containers. + +## Getting Started + +In order to get started, use [the documentation included in this repository](docs/index.md). The documentation contains +a list of the options for the provider. Moreover, there are some guides available how to combine options and start +specific VMs. + +## Quick Start + +Follow this [install guide](docs/guides/installation.md) to install the plugin. + +## Known Limitations + +* `proxmox_vm_qemu`.`disk`.`size` attribute does not match what is displayed in the Proxmox UI. +* Updates to `proxmox_vm_qemu` resources almost always result as a failed task within the Proxmox UI. This appears to be + harmless and the desired configuration changes do get applied. +* When using the `proxmox_lxc` resource, the provider will crash unless `rootfs` is defined. +* When using the Network Boot mode (PXE), a valid NIC must be defined for the VM, and the boot order must specify network first. + +## Contributing + +When contributing, please also add documentation to help other users. + +### Debugging the provider + +Debugging is available for this provider through the Terraform Plugin SDK versions 2.0.0. Therefore, the plugin can be +started with the debugging flag `--debug`. + +For example (using [delve](https://github.com/go-delve/delve) as Debugger): + +```bash +dlv exec --headless ./terraform-provider-my-provider -- --debug +``` + +For more information about debugging a provider please +see: [Debugger-Based Debugging](https://www.terraform.io/docs/extend/debugging.html#debugger-based-debugging) + +## Useful links + +* [Proxmox](https://www.proxmox.com/en/) +* [Proxmox documentation](https://pve.proxmox.com/pve-docs/) +* [Terraform](https://www.terraform.io/) +* [Terraform documentation](https://www.terraform.io/docs/index.html) +* [Recommended ISO builder](https://github.com/Telmate/terraform-ubuntu-proxmox-iso) diff --git a/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/terraform-provider-proxmox_v3.0.2-rc01 b/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/terraform-provider-proxmox_v3.0.2-rc01 new file mode 100755 index 0000000..cd85f95 Binary files /dev/null and b/infrastructure/proxmox/.terraform/providers/registry.opentofu.org/telmate/proxmox/3.0.2-rc01/linux_amd64/terraform-provider-proxmox_v3.0.2-rc01 differ diff --git a/infrastructure/proxmox/main.tf b/infrastructure/proxmox/main.tf new file mode 100644 index 0000000..ca08aca --- /dev/null +++ b/infrastructure/proxmox/main.tf @@ -0,0 +1,13 @@ +module "sandbox_vm" { + source = "./modules/nixos-vm" + + vmid = 123 + name = "sandbox" + target_node = var.pm_node + sshkeys = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air" + cipassword = "$6$rounds=4096$h9zcOYHvB.sy0Ff/$M4cbXjzqmJZ7xRTl3ILWXrg9PePqNzpv.L7MnvMrhcGieK3hrPniU5YEY2Z5/NC1n4QM7VLRSwyP9g9zdjp67/" + # You can override any default variable here: + # cpu_cores = 4 + # memory = 2048 + # disk_size = "10G" +} diff --git a/infrastructure/proxmox/modules/nixos-vm/main.tf b/infrastructure/proxmox/modules/nixos-vm/main.tf new file mode 100644 index 0000000..d788442 --- /dev/null +++ b/infrastructure/proxmox/modules/nixos-vm/main.tf @@ -0,0 +1,66 @@ +terraform { + required_providers { + proxmox = { + source = "Telmate/proxmox" + version = "3.0.2-rc01" + } + } +} + +resource "proxmox_vm_qemu" "nixos-vm" { + vmid = var.vmid + name = var.name + target_node = var.target_node + agent = var.agent + cpu { + cores = var.cpu_cores + } + memory = var.memory + boot = var.boot # " " #"order=scsi0" # has to be the same as the OS disk of the template + clone_id = var.clone_id + full_clone = var.full_clone + scsihw = var.scsihw + vm_state = var.vm_state + automatic_reboot = var.automatic_reboot + + # Cloud-Init configuration + # cicustom = "vendor=local:snippets/qemu-guest-agent.yml" # /var/lib/vz/snippets/qemu-guest-agent.yml + ciupgrade = var.ciupgrade + ipconfig0 = var.ipconfig0 + skip_ipv6 = var.skip_ipv6 + ciuser = var.ciuser + cipassword = var.cipassword # "Enter123!" + sshkeys = var.sshkeys # "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air" + + # Most cloud-init images require a serial device for their display + serial { + id = 0 + } + + disks { + virtio { + virtio0 { + # We have to specify the disk from our template, else Terraform will think it's not supposed to be there + disk { + storage = var.disk_storage + # The size of the disk should be at least as big as the disk in the template. If it's smaller, the disk will be recreated + size = var.disk_size + } + } + } + ide { + # Some images require a cloud-init disk on the IDE controller, others on the SCSI or SATA controller + ide1 { + cloudinit { + storage = var.cloudinit_storage + } + } + } + } + + network { + id = 0 + bridge = var.network_bridge + model = var.network_model + } +} diff --git a/infrastructure/proxmox/modules/nixos-vm/outputs.tf b/infrastructure/proxmox/modules/nixos-vm/outputs.tf new file mode 100644 index 0000000..8a79e61 --- /dev/null +++ b/infrastructure/proxmox/modules/nixos-vm/outputs.tf @@ -0,0 +1,9 @@ +output "id" { + description = "Instance VM ID" + value = proxmox_vm_qemu.nixos-vm.id +} + +output "public_ipv4" { + description = "Instance Public IPv4 Address" + value = proxmox_vm_qemu.nixos-vm.default_ipv4_address +} diff --git a/infrastructure/proxmox/modules/nixos-vm/variables.tf b/infrastructure/proxmox/modules/nixos-vm/variables.tf new file mode 100644 index 0000000..97ed3bf --- /dev/null +++ b/infrastructure/proxmox/modules/nixos-vm/variables.tf @@ -0,0 +1,133 @@ +variable "vmid" { + description = "The VM ID of the Proxmox VM." + type = number +} + +variable "name" { + description = "The name of the Proxmox VM." + type = string +} + +variable "target_node" { + description = "The Proxmox node to provision the VM on." + type = string +} + +variable "agent" { + description = "Enable QEMU Guest Agent (1 for enabled, 0 for disabled)." + type = number + default = 1 +} + +variable "cpu_cores" { + description = "Number of CPU cores for the VM." + type = number + default = 2 +} + +variable "memory" { + description = "Memory in MB for the VM." + type = number + default = 1024 +} + +variable "boot" { + description = "Boot order for the VM (e.g., 'order=scsi0')." + type = string + default = " " # Proxmox expects a space for default if not specified +} + +variable "clone_id" { + description = "The VM ID of the template to clone from." + type = number + default = 9000 +} + +variable "full_clone" { + description = "Whether to perform a full clone or linked clone." + type = bool + default = true +} + +variable "scsihw" { + description = "SCSI controller hardware type." + type = string + default = "virtio-scsi-single" +} + +variable "vm_state" { + description = "Desired state of the VM ('running', 'stopped', etc.)." + type = string + default = "running" +} + +variable "automatic_reboot" { + description = "Automatically reboot the VM on configuration changes." + type = bool + default = true +} + +variable "ciupgrade" { + description = "Upgrade Cloud-Init tools on first boot." + type = bool + default = true +} + +variable "ipconfig0" { + description = "Cloud-Init IP configuration for network interface 0." + type = string + default = "ip=dhcp" +} + +variable "skip_ipv6" { + description = "Skip IPv6 configuration for Cloud-Init." + type = bool + default = true +} + +variable "ciuser" { + description = "Cloud-Init user for the VM." + type = string + default = "root" +} + +variable "cipassword" { + description = "Cloud-Init password for the VM." + type = string + default = "Enter123!" +} + +variable "sshkeys" { + description = "Public SSH key(s) to be added to the VM." + type = string +} + +variable "disk_storage" { + description = "Storage for the primary OS disk." + type = string + default = "pv1" +} + +variable "disk_size" { + description = "Size of the primary OS disk (e.g., '5G')." + type = string + default = "5G" +} + +variable "cloudinit_storage" { + description = "Storage for the Cloud-Init disk." + type = string + default = "local-lvm" +} + +variable "network_bridge" { + description = "Bridge for the network interface." + type = string + default = "vmbr0" +} + +variable "network_model" { + description = "Model for the network interface." + type = string + default = "virtio" +} diff --git a/infrastructure/proxmox/providers.tf b/infrastructure/proxmox/providers.tf new file mode 100644 index 0000000..07ff31b --- /dev/null +++ b/infrastructure/proxmox/providers.tf @@ -0,0 +1,6 @@ +provider "proxmox" { + pm_tls_insecure = true + pm_api_url = var.pm_api_url + pm_api_token_id = var.pm_api_token_id + pm_api_token_secret = var.pm_api_token_secret +} diff --git a/infrastructure/proxmox/sandbox.tf b/infrastructure/proxmox/sandbox.tf new file mode 100644 index 0000000..899f911 --- /dev/null +++ b/infrastructure/proxmox/sandbox.tf @@ -0,0 +1,58 @@ +# resource "proxmox_vm_qemu" "sandbox" { +# vmid = 123 +# name = "sandbox" +# target_node = var.pm_node +# agent = 1 +# cpu { +# cores = 2 +# } +# memory = 1024 +# boot = " " #"order=scsi0" # has to be the same as the OS disk of the template +# clone_id = 9000 +# full_clone = true +# scsihw = "virtio-scsi-single" +# vm_state = "running" +# automatic_reboot = true + +# # Cloud-Init configuration +# # cicustom = "vendor=local:snippets/qemu-guest-agent.yml" # /var/lib/vz/snippets/qemu-guest-agent.yml +# ciupgrade = true +# ipconfig0 = "ip=dhcp" +# skip_ipv6 = true +# ciuser = "root" +# # ci pasword doesn't seem to work with nix +# # cipassword = "Enter123!" +# sshkeys = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air" + +# # Most cloud-init images require a serial device for their display +# serial { +# id = 0 +# } + +# disks { +# virtio { +# virtio0 { +# # We have to specify the disk from our template, else Terraform will think it's not supposed to be there +# disk { +# storage = "pv1" +# # The size of the disk should be at least as big as the disk in the template. If it's smaller, the disk will be recreated +# size = "5G" +# } +# } +# } +# ide { +# # Some images require a cloud-init disk on the IDE controller, others on the SCSI or SATA controller +# ide1 { +# cloudinit { +# storage = "local-lvm" +# } +# } +# } +# } + +# network { +# id = 0 +# bridge = "vmbr0" +# model = "virtio" +# } +# } diff --git a/infrastructure/proxmox/terraform.tfstate b/infrastructure/proxmox/terraform.tfstate new file mode 100644 index 0000000..9b4fec3 --- /dev/null +++ b/infrastructure/proxmox/terraform.tfstate @@ -0,0 +1 @@ +{"version":4,"terraform_version":"1.9.1","serial":16,"lineage":"c76b2921-285f-1904-f2ab-e6a410d16442","outputs":{},"resources":[{"module":"module.sandbox_vm","mode":"managed","type":"proxmox_vm_qemu","name":"nixos-vm","provider":"provider[\"registry.opentofu.org/telmate/proxmox\"]","instances":[{"schema_version":0,"attributes":{"additional_wait":5,"agent":1,"agent_timeout":90,"args":"","automatic_reboot":true,"balloon":0,"bios":"seabios","boot":" ","bootdisk":"","ci_wait":null,"cicustom":null,"cipassword":"$6$rounds=4096$h9zcOYHvB.sy0Ff/$M4cbXjzqmJZ7xRTl3ILWXrg9PePqNzpv.L7MnvMrhcGieK3hrPniU5YEY2Z5/NC1n4QM7VLRSwyP9g9zdjp67/","ciupgrade":true,"ciuser":"root","clone":null,"clone_id":9000,"clone_wait":10,"cores":0,"cpu":[{"affinity":"","cores":2,"flags":[],"limit":0,"numa":false,"sockets":1,"type":"host","units":0,"vcores":0}],"cpu_type":"","current_node":"proxmox-01","default_ipv4_address":"192.168.1.228","default_ipv6_address":"","define_connection_info":true,"desc":"Managed by Terraform.","disk":[],"disks":[{"ide":[{"ide0":[],"ide1":[{"cdrom":[],"cloudinit":[{"storage":"local-lvm"}],"disk":[],"ignore":false,"passthrough":[]}],"ide2":[],"ide3":[]}],"sata":[],"scsi":[],"virtio":[{"virtio0":[{"cdrom":[],"disk":[{"asyncio":"","backup":true,"cache":"","discard":false,"format":"raw","id":0,"iops_r_burst":0,"iops_r_burst_length":0,"iops_r_concurrent":0,"iops_wr_burst":0,"iops_wr_burst_length":0,"iops_wr_concurrent":0,"iothread":false,"linked_disk_id":-1,"mbps_r_burst":0,"mbps_r_concurrent":0,"mbps_wr_burst":0,"mbps_wr_concurrent":0,"readonly":false,"replicate":false,"serial":"","size":"5G","storage":"pv1","wwn":""}],"ignore":false,"passthrough":[]}],"virtio1":[],"virtio10":[],"virtio11":[],"virtio12":[],"virtio13":[],"virtio14":[],"virtio15":[],"virtio2":[],"virtio3":[],"virtio4":[],"virtio5":[],"virtio6":[],"virtio7":[],"virtio8":[],"virtio9":[]}]}],"efidisk":[],"force_create":false,"force_recreate_on_change_of":null,"full_clone":true,"hagroup":"","hastate":"","hostpci":[],"hotplug":"network,disk,usb","id":"proxmox-01/qemu/123","ipconfig0":"ip=dhcp","ipconfig1":null,"ipconfig10":null,"ipconfig11":null,"ipconfig12":null,"ipconfig13":null,"ipconfig14":null,"ipconfig15":null,"ipconfig2":null,"ipconfig3":null,"ipconfig4":null,"ipconfig5":null,"ipconfig6":null,"ipconfig7":null,"ipconfig8":null,"ipconfig9":null,"kvm":true,"linked_vmid":0,"machine":"","memory":1024,"name":"sandbox","nameserver":null,"network":[{"bridge":"vmbr0","firewall":false,"id":0,"link_down":false,"macaddr":"bc:24:11:46:6c:00","model":"virtio","mtu":0,"queues":0,"rate":0,"tag":0}],"numa":false,"onboot":false,"os_network_config":null,"os_type":null,"pci":[],"pcis":[],"pool":"","protection":false,"pxe":null,"qemu_os":"l26","reboot_required":false,"scsihw":"virtio-scsi-single","searchdomain":null,"serial":[{"id":0,"type":"socket"}],"skip_ipv4":false,"skip_ipv6":true,"smbios":[{"family":"","manufacturer":"","product":"","serial":"","sku":"","uuid":"5ae92cdd-a036-4602-af8c-358197f958d9","version":""}],"sockets":0,"ssh_forward_ip":null,"ssh_host":"192.168.1.228","ssh_port":"22","ssh_private_key":null,"ssh_user":null,"sshkeys":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air\n","startup":"","tablet":true,"tags":"","target_node":"proxmox-01","target_nodes":null,"timeouts":null,"tpm_state":[],"unused_disk":[],"usb":[],"usbs":[],"vcpus":0,"vga":[],"vm_state":"running","vmid":123},"sensitive_attributes":[[{"type":"get_attr","value":"ssh_private_key"}],[{"type":"get_attr","value":"cipassword"}]],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWZhdWx0IjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInJlYWQiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH19"}]}],"check_results":null} diff --git a/infrastructure/proxmox/terraform.tfstate.backup b/infrastructure/proxmox/terraform.tfstate.backup new file mode 100644 index 0000000..848b2ff --- /dev/null +++ b/infrastructure/proxmox/terraform.tfstate.backup @@ -0,0 +1 @@ +{"version":4,"terraform_version":"1.9.1","serial":15,"lineage":"c76b2921-285f-1904-f2ab-e6a410d16442","outputs":{},"resources":[{"module":"module.sandbox_vm","mode":"managed","type":"proxmox_vm_qemu","name":"nixos-vm","provider":"provider[\"registry.opentofu.org/telmate/proxmox\"]","instances":[{"schema_version":0,"attributes":{"additional_wait":5,"agent":1,"agent_timeout":90,"args":"","automatic_reboot":true,"balloon":0,"bios":"seabios","boot":" ","bootdisk":"","ci_wait":null,"cicustom":null,"cipassword":"","ciupgrade":true,"ciuser":"root","clone":null,"clone_id":9000,"clone_wait":10,"cores":0,"cpu":[{"affinity":"","cores":2,"flags":[],"limit":0,"numa":false,"sockets":1,"type":"host","units":0,"vcores":0}],"cpu_type":"","current_node":"proxmox-01","default_ipv4_address":"192.168.1.228","default_ipv6_address":"2a05:f6c7:2030:0:be24:11ff:fe46:6c00","define_connection_info":true,"desc":"Managed by Terraform.","disk":[],"disks":[{"ide":[{"ide0":[],"ide1":[{"cdrom":[],"cloudinit":[{"storage":"local-lvm"}],"disk":[],"ignore":false,"passthrough":[]}],"ide2":[],"ide3":[]}],"sata":[],"scsi":[],"virtio":[{"virtio0":[{"cdrom":[],"disk":[{"asyncio":"","backup":true,"cache":"","discard":false,"format":"raw","id":0,"iops_r_burst":0,"iops_r_burst_length":0,"iops_r_concurrent":0,"iops_wr_burst":0,"iops_wr_burst_length":0,"iops_wr_concurrent":0,"iothread":false,"linked_disk_id":-1,"mbps_r_burst":0,"mbps_r_concurrent":0,"mbps_wr_burst":0,"mbps_wr_concurrent":0,"readonly":false,"replicate":false,"serial":"","size":"5G","storage":"pv1","wwn":""}],"ignore":false,"passthrough":[]}],"virtio1":[],"virtio10":[],"virtio11":[],"virtio12":[],"virtio13":[],"virtio14":[],"virtio15":[],"virtio2":[],"virtio3":[],"virtio4":[],"virtio5":[],"virtio6":[],"virtio7":[],"virtio8":[],"virtio9":[]}]}],"efidisk":[],"force_create":false,"force_recreate_on_change_of":null,"full_clone":true,"hagroup":"","hastate":"","hostpci":[],"hotplug":"network,disk,usb","id":"proxmox-01/qemu/123","ipconfig0":"ip=dhcp","ipconfig1":null,"ipconfig10":null,"ipconfig11":null,"ipconfig12":null,"ipconfig13":null,"ipconfig14":null,"ipconfig15":null,"ipconfig2":null,"ipconfig3":null,"ipconfig4":null,"ipconfig5":null,"ipconfig6":null,"ipconfig7":null,"ipconfig8":null,"ipconfig9":null,"kvm":true,"linked_vmid":0,"machine":"","memory":1024,"name":"sandbox","nameserver":null,"network":[{"bridge":"vmbr0","firewall":false,"id":0,"link_down":false,"macaddr":"bc:24:11:46:6c:00","model":"virtio","mtu":0,"queues":0,"rate":0,"tag":0}],"numa":false,"onboot":false,"os_network_config":null,"os_type":null,"pci":[],"pcis":[],"pool":"","protection":false,"pxe":null,"qemu_os":"l26","reboot_required":false,"scsihw":"virtio-scsi-single","searchdomain":null,"serial":[{"id":0,"type":"socket"}],"skip_ipv4":false,"skip_ipv6":true,"smbios":[{"family":"","manufacturer":"","product":"","serial":"","sku":"","uuid":"5ae92cdd-a036-4602-af8c-358197f958d9","version":""}],"sockets":0,"ssh_forward_ip":null,"ssh_host":"192.168.1.228","ssh_port":"22","ssh_private_key":null,"ssh_user":null,"sshkeys":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air\n","startup":"","tablet":true,"tags":"","target_node":"proxmox-01","target_nodes":null,"timeouts":null,"tpm_state":[],"unused_disk":[],"usb":[],"usbs":[],"vcpus":0,"vga":[],"vm_state":"running","vmid":123},"sensitive_attributes":[[{"type":"get_attr","value":"ssh_private_key"}],[{"type":"get_attr","value":"cipassword"}]],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWZhdWx0IjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInJlYWQiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH19"}]}],"check_results":null} diff --git a/infrastructure/proxmox/terraform.tfvars b/infrastructure/proxmox/terraform.tfvars new file mode 100644 index 0000000..32006a4 --- /dev/null +++ b/infrastructure/proxmox/terraform.tfvars @@ -0,0 +1,3 @@ + +pm_node = "proxmox-01" +# nixos_template_id = 9100 diff --git a/infrastructure/proxmox/variables.tf b/infrastructure/proxmox/variables.tf new file mode 100644 index 0000000..daf8c39 --- /dev/null +++ b/infrastructure/proxmox/variables.tf @@ -0,0 +1,19 @@ +variable "pm_api_url" { + description = "The URL of the Proxmox API (e.g., https://192.168.1.10:8006/api2/json)" + type = string +} + +variable "pm_node" { + description = "The Proxmox node name where VMs will be deployed (e.g., 'pve')" + type = string +} + +variable "pm_api_token_id" { + description = "Proxmox API token ID" + type = string +} + +variable "pm_api_token_secret" { + description = "Proxmox API token secret" + type = string +} diff --git a/infrastructure/proxmox/versions.tf b/infrastructure/proxmox/versions.tf new file mode 100644 index 0000000..a2341f9 --- /dev/null +++ b/infrastructure/proxmox/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + proxmox = { + source = "Telmate/proxmox" + version = "3.0.2-rc01" + } + } +} diff --git a/machines/_default/default.nix b/machines/_default/configuration.nix similarity index 50% rename from machines/_default/default.nix rename to machines/_default/configuration.nix index c038ebf..3d38005 100644 --- a/machines/_default/default.nix +++ b/machines/_default/configuration.nix @@ -1,10 +1,15 @@ { - lib, name, + config, + lib, + inputs, ... }: { imports = [ ./common_config.nix + ../modules + inputs.sops-nix.nixosModules.sops + # inputs.home-manager.nixosModules.home-manager ]; networking.hostName = name; @@ -13,7 +18,7 @@ replaceUnknownProfiles = lib.mkDefault true; buildOnTarget = lib.mkDefault false; targetHost = lib.mkDefault "${name}.lab"; - tags = lib.mkDefault ["homelab"]; + tags = lib.mkDefault [config.nixpkgs.system name "homelab"]; }; sops = { @@ -21,20 +26,6 @@ defaultSopsFile = ../../secrets/secrets.yaml; }; - # home-manager = { - # useGlobalPkgs = true; - # useUserPackages = true; - # users.cottand = { - # imports = with flakeInputs.cottand.homeManagerModules; [cli]; - # home.stateVersion = "22.11"; - # }; - # users.root = { - # imports = with flakeInputs.cottand.homeManagerModules; [cli]; - # home.stateVersion = "22.11"; - # }; - # }; - - # consulNode.enable = lib.mkDefault true; nodeExporter.enable = lib.mkDefault true; journalLog.enable = lib.mkDefault true; } diff --git a/machines/auth/bootstrap/service-accounts.nix b/machines/auth/bootstrap/service-accounts.nix index 09a552e..dc37282 100644 --- a/machines/auth/bootstrap/service-accounts.nix +++ b/machines/auth/bootstrap/service-accounts.nix @@ -17,7 +17,7 @@ } { "id": "forgejo", - "email": "forgejo@procopius.dk", + "email": "git@procopius.dk", "password": "${config.sops.placeholder."service_accounts/forgejo/password"}", "displayName": "Forgejo", "groups": [ diff --git a/machines/auth/definition.nix b/machines/auth/configuration.nix similarity index 69% rename from machines/auth/definition.nix rename to machines/auth/configuration.nix index 133c5c1..9f51678 100644 --- a/machines/auth/definition.nix +++ b/machines/auth/configuration.nix @@ -6,5 +6,6 @@ ./redis.nix ]; + deployment.tags = ["authelia" "sso" "ldap" "lldap"]; system.stateVersion = "25.05"; } diff --git a/machines/mail/configuration.nix b/machines/mail/configuration.nix new file mode 100644 index 0000000..15700a3 --- /dev/null +++ b/machines/mail/configuration.nix @@ -0,0 +1,11 @@ +{inputs, ...}: { + imports = [ + ./mailserver.nix + ./networking.nix + inputs.simple-nixos-mailserver.nixosModule + ]; + + deployment.tags = ["mail"]; + + system.stateVersion = "25.05"; +} diff --git a/machines/mail/definition.nix b/machines/mail/networking.nix similarity index 65% rename from machines/mail/definition.nix rename to machines/mail/networking.nix index 97607b0..10eca15 100644 --- a/machines/mail/definition.nix +++ b/machines/mail/networking.nix @@ -1,8 +1,4 @@ { - imports = [ - ./mailserver.nix - ]; - networking = { interfaces.eth0.ipv4.addresses = [ { @@ -13,7 +9,4 @@ nameservers = ["192.168.1.53"]; defaultGateway = "192.168.1.1"; }; - deployment.targetHost = "192.168.1.25"; - - system.stateVersion = "25.05"; } diff --git a/machines/monitor/definition.nix b/machines/monitor/configuration.nix similarity index 80% rename from machines/monitor/definition.nix rename to machines/monitor/configuration.nix index 64dfb92..04496f2 100644 --- a/machines/monitor/definition.nix +++ b/machines/monitor/configuration.nix @@ -10,5 +10,7 @@ ./jellyfin-exporter.nix ]; + deployment.tags = ["grafana" "prometheus"]; + system.stateVersion = "25.05"; } diff --git a/machines/photos/configuration.nix b/machines/photos/configuration.nix new file mode 100644 index 0000000..db54baf --- /dev/null +++ b/machines/photos/configuration.nix @@ -0,0 +1,10 @@ +{outputs, ...}: { + imports = [ + outputs.nixosModules.ente + ./ente.nix + ]; + + deployment.tags = ["ente"]; + + system.stateVersion = "25.05"; +} diff --git a/machines/photos/ente.nix b/machines/photos/ente.nix new file mode 100644 index 0000000..abca1d9 --- /dev/null +++ b/machines/photos/ente.nix @@ -0,0 +1,25 @@ +{ + services.ente.api = { + enable = true; + enableLocalDB = true; + + domain = "ente-v2.procopius.dk"; + settings = { + # apps = { + # accounts = "https://accounts.procopius.dk"; + # cast = "https://cast.procopius.dk"; + # public-albums = "https://albums.procopius.dk"; + # }; + }; + }; + services.ente.web = { + enable = true; + domains = { + api = "ente-v2.procopius.dk"; + accounts = "accounts.procopius.dk"; + albums = "albums.procopius.dk"; + cast = "cast.procopius.dk"; + photos = "photos.procopius.dk"; + }; + }; +} diff --git a/machines/photos/minio.nix b/machines/photos/minio.nix new file mode 100644 index 0000000..c3d6ee2 --- /dev/null +++ b/machines/photos/minio.nix @@ -0,0 +1,6 @@ +{ + services.minio = { + enable = true; + rootCredentialsFile = "/etc/nixos/minio-root-credentials"; + }; +} diff --git a/machines/sandbox/definition.nix b/machines/sandbox/configuration.nix similarity index 52% rename from machines/sandbox/definition.nix rename to machines/sandbox/configuration.nix index 8033946..6c1ca72 100644 --- a/machines/sandbox/definition.nix +++ b/machines/sandbox/configuration.nix @@ -1,3 +1,5 @@ { + deployment.tags = ["sandbox"]; + system.stateVersion = "25.05"; } diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix new file mode 100644 index 0000000..a0250d5 --- /dev/null +++ b/modules/nixos/default.nix @@ -0,0 +1,3 @@ +{ + ente = import ./ente.nix; +} diff --git a/modules/nixos/ente.nix b/modules/nixos/ente.nix new file mode 100644 index 0000000..283e4ec --- /dev/null +++ b/modules/nixos/ente.nix @@ -0,0 +1,351 @@ +{ + config, + lib, + pkgs, + utils, + ... +}: let + inherit + (lib) + getExe + mkDefault + mkEnableOption + mkIf + mkMerge + mkOption + mkPackageOption + optional + types + ; + + cfgApi = config.services.ente.api; + cfgWeb = config.services.ente.web; + + webPackage = enteApp: + cfgWeb.package.override { + inherit enteApp; + enteMainUrl = "https://${cfgWeb.domains.photos}"; + extraBuildEnv = { + NEXT_PUBLIC_ENTE_ENDPOINT = "https://${cfgWeb.domains.api}"; + NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT = "https://${cfgWeb.domains.albums}"; + NEXT_TELEMETRY_DISABLED = "1"; + }; + }; + + defaultUser = "ente"; + defaultGroup = "ente"; + dataDir = "/var/lib/ente"; + + yamlFormat = pkgs.formats.yaml {}; +in { + options.services.ente = { + web = { + enable = mkEnableOption "Ente web frontend (Photos, Albums)"; + package = mkPackageOption pkgs "ente-web" {}; + + domains = { + api = mkOption { + type = types.str; + description = '' + The domain under which the api is served. This will NOT serve the api itself, + but is a required setting to host the frontends! This will automatically be set + for you if you enable both the api server and web frontends. + ''; + }; + + accounts = mkOption { + type = types.str; + description = "The domain under which the accounts frontend will be served."; + }; + + cast = mkOption { + type = types.str; + description = "The domain under which the cast frontend will be served."; + }; + + albums = mkOption { + type = types.str; + description = "The domain under which the albums frontend will be served."; + }; + + photos = mkOption { + type = types.str; + description = "The domain under which the photos frontend will be served."; + }; + }; + }; + + api = { + enable = mkEnableOption "Museum (API server for ente.io)"; + package = mkPackageOption pkgs "museum" {}; + nginx.enable = mkEnableOption "nginx proxy for the API server"; + + user = mkOption { + type = types.str; + default = defaultUser; + description = "User under which museum runs."; + }; + + group = mkOption { + type = types.str; + default = defaultGroup; + description = "Group under which museum runs."; + }; + + domain = mkOption { + type = types.str; + description = "The domain under which the api will be served."; + }; + + enableLocalDB = mkEnableOption "the automatic creation of a local postgres database for museum."; + + settings = mkOption { + description = '' + Museum yaml configuration. Refer to upstream [local.yaml](https://github.com/ente-io/ente/blob/main/server/configurations/local.yaml) for more information. + You can specify secret values in this configuration by setting `somevalue._secret = "/path/to/file"` instead of setting `somevalue` directly. + ''; + default = {}; + type = types.submodule { + freeformType = yamlFormat.type; + options = { + apps = { + public-albums = mkOption { + type = types.str; + default = "https://albums.ente.io"; + description = '' + If you're running a self hosted instance and wish to serve public links, + set this to the URL where your albums web app is running. + ''; + }; + + cast = mkOption { + type = types.str; + default = "https://cast.ente.io"; + description = '' + Set this to the URL where your cast page is running. + This is for browser and chromecast casting support. + ''; + }; + + accounts = mkOption { + type = types.str; + default = "https://accounts.ente.io"; + description = '' + Set this to the URL where your accounts page is running. + This is primarily for passkey support. + ''; + }; + }; + + db = { + host = mkOption { + type = types.str; + description = "The database host"; + }; + + port = mkOption { + type = types.port; + default = 5432; + description = "The database port"; + }; + + name = mkOption { + type = types.str; + description = "The database name"; + }; + + user = mkOption { + type = types.str; + description = "The database user"; + }; + }; + }; + }; + }; + }; + }; + + config = mkMerge [ + (mkIf cfgApi.enable { + services.postgresql = mkIf cfgApi.enableLocalDB { + enable = true; + ensureUsers = [ + { + name = "ente"; + ensureDBOwnership = true; + } + ]; + ensureDatabases = ["ente"]; + }; + + services.ente.web.domains.api = mkIf cfgWeb.enable cfgApi.domain; + services.ente.api.settings = { + log-file = mkDefault ""; + db = mkIf cfgApi.enableLocalDB { + host = "/run/postgresql"; + port = 5432; + name = "ente"; + user = "ente"; + }; + }; + + systemd.services.ente = { + description = "Ente.io Museum API Server"; + after = ["network.target"] ++ optional cfgApi.enableLocalDB "postgresql.service"; + requires = optional cfgApi.enableLocalDB "postgresql.service"; + wantedBy = ["multi-user.target"]; + + preStart = '' + # Generate config including secret values. YAML is a superset of JSON, so we can use this here. + ${utils.genJqSecretsReplacementSnippet cfgApi.settings "/run/ente/local.yaml"} + + # Setup paths + mkdir -p ${dataDir}/configurations + ln -sTf /run/ente/local.yaml ${dataDir}/configurations/local.yaml + ''; + + serviceConfig = { + ExecStart = getExe cfgApi.package; + Type = "simple"; + Restart = "on-failure"; + + AmbientCapablities = []; + CapabilityBoundingSet = []; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = false; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + UMask = "077"; + + BindReadOnlyPaths = [ + "${cfgApi.package}/share/museum/migrations:${dataDir}/migrations" + "${cfgApi.package}/share/museum/mail-templates:${dataDir}/mail-templates" + ]; + + User = cfgApi.user; + Group = cfgApi.group; + + SyslogIdentifier = "ente"; + StateDirectory = "ente"; + WorkingDirectory = dataDir; + RuntimeDirectory = "ente"; + }; + + # Environment MUST be called local, otherwise we cannot log to stdout + environment = { + ENVIRONMENT = "local"; + GIN_MODE = "release"; + }; + }; + + users = { + users = mkIf (cfgApi.user == defaultUser) { + ${defaultUser} = { + description = "ente.io museum service user"; + inherit (cfgApi) group; + isSystemUser = true; + home = dataDir; + }; + }; + groups = mkIf (cfgApi.group == defaultGroup) {${defaultGroup} = {};}; + }; + + services.nginx = mkIf cfgApi.nginx.enable { + enable = true; + upstreams.museum = { + servers."localhost:8080" = {}; + extraConfig = '' + zone museum 64k; + keepalive 20; + ''; + }; + + virtualHosts.${cfgApi.domain} = { + forceSSL = mkDefault false; + locations."/".proxyPass = "http://museum"; + extraConfig = '' + client_max_body_size 4M; + ''; + }; + }; + }) + (mkIf cfgWeb.enable { + services.ente.api.settings = mkIf cfgApi.enable { + apps = { + accounts = "https://${cfgWeb.domains.accounts}"; + cast = "https://${cfgWeb.domains.cast}"; + public-albums = "https://${cfgWeb.domains.albums}"; + }; + + webauthn = { + rpid = cfgWeb.domains.accounts; + rporigins = ["https://${cfgWeb.domains.accounts}"]; + }; + }; + + services.nginx = let + domainFor = app: cfgWeb.domains.${app}; + in { + enable = true; + virtualHosts.${domainFor "accounts"} = { + forceSSL = mkDefault false; + locations."/" = { + root = webPackage "accounts"; + tryFiles = "$uri $uri.html /index.html"; + extraConfig = '' + add_header Access-Control-Allow-Origin 'https://${cfgWeb.domains.api}'; + ''; + }; + }; + virtualHosts.${domainFor "cast"} = { + forceSSL = mkDefault false; + locations."/" = { + root = webPackage "cast"; + tryFiles = "$uri $uri.html /index.html"; + extraConfig = '' + add_header Access-Control-Allow-Origin 'https://${cfgWeb.domains.api}'; + ''; + }; + }; + virtualHosts.${domainFor "photos"} = { + serverAliases = [ + (domainFor "albums") # the albums app is shared with the photos frontend + ]; + forceSSL = mkDefault false; + locations."/" = { + root = webPackage "photos"; + tryFiles = "$uri $uri.html /index.html"; + extraConfig = '' + add_header Access-Control-Allow-Origin 'https://${cfgWeb.domains.api}'; + ''; + }; + }; + }; + }) + ]; + + meta.maintainers = with lib.maintainers; [oddlama]; +} diff --git a/nixos/hosts/forgejo/forgejo.nix b/nixos/hosts/forgejo/forgejo.nix index f59029d..11ed29f 100644 --- a/nixos/hosts/forgejo/forgejo.nix +++ b/nixos/hosts/forgejo/forgejo.nix @@ -16,6 +16,11 @@ in { user = "forgejo"; group = "forgejo"; stateDir = "/srv/forgejo"; + secrets = { + mailer = { + PASSWD = ; + }; + }; settings = { # https://forgejo.org/docs/latest/admin/config-cheat-sheet/ server = { @@ -34,8 +39,7 @@ in { PROTOCOL = "smtp+starttls"; SMTP_ADDR = "mail.procopius.dk"; - USER = "admin@procopius.dk"; - PASSWD = "mikael"; + USER = "git@procopius.dk"; }; database = { DB_TYPE = lib.mkForce "postgres"; @@ -57,9 +61,9 @@ in { ZOMBIE_TASK_TIMEOUT = "30m"; }; ldap = { - AUTHORIZATION_NAME = "My LDAP"; - HOST = "ldap.example.com"; - PORT = 389; + AUTHORIZATION_NAME = "LLDAP"; + HOST = "auth.lab"; + PORT = 3890; ENABLE_TLS = false; USER_SEARCH_BASE = "ou=users,dc=example,dc=com"; USER_FILTER = "(&(objectClass=user)(sAMAccountName=%[1]s))"; @@ -86,7 +90,6 @@ in { security = { INSTALL_LOCK = true; - SECRET_KEY = config.sops.secrets."forgejo-secret-key".path; # can be another secret }; }; }; diff --git a/nixos/hosts/forgejo/storage.nix b/nixos/hosts/forgejo/storage.nix index f944371..d17421c 100644 --- a/nixos/hosts/forgejo/storage.nix +++ b/nixos/hosts/forgejo/storage.nix @@ -1,26 +1,12 @@ { - # services.nfs.client = { - # enable = true; - # idmapd.enable = true; - # }; - - # environment.etc."idmapd.conf".text = '' - # [General] - # Domain = localdomain - - # [Mapping] - # Nobody-User = nobody - # Nobody-Group = nogroup - # ''; - - boot.supportedFilesystems = [ "nfs" ]; + boot.supportedFilesystems = ["nfs"]; services.rpcbind.enable = true; fileSystems."/srv/forgejo" = { device = "192.168.1.226:/volume1/data/forgejo"; fsType = "nfs4"; - options = [ "x-systemd.automount" "noatime" "_netdev" ]; + options = ["x-systemd.automount" "noatime" "_netdev"]; }; systemd.tmpfiles.rules = [ diff --git a/nixos/hosts/sandbox/host.nix b/nixos/hosts/sandbox/host.nix deleted file mode 100644 index 41e1816..0000000 --- a/nixos/hosts/sandbox/host.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - config, - pkgs, - modulesPath, - lib, - ... -}: { - imports = [ - ../../templates/base.nix - ./networking.nix - ./storage.nix - ./sandbox.nix - ./warpgate.nix - ]; -} diff --git a/nixos/hosts/sandbox/networking.nix b/nixos/hosts/sandbox/networking.nix deleted file mode 100644 index e1419e9..0000000 --- a/nixos/hosts/sandbox/networking.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ config, lib, pkgs, ... }: -{ - networking.hostName = "sandbox"; - networking.interfaces.ens18.useDHCP = true; - networking.defaultGateway = "192.168.1.1"; -} diff --git a/nixos/hosts/sandbox/sandbox.nix b/nixos/hosts/sandbox/sandbox.nix deleted file mode 100644 index dc93e49..0000000 --- a/nixos/hosts/sandbox/sandbox.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ config, pkgs, modulesPath, lib, ... }: - -{ -} diff --git a/nixos/hosts/sandbox/storage.nix b/nixos/hosts/sandbox/storage.nix deleted file mode 100644 index c94b739..0000000 --- a/nixos/hosts/sandbox/storage.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ - boot.supportedFilesystems = ["nfs"]; - - services.rpcbind.enable = true; - - # fileSystems."/mnt/nas" = { - # device = "192.168.1.226:/volume1/docker"; - # fsType = "nfs"; - # options = [ "noatime" "vers=4" "rsize=8192" "wsize=8192" ]; - # }; -} diff --git a/nixos/hosts/sandbox/warpgate.nix b/nixos/hosts/sandbox/warpgate.nix deleted file mode 100644 index 7c24ad8..0000000 --- a/nixos/hosts/sandbox/warpgate.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ - virtualisation = { - containers.enable = true; - oci-containers.backend = "podman"; - - podman = { - enable = true; - - # Create a `docker` alias for podman, to use it as a drop-in replacement - dockerCompat = true; - - # Required for containers under podman-compose to be able to talk to each other. - defaultNetwork.settings.dns_enabled = true; - }; - }; - virtualisation.oci-containers.containers = { - warpgate = { - image = "ghcr.io/warp-tech/warpgate"; - ports = [ - "2222:2222" - "8888:8888" - ]; - volumes = [ - "/srv/warpgate/data:/data" - ]; - }; - }; - - systemd.tmpfiles.rules = [ - "d /srv/warpgate 0755 root root -" - "d /srv/warpgate/data 0755 root root -" - ]; - - networking.firewall.allowedTCPPorts = [8888]; -} diff --git a/overlays/default.nix b/overlays/default.nix new file mode 100644 index 0000000..7bfcb4c --- /dev/null +++ b/overlays/default.nix @@ -0,0 +1,23 @@ +# This file defines overlays +{inputs, ...}: { + # This one brings our custom packages from the 'pkgs' directory + additions = final: _prev: import ../pkgs final.pkgs; + + # This one contains whatever you want to overlay + # You can change versions, add patches, set compilation flags, anything really. + # https://nixos.wiki/wiki/Overlays + modifications = final: prev: { + # example = prev.example.overrideAttrs (oldAttrs: rec { + # ... + # }); + }; + + # When applied, the unstable nixpkgs set (declared in the flake inputs) will + # be accessible through 'pkgs.unstable' + unstable-packages = final: _prev: { + unstable = import inputs.nixpkgs-unstable { + system = final.system; + config.allowUnfree = true; + }; + }; +} diff --git a/pkgs/default.nix b/pkgs/default.nix new file mode 100644 index 0000000..f5abee4 --- /dev/null +++ b/pkgs/default.nix @@ -0,0 +1,6 @@ +# Custom packages, that can be defined similarly to ones from nixpkgs +# You can build them using 'nix build .#example' +pkgs: { + # example = pkgs.callPackage ./example { }; + ente-web = pkgs.callPackage ./ente-web.nix {}; +} diff --git a/pkgs/ente-web.nix b/pkgs/ente-web.nix new file mode 100644 index 0000000..8aa0fa6 --- /dev/null +++ b/pkgs/ente-web.nix @@ -0,0 +1,91 @@ +{ + lib, + stdenv, + fetchFromGitHub, + fetchYarnDeps, + nodejs, + yarnConfigHook, + yarnBuildHook, + nix-update-script, + extraBuildEnv ? {}, + # This package contains serveral sub-applications. This specifies which of them you want to build. + enteApp ? "photos", + # Accessing some apps (such as account) directly will result in a hardcoded redirect to ente.io. + # To prevent users from accidentally logging in to ente.io instead of the selfhosted instance, you + # can set this parameter to override these occurrences with your own url. Must include the schema. + # Example: https://my-ente.example.com + enteMainUrl ? null, +}: +stdenv.mkDerivation (finalAttrs: { + pname = "ente-web-${enteApp}"; + version = "1.0.4"; + + src = fetchFromGitHub { + owner = "ente-io"; + repo = "ente"; + sparseCheckout = ["web"]; + tag = "photos-v${finalAttrs.version}"; + fetchSubmodules = true; + hash = "sha256-M1kAZgqjbWNn6LqymtWRmAk/v0vWEGbyS50lVrsr85o="; + }; + sourceRoot = "${finalAttrs.src.name}/web"; + + offlineCache = fetchYarnDeps { + yarnLock = "${finalAttrs.src}/web/yarn.lock"; + hash = "sha256-EYhYwy6+7bgWckU/7SfL1PREWw9JUgKxWadSVtoZwXs="; + }; + + nativeBuildInputs = [ + yarnConfigHook + yarnBuildHook + nodejs + ]; + + # See: https://github.com/ente-io/ente/blob/main/web/apps/photos/.env + env = extraBuildEnv; + + # Replace hardcoded ente.io urls if desired + postPatch = lib.optionalString (enteMainUrl != null) '' + substituteInPlace \ + apps/payments/src/services/billing.ts \ + apps/photos/src/pages/shared-albums.tsx \ + --replace-fail "https://ente.io" ${lib.escapeShellArg enteMainUrl} + + substituteInPlace \ + apps/accounts/src/pages/index.tsx \ + --replace-fail "https://web.ente.io" ${lib.escapeShellArg enteMainUrl} + ''; + + yarnBuildScript = "build:${enteApp}"; + installPhase = let + distName = + if enteApp == "payments" + then "dist" + else "out"; + in '' + runHook preInstall + + cp -r apps/${enteApp}/${distName} $out + + runHook postInstall + ''; + + passthru.updateScript = nix-update-script { + extraArgs = [ + "--version-regex" + "photos-v(.*)" + ]; + }; + + meta = { + description = "Ente application web frontends"; + homepage = "https://ente.io/"; + changelog = "https://github.com/ente-io/ente/releases"; + license = lib.licenses.agpl3Only; + maintainers = with lib.maintainers; [ + pinpox + oddlama + ]; + platforms = lib.platforms.all; + }; +})