Commit files for public release
All checks were successful
CI / build (push) Successful in 13m53s

This commit is contained in:
Fabian Hauser 2024-10-02 16:52:04 +03:00
commit fef2377502
174 changed files with 7423 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

53
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: CI
on:
push:
pull_request:
env:
ATTIC_AUTH_TOKEN: ${{ secrets.ATTIC_AUTH_TOKEN }}
jobs:
build:
container: nixpkgs/nix-flakes:nixos-24.05
steps:
- name: Initialize CI
uses: https://git.qo.is/qo.is/actions-nix-init@main
with:
token: ${{ secrets.CI_TOKEN }}
lfs: false
- name: Add submodules to nix store to circumvent another nix bug
run: |
git clone https://git.qo.is/qo.is/infrastructure-private.git /tmp/private
cd /tmp/private
nix flake prefetch
- name: Use attic cache
run: nix run .#cache use
- name: Build
run: |
nix run .#cache watch &
nix build --max-jobs 12 --cores 12
kill %1
nix run .#cache push
- name: Run Checks
run: |
nix run .#cache watch &
nix flake check
kill %1
- name: Deploy Docs
if: success() && github.ref == 'refs/heads/main'
run: |
nix run .#cache watch &
mkdir ~/.ssh/
echo -e "Host lindberg-webapps.backplane.net.qo.is\n StrictHostKeyChecking no" >> ~/.ssh/config
(umask 0077 && printf "%s\n" "${{ secrets.SSH_DEPLOY_KEY }}" > ~/.ssh/id_ed25519)
# Remote build might be neccessary due to non-wheel nix users signing restrictions.
# However, the build should come from the cache anyway.
nix develop --command deploy --skip-checks --remote-build .#lindberg-webapps.\"docs-ops.qo.is\"
kill %1

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
/configuration.nix
/result*
/host/*/result*
*.qcow2
/.direnv
/book
/.sops.yaml

4
.gitmodules vendored Normal file
View file

@ -0,0 +1,4 @@
[submodule "private"]
path = private
url = https://git.qo.is/qo.is/infrastructure-private.git
branch = main

18
.nixd.json Normal file
View file

@ -0,0 +1,18 @@
{
"eval": {
"target": {
"args": ["-f", "default.nix"],
"installable": ""
}
},
"formatting": {
"command": "nixfmt"
},
"options": {
"enable": true,
"target": {
"args": [],
"installable": ""
}
}
}

5
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"recommendations": [
"jnoortheen.nix-ide"
]
}

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"nix.enableLanguageServer": true,
"nix.formatterPath": "nixfmt",
"nix.serverPath": "nixd",
}

61
README.md Normal file
View file

@ -0,0 +1,61 @@
# qo.is Infrastructure
[This repository](https://gitlab.com/qo.is/infrastructure) contains the infrastructure configuration and documentation sources.
Check out the current [rendered documentation on the deployed gitlab page](https://docs-ops.qo.is).
## Structure
`nixos-configurations`: Main nixos configuration for every host.
`defaults`: Configuration defaults
`modules`: Custom modules (e.g. for vpn and routers)
## Building
This repository requires [nix flakes](https://nixos.wiki/wiki/Flakes)
- `nix build`
Build all host configurations and docs
- `nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel`
Build a single host configuration with
- `nix build .#docs`
Build the documentation website
## Development
- `nix develop`
Development environment
- `nix flake check`
Execute the project's checks
- `nix fmt`
Autofix formatting
### Working with the private submodule
On changes:
```bash
git add private
nix flake lock --update-input private
```
## Deployment
`nix run .#deploy`
See [Deployment](deployment.md) for details.
## Secrets
Secret management is done with [nix-sops](https://github.com/Mic92/sops-nix).
Secrets are stored in `private/passwords.sops.yaml` (sysadmin passwords),
`private/nixos-configurations/secrets.sops.yaml` (shared secrets for all hosts) and
`private/nixos-configurations/<hostname>/secrets.sops.yaml` (host specific secrets).
Usage:
```bash
sops
sops-rekey
```

33
SUMMARY.md Normal file
View file

@ -0,0 +1,33 @@
# Summary
- [Repository README](README.md)
- [Deployment](deploy/README.md)
---
- [Network Topology](defaults/meta/network.md)
- [Hardware (generic)](defaults/hardware/README.md)
- [Backups](backups.md)
- [Updates](updates.md)
- [New Host Setup](nixos-configurations/setup.md)
# Services
- [E-mail](email.md)
- [Git](nixos-modules/qois/git/README.md)
- [Nextcloud](defaults/nextcloud/README.md)
- [Nix Caches](nixos-configurations/lindberg-build/applications/README.md)
- [Static Pages](nixos-modules/qois/static-page/README.md)
- [VPN](defaults/vpn/README.md)
- [Vaultwarden](nixos-modules/vault/README.md)
# Nixos Configurations
- [calanda](nixos-configurations/calanda/README.md)
- [cyprianspitz](nixos-configurations/cyprianspitz/README.md)
- [fulberg](nixos-configurations/fulberg/README.md)
- [lindberg](nixos-configurations/lindberg/README.md)
- [stompert](nixos-configurations/stompert/README.md)
- [tierberg](nixos-configurations/tierberg/README.md)

19
backups.md Normal file
View file

@ -0,0 +1,19 @@
# Backups
We use [borg](https://www.borgbackup.org/) to create encrypted and deduplicated backups.
The backups are encrypted with a (key unlocked by a) secure passphrase that is deployed to the respective node and stored in the [pass repository](https://gitlab.com/qo.is/pass) resp. the sops files in this repository.
Service specific restore instructions are given in the respective services' documentation.
## Host Backups
All hosts make automated backups. See Modules `qois.backup-client` and `qois.backup-server` for details.
## Verify Backups Manually
```bash
ssh root@lindberg-nextcloud.backplane.net.qo.is -- systemctl status borgbackup-job-system-cyprianspitz.service
ssh root@lindberg-webapps.backplane.net.qo.is -- systemctl status borgbackup-job-system-cyprianspitz.service
ssh root@lindberg.backplane.net.qo.is -- systemctl status borgbackup-job-system-cyprianspitz.service
```

11
book.toml Normal file
View file

@ -0,0 +1,11 @@
[book]
authors = ["qo.is contributers"]
language = "en"
multilingual = false
src = "."
title = "qo.is infrastructure docs"
[preprocessor.cmdrun]
[preprocessor.plantuml]
plantuml-cmd="plantuml"

23
checks/default.nix Normal file
View file

@ -0,0 +1,23 @@
{
self,
system,
pkgs,
deployPkgs,
...
}@inputs:
{
${system} = {
# Check project formatting
format = pkgs.runCommand "nixfmt-check" { } ''
set -euo pipefail
cd ${self}
${self.formatter.${system}}/bin/formatter . --check
mkdir $out
'';
#TODO(#29): Integration/System tests
# Import deploy-rs tests
} // (deployPkgs.deploy-rs.lib.deployChecks self.deploy);
}

View file

@ -0,0 +1,5 @@
## Backplane Overlay Network
The `backplane.net.qo.is` overlay network connects all the hosts in a peer-to-peer fashion using [wgautomesh](https://git.deuxfleurs.fr/Deuxfleurs/wgautomesh).
The definition of the connected hosts are in [defaults/meta/network-virtual.nix](../meta/network-virtual.nix).

View file

@ -0,0 +1,58 @@
{
pkgs,
lib,
config,
...
}:
let
hostName = config.networking.hostName;
netName = "backplane";
netConfig = config.qois.meta.network.virtual.${netName};
hostNetConfig = netConfig.hosts.${hostName};
wgDefaultPort = 51825;
in
{
sops.secrets."wgautomesh/gossip-secret".restartUnits = [ "wgautomesh.service" ];
networking.wireguard.enable = true;
networking.wireguard.interfaces."wg-${netName}" = {
ips = [ "${hostNetConfig.v4.ip}/${builtins.toString netConfig.v4.prefixLength}" ];
listenPort = if hostNetConfig.endpoint != null then hostNetConfig.endpoint.port else wgDefaultPort;
privateKeyFile = "/secrets/wireguard/private/${netName}";
generatePrivateKeyFile = true;
};
systemd.network.wait-online.ignoredInterfaces = [ "wg-${netName}" ];
networking.firewall.allowedUDPPorts =
if hostNetConfig.endpoint != null then [ hostNetConfig.endpoint.port ] else [ wgDefaultPort ];
# Configure wgautomesh to setup peers. Make sure that the name is not used in the VPN module
services.wgautomesh = {
enable = true;
gossipSecretFile = builtins.toString config.sops.secrets."wgautomesh/gossip-secret".path;
openFirewall = true;
logLevel = "info";
settings = {
interface = "wg-${netName}";
# Map meta network configuration to the format of wgautomesh and filter out peers with endpoints
peers =
let
reachableHosts = lib.filterAttrs (
peerHostName: peerConfig: peerHostName != hostName # Not this host
) netConfig.hosts;
in
lib.mapAttrsToList (_: peerConfig: {
address = peerConfig.v4.ip;
endpoint =
if peerConfig.endpoint != null then
with peerConfig.endpoint; "${fqdn}:${builtins.toString port}"
else
null;
pubkey = peerConfig.publicKey;
}) reachableHosts;
};
};
systemd.services.wgautomesh.requires = [ "wireguard-wg-backplane.service" ];
}

View file

@ -0,0 +1,55 @@
{
config,
lib,
pkgs,
...
}:
{
environment.systemPackages =
with pkgs;
[
vim
tmux
killall
bc
rename
wipe
gnupg
ripgrep
]
++ [
nix-index
nix-diff
]
++ [
autojump
powerline-go
]
++ [
# File Utilities
ack
unzip
iotop
tree
vim
vimPlugins.pathogen
vimPlugins.airline
git
git-lfs
]
++ [
# Filesystem & Disk Utilities
parted
]
++ [
# Networking Utilities
nmap
bind
curl
wget
rsync
iftop
mailutils
];
}

View file

@ -0,0 +1,132 @@
{
config,
lib,
pkgs,
inputs,
...
}:
{
imports = [
./unfree.nix
./applications.nix
./overlays.nix
./security.nix
];
boot.loader.timeout = 2;
boot.tmp.useTmpfs = true;
boot.loader.grub.splashImage = null;
console.keyMap = "de_CH-latin1";
i18n.defaultLocale = "en_US.UTF-8";
boot.kernel.sysctl = {
"kernel.panic" = 20; # Reboot kernel on panic after this much seconds
};
boot.initrd.network.udhcpc.extraArgs = [
"-A"
"900" # Wait for a DHCP lease on boot for 15mins
];
systemd.watchdog = {
runtimeTime = "5m";
rebootTime = "10m";
};
users.mutableUsers = false;
users.users = {
root.openssh.authorizedKeys.keys =
with lib;
concatLists (
mapAttrsToList (
name: user:
if elem "wheel" user.extraGroups && name != "root" then user.openssh.authorizedKeys.keys else [ ]
) config.users.users
);
};
# Disable dependency on xorg
# TODO: Set environment.noXlibs on hosts that don't need any x libraries.
security.pam.services.su.forwardXAuth = lib.mkForce false;
# Package management
nix = {
settings = {
trusted-users = [
"root"
"@wheel"
];
substituters = [
"https://${inputs.self.nixosConfigurations.lindberg-build.config.qois.nixpkgs-cache.hostname}?priority=39"
"https://cache.nixos.org?priority=40"
"https://attic.qo.is/qois-infrastructure"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"qois-infrastructure:lh35ymN7Aoxm5Hz0S6JusxE+cYzMU+x9OMKjDVIpfuE="
];
};
gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 90d";
};
package = pkgs.nixFlakes;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
system.autoUpgrade = {
enable = true;
randomizedDelaySec = "30m";
flags = [
"--update-input"
"nixpkgs-nixos-2211"
"--commit-lock-file"
];
};
# Network services
networking.firewall = {
allowPing = true;
allowedTCPPorts = [ 22 ];
};
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
# temporary mitigation agains CVE-2024-6387 «regreSSHion» RCE
# See https://github.com/NixOS/nixpkgs/pull/323753#issuecomment-2199762128
settings.LoginGraceTime = 0;
};
security.acme = {
acceptTerms = true;
defaults.email = "sysadmin@qo.is";
};
# Default Settings
environment.etc = {
gitconfig.source = ./etc/gitconfig;
vimrc.source = ./etc/vimrc;
};
programs.autojump.enable = true;
programs.vim.defaultEditor = true;
sops.defaultSopsFile =
let
defaultSopsPath = "${inputs.private}/nixos-configurations/${config.networking.hostName}/secrets.sops.yaml";
in
lib.mkIf (builtins.pathExists defaultSopsPath) defaultSopsPath;
services.fstrim.enable = true;
qois.outgoing-server-mail.enable = true;
qois.backup-client.enable = true;
systemd.extraConfig = "DefaultLimitNOFILE=4096";
}

View file

@ -0,0 +1,31 @@
[core]
packedGitWindowSize = 16m
packedGitLimit = 64m
[pack]
windowMemory = 64m
packSizeLimit = 64m
thread = 1
deltaCacheSize = 1m
[color]
branch = auto
diff = auto
status = auto
[push]
default = simple
[pull]
rebase = true
[branch]
autosetuprebase = always
[commit]
# gpgsign = true
[tag]
# gpgsign = true
[alias]
s = status --short --branch
a = add --patch
c = commit --message
l = log --color --graph --pretty=format:'%Cred%h%Creset - %C(bold)%s%Creset%C(yellow)%d%Creset %C(green)%an%Creset %C(cyan)%cr%Creset' --abbrev-commit
d = diff
[diff]
# noprefix = true

View file

@ -0,0 +1,54 @@
" Use Vim settings, rather than Vi settings (much better!).
" This must be first, because it changes other options as a side effect.
" Avoid side effects when it was already reset.
if &compatible
set nocompatible
endif
" Convenient command to see the difference between the current buffer and the
" file it was loaded from, thus the changes you made.
" Only define it when not defined already.
" Revert with: ":delcommand DiffOrig".
if !exists(":DiffOrig")
command DiffOrig vert new | set bt=nofile | r ++edit # | 0d_ | diffthis
\ | wincmd p | diffthis
endif
" Don't wake up system with blinking cursor:
" http://www.linuxpowertop.org/known.php
let &guicursor = &guicursor . ",a:blinkon0"
""""""""""""""""""""""""""
" Design Settings
""""""""""""""""""""""""""
set background=dark
colorscheme elflord
""""""""""""""""""""""""""
" Other Settings
""""""""""""""""""""""""""
set ignorecase " Ignore search case
set autoindent " Newline with automatic text indent
set ruler " Show current position
set pastetoggle=<F2>
set ignorecase
set hidden
set splitbelow
set splitright
set tabstop=2
set shiftwidth=2
set softtabstop=2
set expandtab
set listchars="eol:¬,tab:>·,trail:~,extends:>,precedes:<,space:␣"
set grepprg=ack\ -k
filetype plugin indent on
syntax on

View file

@ -0,0 +1,12 @@
{
config,
lib,
pkgs,
options,
...
}:
{
nixpkgs.overlays = [ (import ../../overlays) ];
nix.nixPath = options.nix.nixPath.default;
}

View file

@ -0,0 +1,37 @@
{
config,
lib,
pkgs,
...
}:
with lib;
{
# ###########################################################################
# Options taken from hardened kernel profile, see
# https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix
# ###########################################################################
# Enable strict reverse path filtering (that is, do not attempt to route
# packets that "obviously" do not belong to the iface's network; dropped
# packets are logged as martians).
boot.kernel.sysctl."net.ipv4.conf.all.log_martians" = mkDefault true;
boot.kernel.sysctl."net.ipv4.conf.all.rp_filter" = mkDefault "1";
boot.kernel.sysctl."net.ipv4.conf.default.log_martians" = mkDefault true;
boot.kernel.sysctl."net.ipv4.conf.default.rp_filter" = mkDefault "1";
# Ignore broadcast ICMP (mitigate SMURF)
boot.kernel.sysctl."net.ipv4.icmp_echo_ignore_broadcasts" = mkDefault true;
# Ignore incoming ICMP redirects (note: default is needed to ensure that the
# setting is applied to interfaces added after the sysctls are set)
boot.kernel.sysctl."net.ipv4.conf.all.accept_redirects" = mkDefault false;
boot.kernel.sysctl."net.ipv4.conf.all.secure_redirects" = mkDefault false;
boot.kernel.sysctl."net.ipv4.conf.default.accept_redirects" = mkDefault false;
boot.kernel.sysctl."net.ipv4.conf.default.secure_redirects" = mkDefault false;
boot.kernel.sysctl."net.ipv6.conf.all.accept_redirects" = mkDefault false;
boot.kernel.sysctl."net.ipv6.conf.default.accept_redirects" = mkDefault false;
# Ignore outgoing ICMP redirects (this is ipv4 only)
boot.kernel.sysctl."net.ipv4.conf.all.send_redirects" = mkDefault false;
boot.kernel.sysctl."net.ipv4.conf.default.send_redirects" = mkDefault false;
}

View file

@ -0,0 +1,22 @@
{
config,
lib,
pkgs,
...
}:
{
nixpkgs.config.allowUnfreePredicate =
pkg:
builtins.elem (lib.getName pkg) [
"corefonts"
"camingo-code"
"helvetica-neue-lt-std"
#"kochi-substitute-naga10"
"ttf-envy-code-r"
"vista-fonts"
"vista-fonts-chs"
"xkcd-font-unstable"
"ricty"
];
}

View file

@ -0,0 +1,39 @@
{
config,
lib,
modulesPath,
pkgs,
...
}:
{
imports = [
../base-minimal
(modulesPath + "/profiles/qemu-guest.nix")
];
boot.loader.grub.enable = true;
system.autoUpgrade.allowReboot = true;
services.qemuGuest.enable = true;
boot.initrd.availableKernelModules = [
"ahci"
"xhci_pci"
"sr_mod"
];
# Taken from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/minimal.nix
documentation.enable = lib.mkDefault false;
documentation.doc.enable = lib.mkDefault false;
documentation.info.enable = lib.mkDefault false;
documentation.man.enable = lib.mkDefault false;
documentation.nixos.enable = lib.mkDefault false;
}

View file

@ -0,0 +1,32 @@
{
config,
lib,
pkgs,
...
}:
{
environment.systemPackages =
with pkgs;
[
pciutils
dmidecode
smartmontools
iw
efibootmgr
efitools
efivar
pwgen
powertop
lm_sensors
]
++ [
# Filesystem & Disk Utilities
hdparm
smartmontools
]
++ [
# Networking Utilities
tcpdump
];
}

25
defaults/base/default.nix Normal file
View file

@ -0,0 +1,25 @@
{
config,
lib,
pkgs,
...
}:
{
imports = [
../base-minimal
./applications.nix
];
# System Services
services.fwupd.enable = true;
services.smartd = {
enable = true;
notifications.mail = {
enable = true;
mailer = "${pkgs.msmtp}/bin/sendmail";
sender = "system@qo.is";
recipient = "sysadmin@qo.is";
};
};
}

View file

@ -0,0 +1,15 @@
# APU
## Setup
To boot the nixos installer with the console port, add `console=ttyS0,115200n8` to the kernel command line in grub.
# ASROCK Mainboards
`F2`: Boot into BIOS
`F11`: Select boot device
# NUC
- [Boot Keybindings](https://www.intel.com/content/www/us/en/support/articles/000005672/boards-and-kits/desktop-boards.html)

38
defaults/hardware/apu.nix Normal file
View file

@ -0,0 +1,38 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot.loader.grub.extraConfig = "\n serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1\n terminal_input serial\n terminal_output serial\n ";
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"ehci_pci"
"usbhid"
"usb_storage"
"sd_mod"
"sdhci_pci"
"igb"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [
"kvm-amd"
"virtio"
"tun"
];
boot.extraModulePackages = [ ];
boot.kernelParams = [ "console=ttyS0,115200n8" ];
# CPU Configuration
hardware.cpu.amd.updateMicrocode = true;
nix.settings.max-jobs = lib.mkDefault 4;
}

View file

@ -0,0 +1,40 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot.loader.grub.extraConfig = "\n serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1\n terminal_input serial\n terminal_output serial\n ";
boot.initrd.availableKernelModules = [
"ahci"
"ohci_pci"
"xhci_pci"
"ahci"
"ehci_pci"
"usbhid"
"usb_storage"
"sd_mod"
"sdhci_pci"
"r8169"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [
"kvm-amd"
"virtio"
"tun"
];
boot.extraModulePackages = [ ];
boot.kernelParams = [ "console=ttyS0,115200n8" ];
hardware.cpu.amd.updateMicrocode = true;
nix.settings.max-jobs = lib.mkDefault 2;
}

View file

@ -0,0 +1,33 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
"e1000e"
"virtio-pci"
];
boot.initrd.kernelModules = [ ];
# boot.kernelModules = [ "kvm-intel" "virtio" "tun" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# boot.kernelParams = [ "console=ttyS0,115200n8" ];
hardware.cpu.intel.updateMicrocode = true;
powerManagement.cpuFreqGovernor = "ondemand";
nix.settings.max-jobs = lib.mkDefault 8;
}

View file

@ -0,0 +1,27 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot.initrd.availableKernelModules = [
"nvme"
"usbhid"
"usb_storage"
"sd_mod"
"xhci_pci"
"ahci"
"virtio-pci"
"igb"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
hardware.cpu.amd.updateMicrocode = true;
nix.settings.max-jobs = lib.mkDefault 24;
}

33
defaults/hardware/nuc.nix Normal file
View file

@ -0,0 +1,33 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
"e1000e"
"virtio-pci"
];
boot.initrd.kernelModules = [ ];
# boot.kernelModules = [ "kvm-intel" "virtio" "tun" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# boot.kernelParams = [ "console=ttyS0,115200n8" ];
hardware.cpu.intel.updateMicrocode = true;
powerManagement.cpuFreqGovernor = "ondemand";
nix.settings.max-jobs = lib.mkDefault 8;
}

View file

@ -0,0 +1,40 @@
diff --unified --recursive --text archlinux-linux/drivers/net/wireless/ath/regd.c archlinux-linux-patched/drivers/net/wireless/ath/regd.c
--- a/drivers/net/wireless/ath/regd.c 2019-08-29 18:31:52.749909030 +0200
+++ b/drivers/net/wireless/ath/regd.c 2019-08-29 18:33:33.318773763 +0200
@@ -345,6 +345,8 @@
struct ieee80211_channel *ch;
unsigned int i;
+ return;
+
for (band = 0; band < NUM_NL80211_BANDS; band++) {
if (!wiphy->bands[band])
continue;
@@ -378,6 +380,8 @@
{
struct ieee80211_supported_band *sband;
+ return;
+
sband = wiphy->bands[NL80211_BAND_2GHZ];
if (!sband)
return;
@@ -407,6 +411,8 @@
struct ieee80211_channel *ch;
unsigned int i;
+ return;
+
if (!wiphy->bands[NL80211_BAND_5GHZ])
return;
@@ -639,6 +645,9 @@
const struct ieee80211_regdomain *regd;
wiphy->reg_notifier = reg_notifier;
+
+ return 0;
+
wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
REGULATORY_CUSTOM_REG;

View file

@ -0,0 +1,23 @@
{
config,
lib,
pkgs,
...
}:
{
boot.kernelPatches = [
{
name = "ath10k-override-eeprom-regulatory-domain";
patch = ./ath10k-override-eeprom-regulatory-domain.patch;
extraConfig = ''
EXPERT y
CFG80211_CERTIFICATION_ONUS y
ATH_REG_DYNAMIC_USER_REG_HINTS y
ATH_REG_DYNAMIC_USER_CERT_TESTING y
ATH_REG_DYNAMIC_USER_CERT_TESTING y
ATH9K_DFS_CERTIFIED y
ATH10K_DFS_CERTIFIED y
'';
}
];
}

View file

@ -0,0 +1,11 @@
{
config,
lib,
pkgs,
...
}:
{
services.hostapd.extraConfig = ''
ht_capab=[HT40-][HT40+][SHORT-GI-40][TX-STBC][RX-STBC1][DSSS_CCK-40]
'';
}

13
defaults/meta/default.nix Normal file
View file

@ -0,0 +1,13 @@
{
config,
lib,
pkgs,
...
}:
{
imports = [
./hosts.nix
./network-physical.nix
./network-virtual.nix
];
}

44
defaults/meta/hosts.json Normal file
View file

@ -0,0 +1,44 @@
{
"fulberg": {
"hostName": "fulberg",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDCG9qqpUOJ2RsohIqhMuw3YZZSrnPqhf5ayh5y0Cq/I"
},
"calanda": {
"hostName": "calanda",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKdoOZcFFRXIqEWqUnwCk/kqP8DZw6/4omDefCT6aNN4"
},
"lindberg": {
"hostName": "lindberg",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDksfXKLgPJVuWHAl/pxWRhghun8U6asTZNHa34u+gJw"
},
"lindberg-nextcloud": {
"hostName": "lindberg-nextcloud",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFR5U4yhZ2x/WN9dO+hVVSTCPMyv/1TB8mbuCXxexZOo"
},
"lindberg-build": {
"hostName": "lindberg-build",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMnDwwGiucyTI2U8o2rC53weJwp6dO8zcF7BZjkvVq7e"
},
"lindberg-webapps": {
"hostName": "lindberg-webapps",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJT99lj5OI+V1PlZl/T2ikBORwMiXjDfWpHYfq/GvUM5"
},
"batzberg": {
"hostName": "batzberg"
},
"tierberg": {
"hostName": "tierberg",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJS2v0mUDJsNr1DHdgjxEQRnoVaEmExFfvHqpvagYLi6"
},
"stompert": {
"hostName": "stompert",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKuqMPLbREFIrYcmReaRoHdz1TatpvlrZN14L6cikia"
},
"router-coredump": {
"hostName": "router"
},
"cyprianspitz": {
"hostName": "cyprianspitz",
"sshKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE4udYgCfxHEAkM9r8yaerk7l+BgW7039imM0moKpTbB"
}
}

4
defaults/meta/hosts.nix Normal file
View file

@ -0,0 +1,4 @@
{ ... }:
{
qois.meta.hosts = builtins.fromJSON (builtins.readFile ./hosts.json);
}

View file

@ -0,0 +1,114 @@
{
config,
lib,
pkgs,
...
}:
{
qois.meta.network.physical = {
plessur-ext = {
v4 = {
id = "85.195.200.253";
prefixLength = 24;
};
v6 = {
id = "2a02:169:1e02::";
prefixLength = 48;
};
domain = "plessur-ext.net.qo.is";
hosts = {
calanda = {
v4.ip = "85.195.200.253";
};
};
};
plessur-dmz = {
v4 = {
id = "10.1.2.0";
prefixLength = 24;
gateway = "10.1.2.1";
nameservers = [ "10.1.2.1" ];
};
domain = "plessur-dmz.net.qo.is";
hosts = {
calanda = {
v4.ip = "10.1.2.1";
};
fulberg = {
v4.ip = "10.1.2.2";
};
};
};
plessur-lan = {
v4 = {
id = "10.1.1.0";
prefixLength = 24;
};
domain = "plessur-lan.net.qo.is";
hosts = {
calanda = {
v4.ip = "10.1.1.1";
};
};
};
eem-lan = {
domain = "eem-lan.net.qo.is";
hosts = {
stompert.v4.ip = ""; # TODO
};
};
riedbach-ext = {
# IP: Dynamic
domain = "riedbach-ext.net.qo.is";
hosts = {
lindberg = {
# TODO: This is the router, not really lindberg.
v4.ip = "145.40.194.243";
};
};
};
lattenbach-ext = {
# Forwarded ports:
# udp:51820 -> 10.0.0.60:51820
# tcp:51022 -> 10.0.0.60:22
# tcp:51023 -> 10.0.0.60:2222
domain = "lattenbach-ext.net.qo.is";
hosts.router-coredump.v4.ip = "5.226.148.126";
};
lattenbach-lan = {
# Coredump LAN
v4 = {
id = "10.0.0.0";
prefixLength = 16;
};
domain = "lattenbach-lan.net.qo.is";
hosts = {
tierberg = {
v4.ip = "10.0.0.60";
};
};
};
lattenbach-nas = {
# Coredump net between apu and nas
v4 = {
id = "192.168.254.0";
prefixLength = 24;
};
domain = "lattenbach-nas.net.qo.is";
hosts = {
tierberg.v4.ip = "192.168.254.2";
batzberg.v4.ip = "192.168.254.1";
};
};
};
}

View file

@ -0,0 +1,114 @@
{
config,
lib,
pkgs,
...
}:
{
qois.meta.network.virtual =
let
physical-network = config.qois.meta.network.physical;
in
{
vpn = {
v4 = {
id = "100.64.0.0";
prefixLength = 10;
};
domain = "vpn.qo.is";
hosts = { };
};
backplane = {
v4 = {
id = "10.250.0.0";
prefixLength = 24;
};
domain = "backplane.net.qo.is";
hosts = {
fulberg = {
v4.ip = "10.250.0.1";
endpoint = {
fqdn = physical-network.plessur-ext.hosts.calanda.fqdn;
port = 51821;
};
publicKey = "xcQOu+pp4ckNygcsLmJL1NmUzbbC+k3I7y+hJ9Ul4nk=";
persistentKeepalive = 25;
};
lindberg = {
v4.ip = "10.250.0.2";
#endpoint = { # TODO: Port forwarding
# fqdn = physical-network.riedbach-ext.hosts.lindberg.fqdn;
# port = 51821;
#};
publicKey = "uxxdpFXSTnfTvzSEzrUq4DuWSILJD5tNj6ks2jhWF10=";
persistentKeepalive = 25; # TODO: Remove when port forwarding enabled
};
lindberg-nextcloud = {
v4.ip = "10.250.0.3";
publicKey = "6XGL4QKB8AMpm/VGcTgWqk9RiSws7DmY5TpIDkXbwlg=";
persistentKeepalive = 25;
};
tierberg = {
v4.ip = "10.250.0.4";
publicKey = "51j1l+pT9W61wx4y2KyUb1seLdCHs3FUKAjmrHBFz1w=";
persistentKeepalive = 25;
};
stompert = {
v4.ip = "10.250.0.5";
publicKey = "CHTjQbmN9WhbRCxKgowxpMx4c5Zu0NDk0rRXEvuB3XA=";
persistentKeepalive = 25;
};
calanda = {
v4.ip = "10.250.0.6";
publicKey = "WMuMCzo8e/aNeGP7256mhK0Fe+x06Ws7a9hOZDPCr0M=";
endpoint = {
fqdn = physical-network.plessur-ext.hosts.calanda.fqdn;
port = 51823;
};
};
lindberg-build = {
v4.ip = "10.250.0.7";
publicKey = "eWuvGpNVl601VDIgshOm287dlZa/5gF9lL4SjYEbIG8=";
persistentKeepalive = 25;
};
lindberg-webapps = {
v4.ip = "10.250.0.8";
publicKey = "LOA3Kumg8FV4DJxONwv+/8l/jOQLJ6SD2k/RegerR04=";
persistentKeepalive = 25;
};
cyprianspitz = {
v4.ip = "10.250.0.9";
endpoint = {
fqdn = physical-network.plessur-ext.hosts.calanda.fqdn;
port = 51824;
};
publicKey = "iLzHSgIwZz44AF7961mwEbK9AnSwcr+aKpd7XAAVTHo=";
};
};
};
lindberg-vms-nat = {
v4 = {
id = "10.247.0.0";
prefixLength = 24;
};
domain = "lindberg-vms-nat.net.qo.is";
hosts = {
lindberg.v4.ip = "10.247.0.1";
};
};
cyprianspitz-vms-nat = {
v4 = {
id = "10.247.0.0";
prefixLength = 24;
};
domain = "cyprianspitz-vms-nat.net.qo.is";
hosts = {
cyprianspitz.v4.ip = "10.248.0.1";
};
};
};
}

83
defaults/meta/network.md Normal file
View file

@ -0,0 +1,83 @@
# Network
This document provides an overview over the qo.is network structure.
## Physical View
```plantuml
@startuml
skinparam style strictuml
left to right direction
package "plessur.net.qo.is" {
entity mediaconvchur [
Media
Converter
(Passive)
]
node calanda
node fulberg
cloud plessurnet [
<i>LAN Plessur
]
mediaconvchur - "enp4" calanda
calanda "br0 (enp2, wlp1, wlp5)" --- plessurnet
calanda "enp4" -- "eno1" fulberg
}
package "riedbach.net.qo.is" {
node riedbachrouter
node lindberg
riedbachrouter -- "enp5s0" lindberg
}
package "eem.net.qo.is" {
node eemrouter
node stompert
eemrouter -- "enp2s0" stompert
}
cloud internet[
<b>@
]
package "coredump.net.qo.is" {
node coredumprouter
node tierberg
coredumprouter -- "enpXs0" tierberg
}
internet .. mediaconvchur: INIT7 Fiber (1G/1G)
internet .. riedbachrouter: iway Fiber (1G/1G)
internet .. eemrouter: KPN NL Fiber
internet .. coredumprouter: Openfactory DSL
@enduml
```
## DNS
All Services are published under the *qo.is* domain name. Following services are available:
`qo.is` Primery Domain - Redirect to docs.qo.is and some .well-known ressources
{{#include ../backplane-net/README.md}}
## Contacts
### Init7
- [Status Netzwerkdienste](https://www.init7.net/status/)
- [NOC E-Mail](mailto:noc@init7.net)
- +41 44 315 44 00
- Init7 (Schweiz) AG, Technoparkstrasse 5, CH-8406 Winterthur

View file

@ -0,0 +1,17 @@
# Nextcloud
Running on [cloud.qo.is](https://cloud.qo.is), contact someone from the board for administrative tasks.
At this time, we do not enforce any size limits or alike.
We have some globally configured shared folders for our family members.
For user documentation, refer to the [upstream Nextcloud docs](https://docs.nextcloud.com/server/stable/user_manual/en/). Clients can be downloaded from [nextcloud.com/install](https://nextcloud.com/install/).
## Backup / Restore
1. Stop all related services: nextcloud, php-fpm, redis etc.
2. (mabe dump redis data?)
3. Import Database Backup
4. Restore `/var/lib/nextcloud`, which is currently a bind mount on `lindberg`'s `/mnt/data` volume
5. Resync nextcloud files and database, see [nextcloud docs](https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html)

View file

@ -0,0 +1,81 @@
# Default configuration for hosts
{
config,
lib,
pkgs,
...
}:
{
sops.secrets."nextcloud/admin" = with config.users.users.nextcloud; {
inherit group;
owner = name;
};
qois.postgresql.enable = true;
qois.backup-client.includePaths = [ config.services.nextcloud.home ];
services.nextcloud = {
enable = true;
https = true;
webfinger = true;
maxUploadSize = "10G";
database.createLocally = true;
config = {
adminpassFile = config.sops.secrets."nextcloud/admin".path;
adminuser = "root";
dbtype = "pgsql";
};
phpOptions = {
"opcache.interned_strings_buffer" = "23";
};
poolSettings = {
"pm" = "dynamic";
"pm.max_children" = "256";
"pm.max_requests" = "500";
"pm.max_spare_servers" = "16";
"pm.min_spare_servers" = "2";
"pm.start_servers" = "8";
};
configureRedis = true;
caching.redis = true;
notify_push = {
enable = true;
bendDomainToLocalhost = true;
};
settings = {
log_type = "syslog";
syslog_tag = "nextcloud";
"memories.exiftool" = "${lib.getExe pkgs.exiftool}";
"memories.vod.ffmpeg" = "${lib.getExe pkgs.ffmpeg-headless}";
"memories.vod.ffprobe" = "${pkgs.ffmpeg-headless}/bin/ffprobe";
preview_ffmpeg_path = "${lib.getExe pkgs.ffmpeg-headless}";
mail_smtpmode = "sendmail";
mail_domain = "qo.is";
};
};
services.phpfpm.pools.nextcloud.settings = {
"pm.max_children" = lib.mkForce "256";
"pm.max_spare_servers" = lib.mkForce "16";
"pm.start_servers" = lib.mkForce "8";
};
users.users.nextcloud.extraGroups = [ "postdrop" ];
systemd.services.nextcloud-cron = {
path = [ pkgs.perl ];
};
environment.systemPackages = with pkgs; [
nodejs # required for Recognize
];
}

122
defaults/vpn/README.md Normal file
View file

@ -0,0 +1,122 @@
# VPN
On [vpn.qo.is](https://vpn.qo.is) we run a [Tailscale](https://tailscale.com) compatible VPN service. To use the service, you can use a normal Tailscale client with following additional configuration:
| Option | Recommended value | Description |
|--------|-------------------|-------------|
| `accept-routes` | enabled (flag) | Accept direct routes to internal services |
| `exit-node` | `100.64.0.5` (lindberg) or `100.64.0.6` (cypriaspitz) | Use host as [exit node](#exit-nodes) |
| `login-server` | `https://vpn.qo.is` | Use our own VPN service and not tailscale's upstream one |
⚠️ Currently, if the client is in an IPv6 network, the transport is broken. See [#51](https://gitlab.com/qo.is/infrastructure/-/issues/51) for progress on this.
## Exit nodes
- `100.64.0.5`: lindberg (riedbach-net)
- `100.64.0.6`: cyprianspitz (plessur-net)
Currently, name resolution for these do not work reliably on first starts, hence the IP must be used. This hould be fixed in the future.
## User and Client Management
To register a new client, you can generate a pre-auth key and insert it in the client:
```bash
headscale preauthkeys create --user marlene.mayer
```
Or alternatively use the register command shown when configuring the VPN client.
## ACL
At this time, there are a few ACL rules to isolate a users host but do not expect them to be expected to be enforced - expect your client to be accessible by the whole network.
## Exit Nodes
To add an exit node, create a preauth secret on the `vpn.qo.is` host:
```bash
headscale preauthkeys create --user srv --reusable
```
and configure the host as follows:
```nix
# TODO: This should not be a snipped but a module
{config, ...}: {
# Use this node as vpn exit node
services.tailscale = let meta = config.qois.meta; in {
enable = true;
openFirewall = true;
useRoutingFeatures = "server";
authKeyFile = "/secrets/wireguard/tailscale-key"; # The preauth secret. TODO: Should be in sops.
extraUpFlags = [
"--login-server=https://vpn.qo.is"
"--advertise-exit-node"
(
with meta.network.virtual.backplane.v4; "--advertise-routes=${id}/${builtins.toString prefixLength}"
)
"--advertise-tags=tag:srv"
];
};
}
```
and register it in Headscale with:
```bash
headscale nodes register -u srv -k nodekey:xyzxyzxyzxyzxyzxyzxyzxyz
```
With using the `srv` user, exit nodes and routes get automatically accepted as trusted.
## Clients
### NixOS
Sample config:
```nix
{ config, pkgs, ... }: {
services.tailscale = {
enable = true;
openFirewall = true;
useRoutingFeatures = "client";
authKeyFile = "/secrets/wireguard/tailscale-key"; # This is the pre-auth secret. Make sure it's only accessible by root.
extraUpFlags = [
"--operator"
"yourUserNameChangePlease"
"--accept-routes"
"--exit-node=100.64.0.5"
"--login-server=https://vpn.qo.is"
];
};
}
```
### Mobile App
> Android App: Tip 5 times on the tooltip dots to reveal server config option
See [this Headscale documentation for more](https://headscale.net/android-client/#configuring-the-headscale-url) on how to configure the mobile app. Note that on restarts, sometimes you have to reopen/save the config dialog. If the Tailscale login site is shown, just close the browser with the ❌.
## Backup and Restore
### Server
1. `systemctl stop headscale`
2. Replace `/var/lib/headscale`
3. `systemctl start headscale`
4. Monitor logs for errors
Note: `/var/lib/headscale` contains a sqlite database.
### Clients
1. `systemctl stop tailscaled`
2. Replace `/var/lib/tailscale`
3. `systemctl start tailscaled`
4. Monitor logs for errors

View file

@ -0,0 +1,18 @@
{
config,
lib,
pkgs,
...
}:
{
services.nginx = {
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedGzipSettings = true;
recommendedBrotliSettings = true;
logError = "stderr warn";
proxyResolveWhileRunning = true;
};
}

29
deploy/README.md Normal file
View file

@ -0,0 +1,29 @@
# Deployment
Note that you have to be connected to the `vpn.qo.is`
(or execute the deployment from a host that is in the `backplane.net.qo.is` overlay network)
and that you need to have SSH root access to the target machines.
#### Deploy to all hosts
```bash
nix run .#deploy-qois
```
#### Deploy to selected target hosts
```bash
nix run .#deploy-qois .#<hostname> .#<hostname2>
# e.g.
nix run .#deploy-qois .#fulberg
```
#### Deploy with extended timeouts (sometimes required for slow APU devices)
```bash
nix run .#deploy-qois .#calanda -- --confirm-timeout 600 --activation-timeout 600
```

12
deploy/default.nix Normal file
View file

@ -0,0 +1,12 @@
{
deployPkgs,
pkgs,
self,
...
}@params:
with pkgs.lib;
pipe ./. [
self.lib.loadSubmodulesFrom
(map (f: (import f params)))
(foldl recursiveUpdate { })
]

View file

@ -0,0 +1,17 @@
{
deployPkgs,
pkgs,
self,
system,
...
}:
let
domain = "docs-ops.qo.is";
in
{
nodes.lindberg-webapps.profiles."${domain}" = {
sshUser = "nginx-${domain}";
path = deployPkgs.deploy-rs.lib.activate.noop self.packages.${system}.docs;
profilePath = "/var/lib/nginx-${domain}/root";
};
}

20
deploy/system/default.nix Normal file
View file

@ -0,0 +1,20 @@
{
deployPkgs,
pkgs,
self,
system,
...
}:
{
nodes = pkgs.lib.mapAttrs (host: config: {
hostname = "${host}.backplane.net.qo.is";
profiles.system = {
sshUser = "root";
user = "root";
activationTimeout = 420;
confirmTimeout = 120;
path = deployPkgs.deploy-rs.lib.activate.nixos config;
};
}) self.nixosConfigurations;
}

56
dev-shells/default.nix Normal file
View file

@ -0,0 +1,56 @@
{
pkgs,
system,
self,
...
}:
{
${system}.default = pkgs.mkShell {
name = "qois-infrastructure-shell";
buildInputs =
let
vscode-with-extensions = pkgs.vscode-with-extensions.override {
vscodeExtensions = with pkgs.vscode-extensions; [ jnoortheen.nix-ide ];
vscode = pkgs.vscodium;
};
in
[ vscode-with-extensions ]
++ (with self.packages.${system}; [
cache
deploy-qois
sops
sops-rekey
])
++ (with pkgs; [
attic-client
deploy-rs
nixd
nixfmt-rfc-style
nixos-anywhere
ssh-to-age
pssh
yq
jq
]);
LANG = "C.UTF-8";
LC_ALL = "C.UTF-8";
shellHook = ''
# Bring xdg data dirs of dependencies and current program into the
# environment. This will allow us to get shell completion if any
# and there might be other benefits as well.
xdg_inputs=( "''${buildInputs[@]}" )
for p in "''${xdg_inputs[@]}"; do
if [[ -d "$p/share" ]]; then
XDG_DATA_DIRS="''${XDG_DATA_DIRS}''${XDG_DATA_DIRS+:}$p/share"
fi
done
export XDG_DATA_DIRS
# Make sure we support the pure case as well as non nixos cases
# where dynamic bash completions were not sourced.
#if ! type _completion_loader > /dev/null; then
# . ${pkgs.bash-completion}/etc/profile.d/bash_completion.sh
#fi
'';
};
}

20
email.md Normal file
View file

@ -0,0 +1,20 @@
# E-mail
Currently, we don't host our own e-mail services, but we with a [cyon](https://www.cyon.ch) webhosting.
The login for the [cyon admin panel](https://my.cyon.ch/) may be found in pass.
E-Mail accounts should be created in a `first.lastname@qo.is` fashion.
Alias/forwarding Domains may be added on an best effort basis.
Bills for these domains should go directly to the respective owner (i.e. should be registered with own accounts).
## System E-mails
For groups, systems, services that require e-mail access, other accounts may be created.
- Engineering receives mails on `sysadmin@qo.is`
- Servers send from `system@qo.is`
- `no-reply@qo.is` is blackholed and may be used by services that don't handle replies.
- Services include e.g. `vault@qo.is` for vaultwarden.

309
flake.lock Normal file
View file

@ -0,0 +1,309 @@
{
"nodes": {
"attic": {
"inputs": {
"crane": "crane",
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1726069220,
"narHash": "sha256-dAUWlC8uMJX9iovycfvJcg5nm3PzqJIRAOwN4z322zM=",
"owner": "zhaofengli",
"repo": "attic",
"rev": "416687e59c4f0b32742423458cab2c5ff8fe748a",
"type": "github"
},
"original": {
"owner": "zhaofengli",
"repo": "attic",
"type": "github"
}
},
"crane": {
"inputs": {
"nixpkgs": [
"attic",
"nixpkgs"
]
},
"locked": {
"lastModified": 1722960479,
"narHash": "sha256-NhCkJJQhD5GUib8zN9JrmYGMwt4lCRp6ZVNzIiYCl0Y=",
"owner": "ipetkov",
"repo": "crane",
"rev": "4c6c77920b8d44cd6660c1621dea6b3fc4b4c4f4",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"deploy-rs": {
"inputs": {
"flake-compat": "flake-compat_2",
"nixpkgs": "nixpkgs_2",
"utils": "utils"
},
"locked": {
"lastModified": 1718194053,
"narHash": "sha256-FaGrf7qwZ99ehPJCAwgvNY5sLCqQ3GDiE/6uLhxxwSY=",
"owner": "serokell",
"repo": "deploy-rs",
"rev": "3867348fa92bc892eba5d9ddb2d7a97b9e127a8a",
"type": "github"
},
"original": {
"owner": "serokell",
"repo": "deploy-rs",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs-nixos-stable"
]
},
"locked": {
"lastModified": 1726396892,
"narHash": "sha256-KRGuT5nGRAOT3heigRWg41tbYpTpapGhsWc+XjnIx0w=",
"owner": "nix-community",
"repo": "disko",
"rev": "51e3a7e51279fedfb6669a00d21dc5936c78a6ce",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"attic",
"nixpkgs"
]
},
"locked": {
"lastModified": 1722555600,
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1726042813,
"narHash": "sha256-LnNKCCxnwgF+575y0pxUdlGZBO/ru1CtGHIqQVfvjlA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "159be5db480d1df880a0135ca0bfed84c2f88353",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-nixos-stable": {
"locked": {
"lastModified": 1726320982,
"narHash": "sha256-RuVXUwcYwaUeks6h3OLrEmg14z9aFXdWppTWPMTwdQw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8f7492cce28977fbf8bd12c72af08b1f6c7c3e49",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-nixos-unstable": {
"locked": {
"lastModified": 1726243404,
"narHash": "sha256-sjiGsMh+1cWXb53Tecsm4skyFNag33GPbVgCdfj3n9I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "345c263f2f53a3710abe117f28a5cb86d0ba4059",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1724316499,
"narHash": "sha256-Qb9MhKBUTCfWg/wqqaxt89Xfi6qTD3XpTzQ9eXi3JmE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "797f7dc49e0bc7fab4b57c021cdf68f595e47841",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1702272962,
"narHash": "sha256-D+zHwkwPc6oYQ4G3A1HuadopqRwUY/JkMwHz1YF7j4Q=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e97b3e4186bcadf0ef1b6be22b8558eab1cdeb5d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"private": {
"inputs": {
"nixpkgs-nixos-unstable": [
"nixpkgs-nixos-unstable"
]
},
"locked": {
"lastModified": 1727557927,
"narHash": "sha256-+dTv85ZXAatKiCu5VKTQkFE/RmWdlXwkuPvjOmfcPBI=",
"ref": "refs/heads/main",
"rev": "9a646336c5ad419ec79ae81a47d68213bdcbff92",
"revCount": 5,
"type": "git",
"url": "file:./private"
},
"original": {
"type": "git",
"url": "file:./private"
}
},
"root": {
"inputs": {
"attic": "attic",
"deploy-rs": "deploy-rs",
"disko": "disko",
"nixpkgs-nixos-stable": "nixpkgs-nixos-stable",
"nixpkgs-nixos-unstable": "nixpkgs-nixos-unstable",
"private": "private",
"sops-nix": "sops-nix"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs-nixos-unstable"
],
"nixpkgs-stable": [
"nixpkgs-nixos-stable"
]
},
"locked": {
"lastModified": 1726218807,
"narHash": "sha256-z7CoWbSOtsOz8TmRKDnobURkKfv6nPZCo3ayolNuQGc=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "f30b1bac192e2dc252107ac8a59a03ad25e1b96e",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

63
flake.nix Normal file
View file

@ -0,0 +1,63 @@
{
description = "qo.is infrastructure: Host and Network Configuration";
nixConfig = {
extra-substituters = "https://attic.qo.is/qois-infrastructure";
extra-trusted-public-keys = "qois-infrastructure:lh35ymN7Aoxm5Hz0S6JusxE+cYzMU+x9OMKjDVIpfuE=";
};
inputs = {
attic.url = "github:zhaofengli/attic";
deploy-rs.url = "github:serokell/deploy-rs";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs-nixos-stable";
};
nixpkgs-nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-nixos-stable.url = "github:NixOS/nixpkgs/nixos-24.05";
sops-nix = {
url = "github:Mic92/sops-nix";
inputs = {
nixpkgs.follows = "nixpkgs-nixos-unstable";
nixpkgs-stable.follows = "nixpkgs-nixos-stable";
};
};
private.url = "git+file:./private";
private.inputs.nixpkgs-nixos-unstable.follows = "nixpkgs-nixos-unstable";
};
outputs =
{ nixpkgs-nixos-unstable, deploy-rs, ... }@inputs:
let
system = "x86_64-linux";
# Packages for development and build process
pkgs = import nixpkgs-nixos-unstable { inherit system; };
deployPkgs = import nixpkgs-nixos-unstable {
inherit system;
overlays = [
deploy-rs.overlay
(self: super: {
deploy-rs = {
inherit (pkgs) deploy-rs;
lib = super.deploy-rs.lib;
};
})
];
};
importParams = inputs // {
inherit pkgs;
inherit deployPkgs;
inherit system;
};
in
{
checks = import ./checks/default.nix importParams;
deploy = import ./deploy/default.nix importParams;
devShells = import ./dev-shells/default.nix importParams;
formatter.${system} = pkgs.writeShellScriptBin "formatter" ''
${pkgs.findutils}/bin/find $1 -type f -name '*.nix' -exec ${pkgs.nixfmt-rfc-style}/bin/nixfmt ''${@:2} {} +
'';
nixosConfigurations = import ./nixos-configurations/default.nix importParams;
nixosModules = import ./nixos-modules/default.nix importParams;
packages = import ./packages/default.nix importParams;
lib = import ./lib/default.nix importParams;
};
}

18
lib/default.nix Normal file
View file

@ -0,0 +1,18 @@
{ pkgs, ... }:
let
lib = pkgs.lib;
foldersWithNix =
path:
let
folders = lib.attrNames (lib.filterAttrs (n: t: t == "directory") (builtins.readDir path));
isFolderWithDefaultNix = folder: lib.pathExists (lib.path.append path "./${folder}/default.nix");
in
lib.filter isFolderWithDefaultNix folders;
in
{
inherit foldersWithNix;
loadSubmodulesFrom =
path: map (folder: lib.path.append path "./${folder}/default.nix") (foldersWithNix path);
}

View file

@ -0,0 +1 @@
# calanda

View file

@ -0,0 +1,21 @@
{ config, pkgs, ... }:
{
imports = [
./networking.nix
./filesystems.nix
../../defaults/hardware/apu.nix
../../defaults/base
../../defaults/meta
];
# This value determines the NixOS release from which the default
# settings for stateful data, like fi:le locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "23.05"; # Did you read the comment?
}

View file

@ -0,0 +1,20 @@
{ config, pkgs, ... }:
{
fileSystems."/" = {
device = "/dev/disk/by-uuid/16efc5db-0697-4f39-b64b-fc18ac318625";
fsType = "btrfs";
options = [
"defaults"
"subvol=nixos"
"noatime"
];
};
swapDevices = [ { device = "/dev/disk/by-uuid/b5104a7c-4a4a-4048-a9f8-44ddb0082632"; } ];
boot.loader.grub = {
enable = true;
device = "/dev/sda";
};
}

View file

@ -0,0 +1,118 @@
{ config, pkgs, ... }:
let
meta = config.qois.meta;
plessur-dmz-net = meta.network.physical.plessur-dmz;
plessur-lan-net = meta.network.physical.plessur-lan;
getCalandaIp4 = net: net.hosts.calanda.v4.ip;
in
{
imports = [ ../../defaults/backplane-net ];
networking.hostName = meta.hosts.calanda.hostName;
networking.domain = "ilanz.fh2.ch";
networking.enableIPv6 = false; # TODO
networking.useDHCP = false;
networking.interfaces.enp4s0.useDHCP = true;
networking.firewall.allowedTCPPorts = [
80
443
];
networking.interfaces.enp3s0 = {
ipv4.addresses = [
{
inherit (plessur-dmz-net.v4) prefixLength;
address = getCalandaIp4 plessur-dmz-net;
}
];
};
# TODO: Metaize ips
services.qois.router = {
enable = true;
wanInterface = "enp4s0";
wirelessInterfaces = [ "wlp5s0" ];
lanInterfaces = [ "enp2s0" ];
internalRouterIP = getCalandaIp4 plessur-lan-net;
dhcp = {
enable = true;
localDomain = "ilanz.fh2.ch"; # TODO: Legacy hostname
dhcpRange = "10.1.1.2,10.1.1.249";
};
recursiveDns = {
enable = true;
networkIdIp = plessur-lan-net.v4.id;
};
wireless = {
enable = true;
wleInterface24Ghz = "wlp5s0";
ssid = "hauser";
};
};
# DMZ
services.unbound.settings.server = {
interface = [ plessur-dmz-net.hosts.calanda.v4.ip ];
access-control = [
''"${plessur-dmz-net.v4.id}/${toString plessur-dmz-net.v4.prefixLength}" allow''
];
};
networking.firewall.interfaces.enp3s0.allowedUDPPorts = [ 53 ];
networking.nat.internalInterfaces = [ "enp3s0" ];
# DMZ Portforwarding
networking.nat.forwardPorts =
let
fulbergPort = (
proto: port: {
destination = "10.1.2.2:${toString port}";
proto = proto;
sourcePort = port;
loopbackIPs = [ "85.195.200.253" ];
}
);
cyprianspitzPort = (
proto: port: {
destination = "10.1.1.11:${toString port}";
proto = proto;
sourcePort = port;
loopbackIPs = [ "85.195.200.253" ];
}
);
in
[
{
destination = "10.1.2.2:22";
proto = "tcp";
sourcePort = 8022;
}
{
destination = "10.1.2.2:2222";
proto = "tcp";
sourcePort = 8222;
}
{
destination = "10.1.1.11:2222";
proto = "tcp";
sourcePort = 8223;
}
]
++ map (fulbergPort "udp") [
51820
51821
]
++ map (cyprianspitzPort "tcp") [
80
443
]
++ map (cyprianspitzPort "udp") [
51824
1666
41641
3478
3479
];
}

View file

@ -0,0 +1,32 @@
# Host: Cyprianspitz
## Operations {#_operations}
Reboot requires passphrase.
``` bash
# Get HDD Password:
sops decrypt --extract '["system"]["hdd"]' private/nixos-configurations/cyprianspitz/secrets.sops.yaml
ssh -p 8223 -J root@calanda.plessur-ext.net.qo.is
```
## Hardware
TODO
- [Mainboard Manual](docs/z790m-itx-wifi.pdf)
### Top Overview
![](docs/top-view.jpg)
### PCIE Side
![](docs/pcie-side.jpg)
### HDD Bay
Note that slot 5 (the leftmost) SATA bay is not connected due to the mainboard only having 4 SATA plugs.

View file

@ -0,0 +1,12 @@
{ pkgs, config, ... }:
{
qois.backup-server = {
enable = true;
backupStorageRoot =
let
dataDrive = config.disko.devices.lvm_vg.vg_data.lvs.lv_data.content.mountpoint;
in
dataDrive + "/backup";
};
}

View file

@ -0,0 +1,10 @@
{ config, pkgs, ... }:
{
imports = [
./backup.nix
./vpn.nix
];
qois.loadbalancer.enable = true;
}

View file

@ -0,0 +1,4 @@
{ config, pkgs, ... }:
{
qois.vpn-server.enable = true;
}

View file

@ -0,0 +1,28 @@
{ config, pkgs, ... }:
{
imports = [
./applications
./disko-config.nix
./filesystems.nix
./networking.nix
./secrets.nix
./virtualisation.nix
../../defaults/hardware/asrock-z790m.nix
../../defaults/base
../../defaults/meta
];
# Set your time zone.
time.timeZone = "Europe/Amsterdam";
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "24.05"; # Did you read the comment?
}

View file

@ -0,0 +1,132 @@
{ pkgs, ... }:
{
disko.devices = {
disk = rec {
data-1 = {
type = "disk";
device = "/dev/disk/by-id/ata-ST16000NM000J-2TW103_ZRS110XA";
content = {
type = "gpt";
partitions = {
raid_data = {
size = "100%";
content = {
type = "mdraid";
name = "raid_data";
};
};
};
};
};
#data-2 = { # TODO
# type = "disk";
# device = "/dev/disk/by-id/ata-TODO";
# content = data-1.content;
#};
system-1 = {
type = "disk";
device = "/dev/disk/by-id/nvme-Lexar_SSD_NM790_1TB_NL8052R000144P2202";
content = {
type = "gpt";
partitions = {
boot = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot-primary";
};
};
raid_system = {
start = "5G";
size = "100%";
content = {
type = "mdraid";
name = "raid_system";
};
};
};
};
};
system-2 = {
type = "disk";
device = "/dev/disk/by-id/nvme-Lexar_SSD_NM790_1TB_NL8052R002402P2202";
content = pkgs.lib.recursiveUpdate system-1.content {
partitions.boot.content.mountpoint = "/boot-secondary";
};
};
};
mdadm = {
"raid_system" = {
type = "mdadm";
level = 1;
content = {
type = "luks";
name = "crypted_system";
passwordFile = "/run/secrets/system/hdd.key";
settings = {
allowDiscards = true;
bypassWorkqueues = true;
};
content = {
type = "lvm_pv";
vg = "vg_system";
};
};
};
"raid_data" = {
type = "mdadm";
level = 1;
content = {
type = "luks";
name = "crypted_data";
passwordFile = "/run/secrets/system/hdd.key";
settings.allowDiscards = true;
content = {
type = "lvm_pv";
vg = "vg_data";
};
};
};
};
lvm_vg = {
vg_data = {
type = "lvm_vg";
lvs = {
lv_data = {
size = "14TB";
content = {
type = "filesystem";
format = "btrfs";
mountpoint = "/mnt/data";
mountOptions = [
"defaults"
"noatime"
];
};
};
};
};
vg_system = {
type = "lvm_vg";
lvs = {
hv_cyprianspitz = {
size = "100GiB";
content = {
type = "btrfs";
mountOptions = [
"defaults"
"noatime"
];
subvolumes = {
"/root".mountpoint = "/";
};
};
};
};
};
};
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View file

@ -0,0 +1,36 @@
{ config, pkgs, ... }:
{
# Configurations are set in disko-config.nix!
# mdadm.conf generated by `mdadm --detail --scan`
# TODO
boot.swraid.enable = true;
boot.swraid.mdadmConf = ''
MAILADDR root
'';
# TODO: RAID Monitoring
# TODO: Set spin-down time of physical disks
services.fwupd.daemonSettings.EspLocation = pkgs.lib.mkForce config.disko.devices.disk.system-1.content.partitions.boot.content.mountpoint;
# Use the systemd-boot EFI boot loader.
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.grub = {
enable = true;
efiSupport = true;
mirroredBoots = [
{
devices = [ "nodev" ];
path = "/boot-primary";
efiBootloaderId = "NixOS primary";
}
{
devices = [ "nodev" ];
path = "/boot-secondary";
efiBootloaderId = "NixOS secondary";
}
];
};
}

View file

@ -0,0 +1,97 @@
{ config, pkgs, ... }:
let
meta = config.qois.meta;
in
{
networking.hostName = meta.hosts.cyprianspitz.hostName;
imports = [ ../../defaults/backplane-net ];
networking.useDHCP = false;
networking.interfaces.enp0s31f6.useDHCP = true;
networking.interfaces.enp2s0.useDHCP = true;
# Virtualization
networking.interfaces.vms-nat.useDHCP = false;
networking.interfaces.vms-nat.ipv4.addresses = [
(
let
netConfig = meta.network.virtual.cyprianspitz-vms-nat;
in
{
address = netConfig.hosts.cyprianspitz.v4.ip;
prefixLength = netConfig.v4.prefixLength;
}
)
];
networking.bridges.vms-nat.interfaces = [ ];
networking.nat = {
enable = true;
internalInterfaces = [ "vms-nat" ];
internalIPs = with meta.network.virtual.cyprianspitz-vms-nat.v4; [
"${id}/${builtins.toString prefixLength}"
];
externalInterface = "enp0s31f6";
};
services.dnsmasq =
let
netConfig = meta.network.virtual.cyprianspitz-vms-nat;
in
{
enable = true;
resolveLocalQueries = false;
settings = {
interface = "vms-nat";
bind-interfaces = true;
domain-needed = true;
domain = netConfig.domain;
dhcp-range = [ "10.248.0.2,10.248.0.253" ];
dhcp-option = [
"option:router,${netConfig.hosts.cyprianspitz.v4.ip}"
"option:domain-search,${netConfig.domain}"
];
dhcp-authoritative = true;
};
};
systemd.services.dnsmasq.bindsTo = [ "network-addresses-vms-nat.service" ];
networking.firewall.interfaces.vms-nat = {
allowedUDPPorts = [
53
67
];
allowedTCPPorts = [ 53 ];
};
# Boot
boot.initrd.network.udhcpc.enable = true;
services.qois.luks-ssh = {
enable = true;
interface = "eth0";
sshPort = 2222;
sshHostKey = "/secrets/system/initrd-ssh-key";
# TODO Solve sops dependency porblem: config.sops.secrets."system/initrd-ssh-key".path;
};
# Configure this node to be used as an vpn exit node
qois.backup-client.includePaths = [ "/var/lib/tailscale" ];
services.tailscale = {
enable = true;
openFirewall = true;
useRoutingFeatures = "server";
authKeyFile = config.sops.secrets."tailscale/key".path;
extraUpFlags = [
"--login-server=https://vpn.qo.is"
"--advertise-exit-node"
(
with meta.network.virtual.backplane.v4; "--advertise-routes=${id}/${builtins.toString prefixLength}"
)
"--advertise-tags=tag:srv"
];
};
}

View file

@ -0,0 +1,10 @@
{ ... }:
{
sops.secrets = {
"system/hdd" = { };
"system/initrd-ssh-key" = { };
"tailscale/key" = {
restartUnits = [ "tailscaled.service" ];
};
};
}

View file

@ -0,0 +1,8 @@
{ config, pkgs, ... }:
{
virtualisation.libvirtd = {
enable = true;
onShutdown = "shutdown";
};
environment.systemPackages = [ pkgs.virtiofsd ];
}

View file

@ -0,0 +1,41 @@
{
self,
pkgs,
nixpkgs-nixos-stable,
disko,
attic,
sops-nix,
...
}@inputs:
let
configs = self.lib.foldersWithNix ./.;
in
pkgs.lib.genAttrs configs (
config:
nixpkgs-nixos-stable.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {
inherit inputs;
};
modules = [
self.nixosModules.default
./${config}/default.nix
(
{ ... }:
{
imports = [ "${attic}/nixos/atticd.nix" ];
services.atticd.useFlakeCompatOverlay = false;
}
)
disko.nixosModules.disko
sops-nix.nixosModules.sops
(
{ ... }:
{
system.extraSystemBuilderCmds = "ln -s ${self} $out/nixos-configuration";
imports = [ ./secrets.nix ];
}
)
];
}
)

View file

@ -0,0 +1 @@
# fulberg

View file

@ -0,0 +1 @@
{ ... }: { }

View file

@ -0,0 +1,35 @@
{ config, pkgs, ... }:
{
qois.backup-server = {
enable = true;
backupStorageRoot = "/mnt/nas/backup";
};
services.borgbackup.repos =
let
backupRoot = "/mnt/nas/backup";
hostBackupRoot = "${backupRoot}/hosts";
dataBackupRoot = "${backupRoot}/data";
in
{
"lindberg-nextcloud" = {
authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIpzfp9VqclbPJ42ZrkRpvjMSTeyq0qce03zCRXqIHMw backup@lindberg-nextcloud"
];
path = "${hostBackupRoot}/lindberg-nextcloud";
};
"lindberg-data" = {
authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGTmyoVONC12MgOodvzdPpZzLSVwpkC6zkf+Rg0W36gy backup-data@lindberg"
];
path = "${dataBackupRoot}/lindberg";
};
"lindberg-build-system" = {
authorizedKeys = [
"ssh-ed25519 AAAATODOTODOTODONTE5AAAAIGTmyoVONC12MgOodvzdPpZzLSVwpkC6zkf+Rg0W36gy backup-system@lindberg-build"
];
path = "${dataBackupRoot}/lindberg-build-system";
};
};
}

View file

@ -0,0 +1,22 @@
{ config, pkgs, ... }:
{
imports = [
../../defaults/base
../../defaults/hardware/apu.nix
../../defaults/meta
./applications
./backup.nix
./filesystems.nix
./networking.nix
./secrets.nix
];
# This value determines the NixOS release from which the default
# settings for stateful data, like fi:le locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "23.05"; # Did you read the comment?
}

View file

@ -0,0 +1,31 @@
{ config, pkgs, ... }:
{
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/360a6bc9-fc4e-4803-bd53-69320ac32ac5";
fsType = "btrfs";
options = [
"defaults"
"subvol=nixos"
"noatime"
];
};
"/mnt/nas" = {
device = "10.1.1.39:/qois";
fsType = "nfs";
options = [
"defaults"
"noatime"
"soft"
];
};
};
swapDevices = [ { device = "/dev/disk/by-uuid/73f91e99-d856-4504-b6b2-d60f855d6d95"; } ];
boot.loader.grub = {
enable = true;
device = "/dev/sda";
};
}

View file

@ -0,0 +1,48 @@
{ config, pkgs, ... }:
let
meta = config.qois.meta;
plessur-dmz-net = meta.network.physical.plessur-dmz;
getCalandaIp4 = net: net.hosts.calanda.v4.ip;
in
{
networking.hostName = meta.hosts.fulberg.hostName;
imports = [ ../../defaults/backplane-net ];
# WWAN is currently not available due to a broken SIM-card.
#services.qois.wwan = {
# enable = true;
# apn = "gprs.swisscom.ch";
# networkInterface = "wwp0s19u1u3i12";
#};
networking.interfaces.enp1s0 = {
useDHCP = false;
ipv4.addresses = [
{
inherit (plessur-dmz-net.v4) prefixLength;
address = plessur-dmz-net.hosts.fulberg.v4.ip;
}
];
};
networking.defaultGateway = plessur-dmz-net.v4.gateway;
networking.nameservers = plessur-dmz-net.v4.nameservers;
# Configure this node to be used as an vpn exit node
qois.backup-client.includePaths = [ "/var/lib/tailscale" ];
services.tailscale = {
enable = true;
openFirewall = true;
useRoutingFeatures = "server";
authKeyFile = config.sops.secrets."tailscale/key".path;
extraUpFlags = [
"--login-server=https://vpn.qo.is"
"--advertise-exit-node"
(
with meta.network.virtual.backplane.v4; "--advertise-routes=${id}/${builtins.toString prefixLength}"
)
"--advertise-tags=tag:srv"
];
};
}

View file

@ -0,0 +1,8 @@
{ ... }:
{
sops.secrets = {
"tailscale/key" = {
restartUnits = [ "tailscale.service" ];
};
};
}

View file

@ -0,0 +1,39 @@
# Nix Caches
## Nixpkgs Cache
To put less load on the upstream nixpkgs CDN and speed up builds, we run a (public) nixpkgs cache on [nixpkgs-cache.qo.is](https://nixpkgs-cache.qo.is). To use it, configure nix like follows in your `nix.conf`:
```nix
substituters = https://nixpkgs-cache.qo.is?priority=39
```
Note that the [cache.nixos.org](https://cache.nixos.org) public key must also be trusted:
```nix
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
```
See the [nix documentation](https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-substituters) for details about substitutors.
## Attic
We use [attic](https://docs.attic.rs/) as a self hosted nix build cache.
See [upstream documentation](https://docs.attic.rs/reference/attic-cli.html) for details on how to use it.
### Server Administration
Add users:
```bash
# For example, to generate a token for Alice with read-write access to any cache starting with `dev-` and read-only access to `prod`, expiring in 2 years:
atticadm make-token --sub "alice" --validity "2y" --pull "dev-*" --push "dev-*" --pull "prod"
```
### Client Usage
`attic login qois https://attic.qo.is <TOKEN_HERE>`
`attic use qois:cachename`

View file

@ -0,0 +1,77 @@
{ config, pkgs, ... }:
let
atticPort = 8080;
atticHostname = "attic.qo.is";
in
{
services.atticd = {
enable = true;
# Replace with absolute path to your credentials file
# generate secret with
# nix run system#openssl rand 64 | base64 -w0
# ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64="output from openssl"
credentialsFile = config.sops.secrets."attic/server_token".path;
settings = {
listen = "127.0.0.1:${builtins.toString atticPort}";
allowed-hosts = [ "attic.qo.is" ];
api-endpoint = "https://attic.qo.is/";
# Data chunking
#
# Warning: If you change any of the values here, it will be
# difficult to reuse existing chunks for newly-uploaded NARs
# since the cutpoints will be different. As a result, the
# deduplication ratio will suffer for a while after the change.
chunking = {
# The minimum NAR size to trigger chunking
#
# If 0, chunking is disabled entirely for newly-uploaded NARs.
# If 1, all NARs are chunked.
nar-size-threshold = 64 * 1024; # 64 KiB
# The preferred minimum size of a chunk, in bytes
min-size = 16 * 1024; # 16 KiB
# The preferred average size of a chunk, in bytes
avg-size = 64 * 1024; # 64 KiB
# The preferred maximum size of a chunk, in bytes
max-size = 256 * 1024; # 256 KiB
};
database.url = "postgresql:///atticd?host=/run/postgresql";
};
};
imports = [ ../../../defaults/webserver ];
qois.postgresql.enable = true;
# Note: Attic cache availability is "best effort", so no artifacts are backed up.
services.postgresql = {
ensureDatabases = [ "atticd" ];
ensureUsers = [
{
name = "atticd";
ensureDBOwnership = true;
}
];
};
services.nginx = {
enable = true;
clientMaxBodySize = "1g";
virtualHosts.${atticHostname} = {
kTLS = true;
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://127.0.0.1:${builtins.toString atticPort}";
};
};
}

View file

@ -0,0 +1,11 @@
{ config, pkgs, ... }:
{
imports = [
./gitlab-runner.nix
./attic.nix
./nixpkgs-cache.nix
];
qois.git-ci-runner.enable = true;
}

View file

@ -0,0 +1,27 @@
{ config, pkgs, ... }:
{
services.gitlab-runner = {
enable = true;
gracefulTimeout = "20min";
clear-docker-cache = {
enable = true;
dates = "monthly";
};
services = {
default = {
runUntagged = true;
# File should contain at least these two variables:
# `CI_SERVER_URL`
# `REGISTRATION_TOKEN`
registrationConfigFile = config.sops.secrets."gitlab-runner/default-registration".path;
dockerImage = "debian:stable";
limit = 42; # The magic value
maximumTimeout = 7200; # 2h oughta be enough for everyone
};
};
};
}

View file

@ -0,0 +1,8 @@
{ config, pkgs, ... }:
{
qois.nixpkgs-cache = {
enable = true;
hostname = "nixpkgs-cache.qo.is";
dnsResolvers = [ config.qois.meta.network.virtual.lindberg-vms-nat.hosts.lindberg.v4.ip ];
};
}

View file

@ -0,0 +1,43 @@
{ config, pkgs, ... }:
let
vnet = config.qois.meta.network.virtual.backplane.hosts;
systemTargets = [
"fulberg"
"tierberg"
];
systemJobs = builtins.listToAttrs (
map (backupHost: {
name = "system-${backupHost}";
value = {
repo = "borg@${vnet.${backupHost}.v4.ip}:.";
environment.BORG_RSH = "ssh -i /secrets/backup/system/ssh-key";
paths = [
"/etc"
"/home"
"/var"
"/secrets"
];
exclude = [
"/var/tmp"
"/var/cache"
"/var/lib/atticd"
"/var/cache/nginx/nixpkgs-cache"
];
doInit = false;
encryption = {
mode = "repokey";
passCommand = "cat /secrets/backup/system/password";
};
startAt = "07:06";
persistentTimer = true;
};
}) systemTargets
);
in
{
services.borgbackup.jobs = systemJobs;
}

View file

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
{
imports = [
../../defaults/base-vm
../../defaults/meta
../../defaults/backplane-net
./applications
./backup.nix
./disko-config.nix
./networking.nix
./secrets.nix
];
# Set your time zone.
time.timeZone = "Europe/Amsterdam";
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "23.11"; # Did you read the comment?
}

View file

@ -0,0 +1,64 @@
{ ... }:
{
disko.devices.disk = {
system = {
type = "disk";
device = "/dev/vda";
content = {
type = "gpt";
partitions = {
boot = {
# for grub MBR
size = "1M";
type = "EF02";
};
system = {
size = "100%";
content = {
type = "btrfs";
subvolumes = {
"/nix" = {
mountpoint = "/nix";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"/root" = {
mountpoint = "/";
mountOptions = [ "noatime" ];
};
};
};
};
};
};
};
nixpkgs_cache = {
type = "disk";
device = "/dev/vdb";
content = {
type = "gpt";
partitions.nixpkgs_cache = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/var/cache/nginx/nixpkgs-cache";
};
};
};
};
swap = {
type = "disk";
device = "/dev/vdc";
content = {
type = "gpt";
partitions.swap = {
size = "100%";
content.type = "swap";
};
};
};
};
}

View file

@ -0,0 +1,13 @@
{ config, pkgs, ... }:
{
networking.hostName = config.qois.meta.hosts.lindberg-build.hostName;
networking.useDHCP = false;
networking.interfaces.enp11s0.useDHCP = true;
networking.firewall.allowedTCPPorts = [
80
443
];
}

View file

@ -0,0 +1,11 @@
{ ... }:
{
sops.secrets = {
"attic/server_token" = {
restartUnits = [ "atticd.service" ];
};
"gitlab-runner/default-registration" = {
restartUnits = [ "gitlab-runner.service" ];
};
};
}

View file

@ -0,0 +1,21 @@
{ config, pkgs, ... }:
let
host = "cloud.qo.is";
in
{
imports = [ ../../../defaults/nextcloud ];
qois.postgresql.enable = true;
services.nextcloud = {
hostName = host;
package = pkgs.nextcloud29;
settings.default_phone_region = "CH";
};
services.nginx.virtualHosts."${host}" = {
forceSSL = true;
enableACME = true;
kTLS = true;
};
}

View file

@ -0,0 +1,5 @@
{ config, pkgs, ... }:
{
imports = [ ./cloud.nix ];
}

View file

@ -0,0 +1,8 @@
{ config, pkgs, ... }:
{
qois.backup-client.excludePaths = [
"/var/lib/nextcloud/data" # Data is backed up on lindberg
];
}

View file

@ -0,0 +1,50 @@
{ config, pkgs, ... }:
{
imports = [
../../defaults/backplane-net
../../defaults/base-vm
../../defaults/meta
../../defaults/webserver
./applications
./backup.nix
./secrets.nix
];
boot.loader.grub.device = "/dev/vda";
fileSystems."/" = {
device = "/dev/disk/by-uuid/5b6823ec-921f-400a-a7c0-3fe34d56ae12";
fsType = "btrfs";
options = [ "subvol=root" ];
};
systemd.mounts = [
{
what = "data/nextcloud";
where = "/var/lib/nextcloud";
type = "virtiofs";
wantedBy = [ "multi-user.target" ];
enable = true;
}
];
networking.hostName = config.qois.meta.hosts.lindberg-nextcloud.hostName;
networking.useDHCP = false;
networking.interfaces.enp2s0.useDHCP = true;
networking.firewall.allowedTCPPorts = [
80
443
];
# Set your time zone.
time.timeZone = "Europe/Amsterdam";
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "22.05"; # Did you read the comment?
}

View file

@ -0,0 +1,17 @@
{ ... }:
let
backupConfiguration = {
restartUnits = [
"borgbackup-job-system-fulberg.service"
"borgbackup-job-system-tierberg.service"
];
};
in
{
sops.secrets = {
"backup/system/password" = backupConfiguration;
"backup/system/ssh-key" = backupConfiguration;
"nextcloud/admin" = { };
};
}

View file

@ -0,0 +1,3 @@
# Web Apps
## fabianhauser.ch

View file

@ -0,0 +1,9 @@
{ config, pkgs, ... }:
{
imports = [ ];
qois.vault.enable = true;
qois.git.enable = true;
qois.static-page.enable = true;
}

View file

@ -0,0 +1,25 @@
{ config, pkgs, ... }:
{
imports = [
../../defaults/base-vm
../../defaults/meta
../../defaults/backplane-net
./applications
./disko-config.nix
./networking.nix
./secrets.nix
];
# Set your time zone.
time.timeZone = "Europe/Amsterdam";
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "23.11"; # Did you read the comment?
}

View file

@ -0,0 +1,38 @@
{ ... }:
{
disko.devices.disk = {
system = {
type = "disk";
device = "/dev/vda";
content = {
type = "gpt";
partitions = {
boot = {
# for grub MBR
size = "1M";
type = "EF02";
};
system = {
size = "100%";
content = {
type = "btrfs";
subvolumes = {
"/nix" = {
mountpoint = "/nix";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"/root" = {
mountpoint = "/";
mountOptions = [ "noatime" ];
};
};
};
};
};
};
};
};
}

View file

@ -0,0 +1,13 @@
{ config, pkgs, ... }:
{
networking.hostName = config.qois.meta.hosts.lindberg-webapps.hostName;
networking.useDHCP = false;
networking.interfaces.enp1s0.useDHCP = true;
networking.firewall.allowedTCPPorts = [
80
443
];
}

View file

@ -0,0 +1,4 @@
{ ... }:
{
sops.secrets = { };
}

View file

@ -0,0 +1,61 @@
# Host: Lindberg
## Operations {#_operations}
Reboot requires passphrase (see pass `host/lindberg/hdd_luks`)
``` bash
ssh -p 2222 root@lindberg.riedbach-ext.net.qo.is
```
## Hardware
- [Mainboard Manual](docs/X570Pro4-mainboard-manual.pdf)
### Front / Back
#### Front Overview
![](docs/front_full.jpg)
#### Front PCIE
![](docs/front_pcie_overview.jpg)
![](docs/front_pcie_ssd.jpg)
#### Front Cables
![](docs/front_cables.jpg)
#### Back
![](docs/back_overview.jpg)
### HDDs
![](docs/back_hdds.jpg)
#### HDD (0)
![](docs/hdd_0.jpg)
#### HDD (1)
![](docs/hdd_1.jpg)
#### HDD (3)
![](docs/hdd_3.jpg)
#### zvtaa02h
![](docs/hdd_zvtaa02h.jpg)
#### zvtaeypl
![](docs/hdd_zvtaeypl.jpg)
### SSD left
![](docs/ssd_1_left.jpg)

View file

@ -0,0 +1,5 @@
{ config, pkgs, ... }:
{
imports = [ ./loadbalancer.nix ];
}

Some files were not shown because too many files have changed in this diff Show more