Compare commits

..

1 commit

Author SHA1 Message Date
2152d863c0 Add caral documentation
All checks were successful
CI / build (push) Successful in 3m26s
2025-01-06 16:31:13 +02:00
137 changed files with 1364 additions and 1562 deletions

View file

@ -1,12 +1,11 @@
name: CI
on:
push:
defaults:
run:
shell: nix develop --command bash -c "{0}"
env:
CACHE_NAME: qois
CACHE_REPOSITORY: qois:qois-infrastructure
ATTIC_AUTH_TOKEN: ${{ secrets.ATTIC_AUTH_TOKEN }}
jobs:
build:
runs-on: nix
@ -16,20 +15,31 @@ jobs:
with:
token: ${{ secrets.CI_TOKEN }}
lfs: false
- name: Setup Attic Cache
env:
SERVER: https://attic.qo.is/
ATTIC_AUTH_TOKEN: ${{ secrets.ATTIC_AUTH_TOKEN }}
- name: Add submodules to nix store to circumvent another nix bug
run: |
attic login "$CACHE_NAME" "$SERVER" "$ATTIC_AUTH_TOKEN"
attic use "$CACHE_REPOSITORY"
- name: Run Builds and Checks
run: nix-fast-build --no-nom --max-jobs 6 --skip-cached --attic-cache "$CACHE_REPOSITORY"
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 build --max-jobs 12 --cores 12
nix run .#cache push
- name: Run Checks
run: nix flake check
- name: Deploy Docs
if: success() && github.ref == 'refs/heads/main'
run: |
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)
deploy --skip-checks --remote-build .#lindberg-webapps.\"docs-ops.qo.is\"
# Remote build is neccessary due to non-wheel nix users signing restrictions. However, the build should come from the cache anyway.
# 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\"

2
.gitignore vendored
View file

@ -5,5 +5,3 @@ result*
/.direnv
/book
/.sops.yaml
/.nixos-test-history
/.pre-commit-config.yaml

View file

@ -1,10 +1,7 @@
{
"eval": {
"target": {
"args": [
"-f",
"default.nix"
],
"args": ["-f", "default.nix"],
"installable": ""
}
},

View file

@ -6,65 +6,63 @@ Check out the current [rendered documentation](https://docs-ops.qo.is).
## Structure
`nixos-configurations`: Main nixos configuration for every host.\
`defaults`: Configuration defaults\
`nixos-modules`: Custom modules (e.g. for vpn and routers)\
`nixos-configurations`: Main nixos configuration for every host.
`defaults`: Configuration defaults
`nixos-modules`: Custom modules (e.g. for vpn and routers)
`private`: Private configuration values (like users, sops-encrypted secrets and keys)
## Development
## Building
This repository requires [nix flakes](https://nixos.wiki/wiki/Flakes)
- `nix flake check`\
Execute the project's checks, which includes building all configurations and packages. See [Tests](./checks/README.md).
- `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
- `nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel`\
Build a single host configuration.
## Development
- `nix build .#docs`\
Build the documentation website.
- `nix develop`\
- `nix develop`
Development environment
- `nix fmt`\
- `nix flake check`
Execute the project's checks
- `nix fmt`
Autofix formatting
### Secrets and `private` Submodule
### Working with the private submodule
Secret management is done with [nix-sops](https://github.com/Mic92/sops-nix) and a git submodule in `private`.\
Make sure you have the submodule correctly available. To clone with submodules (if you have access):
To clone with submodules (if you have access):
```bash
git clone --recurse-submodules https://git.qo.is/qo.is/infrastructure.git
# See below for how to commit changes.
```
Secrets are stored in `private/passwords.sops.yaml` (sysadmin passwords),
`private/nixos-modules/shared-secrets/default.sops.yaml` (shared secrets for all hosts) and
`private/nixos-configurations/<hostname>/secrets.sops.yaml` (host specific secrets).
To modify secrets:
On changes:
```bash
sops $file # To edit a file
sops-rekey # To rekey all secrets, e.g. after a key rollover or new host
```
After changing secrets:
```bash
# Commit changes in subrepo
pushd private
git commit
git push
nix flake prefetch . # Make subrepo available in nix store. Required until nix 2.27.
popd
git add private
nix flake lock --update-input private
```
## Deployment
`nix run .#deploy-qois`
See [Deployment](deploy/README.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 $file # To edit a file
sops-rekey # To rekey all secrets, e.g. after a key rollover or new host
```

View file

@ -1,10 +1,9 @@
# Summary
- [Repository README](README.md)
- [Testing](checks/README.md)
- [Deployment](deploy/README.md)
______________________________________________________________________
---
- [Network Topology](defaults/meta/network.md)
- [Hardware (generic)](defaults/hardware/README.md)
@ -12,15 +11,16 @@ ______________________________________________________________________
- [Updates](updates.md)
- [New Host Setup](nixos-configurations/setup.md)
# Services
- [E-mail](email.md)
- [Git CI Runner](nixos-modules/git-ci-runner/README.md)
- [Git Hosting](nixos-modules/git/README.md)
- [Nextcloud](nixos-modules/cloud/README.md)
- [Git CI Runner](nixos-modules/qois/git-ci-runner/README.md)
- [Git Hosting](nixos-modules/qois/git/README.md)
- [Nextcloud](defaults/nextcloud/README.md)
- [Nix Caches](nixos-configurations/lindberg-build/applications/README.md)
- [Static Pages](nixos-modules/static-page/README.md)
- [VPN](nixos-modules/vpn-server/README.md)
- [Static Pages](nixos-modules/qois/static-page/README.md)
- [VPN](defaults/vpn/README.md)
- [Vaultwarden](nixos-modules/vault/README.md)
# Nixos Configurations
@ -29,3 +29,4 @@ ______________________________________________________________________
- [cyprianspitz](nixos-configurations/cyprianspitz/README.md)
- [lindberg](nixos-configurations/lindberg/README.md)
- [stompert](nixos-configurations/stompert/README.md)

View file

@ -1,25 +0,0 @@
# Tests
`nix flake check` currently:
- builds all nixos-configurations
- builds all packages
- runs all [nixos-module tests](#module-tests)
- checks all deployment configurations
- checks repository formatting.
## Module Tests
We test our nixos modules with [NixOS tests](https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests).
Running nixos tests requires QEMU virtualisation, so make sure you have KVM virtualisation support enabled.
Run all: `nix build .#checks.x86_64-linux.nixos-modules`\
Run single test: `nix build .#checks.x86_64-linux.nixos-modules.entries.vm-test-run-testNameAsInDerivationName`
### Run Test Interactively
```bash
nix run .#checks.x86_64-linux.nixos-modules.entries.vm-test-run-testNameAsInDerivationName.driverInteractive
```
See [upstream documentation](https://nixos.org/manual/nixos/stable/#sec-running-nixos-tests-interactively) for more details.

View file

@ -1,22 +1,22 @@
{
self,
flakeSelf,
system,
pkgs,
deployPkgs,
treefmtEval,
...
}@inputs:
{
${system} = {
formatting = treefmtEval.config.build.check flakeSelf;
nixos-modules = pkgs.callPackage ./nixos-modules {
defaultModule = self.nixosModules.default;
inherit (self.lib) getSubDirs isFolderWithFile;
};
# Check project formatting
format = pkgs.runCommand "nixfmt-check" { } ''
set -euo pipefail
cd ${self}
${self.formatter.${system}}/bin/formatter . --check
mkdir $out
'';
nixos-configurations = import ./nixos-configurations inputs;
#TODO(#29): Integration/System tests
# Import deploy-rs tests
} // (deployPkgs.deploy-rs.lib.deployChecks self.deploy);

View file

@ -1,4 +0,0 @@
{ self, pkgs, ... }:
pkgs.linkFarmFromDrvs "all" (
pkgs.lib.mapAttrsToList (_n: v: v.config.system.build.toplevel) self.nixosConfigurations
)

View file

@ -1,68 +0,0 @@
{
linkFarmFromDrvs,
isFolderWithFile,
getSubDirs,
lib,
testers,
defaultModule,
}:
let
inherit (lib)
filter
path
mkDefault
mkForce
readFile
attrNames
concatStringsSep
pipe
;
modulesBaseDir = ../../nixos-modules;
mkTest =
name:
let
getFilePath = file: path.append modulesBaseDir "./${name}/${file}";
in
testers.runNixOSTest {
inherit name;
imports = [
(import (getFilePath "test.nix") {
inherit name;
inherit lib;
})
];
defaults = {
imports = [ defaultModule ];
qois.outgoing-server-mail.enable = mkForce false;
qois.backup-client.enable = mkForce false;
};
# Calls a `test(...)` python function in the test's python file with the list of nodes and helper functions.
# Helper symbols may be added as function args when needed and can be found in:
# https://github.com/NixOS/nixpkgs/blob/master/nixos/lib/test-driver/src/test_driver/driver.py#L121
testScript = mkDefault (
{ nodes, ... }:
let
script = readFile (getFilePath "test.py");
nodeArgs = pipe nodes [
attrNames
(map (val: "${val}=${val}"))
(concatStringsSep ", ")
];
in
''
${script}
test(${nodeArgs}, subtest=subtest)
''
);
};
in
pipe modulesBaseDir [
getSubDirs
(filter (isFolderWithFile "test.nix" modulesBaseDir))
(map mkTest)
(linkFarmFromDrvs "nixos-modules")
]

View file

@ -1,5 +0,0 @@
{ self, pkgs, ... }:
let
inherit (pkgs.lib) attrValues;
in
pkgs.linkFarmFromDrvs "all" (attrValues self.packages)

View file

@ -1,4 +1,6 @@
{
config,
lib,
pkgs,
...
}:

View file

@ -2,15 +2,15 @@
config,
lib,
pkgs,
inputs,
...
}:
{
imports = [
./unfree.nix
./applications.nix
./overlays.nix
./physical.nix
./security.nix
./virtual-machine.nix
];
boot.loader.timeout = 2;
@ -55,9 +55,9 @@
settings =
let
substituters = [
"https://${config.qois.nixpkgs-cache.hostname}?priority=30"
"https://attic.qo.is/qois-infrastructure?priority=32"
"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"
];
in
{
@ -69,7 +69,7 @@
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"qois-infrastructure:lh35ymN7Aoxm5Hz0S6JusxE+cYzMU+x9OMKjDVIpfuE="
];
trusted-substituters = substituters;
trusted-substituters = substituters; # For hosts that limit the subst list
inherit substituters;
};
gc = {
@ -111,6 +111,12 @@
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;

View file

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

View file

@ -1,5 +1,7 @@
{
config,
lib,
pkgs,
...
}:
with lib;

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

@ -1,3 +1,4 @@
# APU
## Setup
@ -6,5 +7,9 @@ To boot the nixos installer with the console port, add `console=ttyS0,115200n8`
# ASROCK Mainboards
`F2`: Boot into BIOS\
`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)

View file

@ -2,7 +2,9 @@
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:

View file

@ -2,7 +2,9 @@
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:

View file

@ -2,7 +2,9 @@
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:

View file

@ -1,5 +1,7 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:

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]
'';
}

View file

@ -1,4 +1,7 @@
{
config,
lib,
pkgs,
...
}:
{

View file

@ -1,4 +1,7 @@
{
config,
lib,
pkgs,
...
}:
{

View file

@ -1,5 +1,7 @@
{
config,
lib,
pkgs,
...
}:
{

View file

@ -74,6 +74,7 @@ All Services are published under the *qo.is* domain name. Following services are
## Contacts
### Init7
- [Status Netzwerkdienste](https://www.init7.net/status/)

View file

@ -11,7 +11,7 @@ For user documentation, refer to the [upstream Nextcloud docs](https://docs.next
## Backup / Restore
1. Stop all related services: nextcloud, php-fpm, redis etc.
1. (mabe dump redis data?)
1. Import Database Backup
1. Restore `/var/lib/nextcloud`, which is currently a bind mount on `lindberg`'s `/mnt/data` volume
1. Resync nextcloud files and database, see [nextcloud docs](https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html)
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,99 @@
# Default configuration for hosts
{
config,
lib,
pkgs,
...
}:
{
sops.secrets."nextcloud/admin" = with config.users.users.nextcloud; {
inherit group;
owner = name;
};
services.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";
};
appstoreEnable = false;
extraApps = {
inherit (config.services.nextcloud.package.passthru.packages.apps)
calendar
contacts
deck
groupfolders
maps
memories
music
news
notes
notify_push
tasks
twofactor_webauthn
;
};
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

@ -4,6 +4,7 @@ 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 selected target hosts
```bash

View file

@ -1,4 +1,5 @@
{
deployPkgs,
pkgs,
self,
...

View file

@ -1,5 +1,6 @@
{
deployPkgs,
pkgs,
self,
system,
...

View file

@ -1,23 +1,11 @@
{
pkgs,
git-hooks-nix,
treefmtEval,
system,
self,
...
}:
let
pre-commit-check = git-hooks-nix.lib.${system}.run {
src = ../.;
hooks.treefmt = {
enable = true;
package = treefmtEval.config.build.wrapper;
always_run = true;
};
};
in
{
${system}.default = pkgs.mkShellNoCC {
${system}.default = pkgs.mkShell {
name = "qois-infrastructure-shell";
buildInputs =
let
@ -26,9 +14,9 @@ in
vscode = pkgs.vscodium;
};
in
pre-commit-check.enabledPackages
++ [ vscodium-with-extensions ]
[ vscodium-with-extensions ]
++ (with self.packages.${system}; [
cache
deploy-qois
sops
sops-rekey
@ -36,15 +24,13 @@ in
++ (with pkgs; [
attic-client
deploy-rs
jq
nix-fast-build
nixVersions.latest
nixd
nixfmt-rfc-style
nixos-anywhere
pssh
ssh-to-age
pssh
yq
jq
]);
LANG = "C.UTF-8";
LC_ALL = "C.UTF-8";
@ -60,7 +46,11 @@ in
done
export XDG_DATA_DIRS
${pre-commit-check.shellHook}
# 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
'';
};
}

View file

@ -9,6 +9,7 @@ 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.

126
flake.lock generated
View file

@ -27,11 +27,11 @@
]
},
"locked": {
"lastModified": 1741786315,
"narHash": "sha256-VT65AE2syHVj6v/DGB496bqBnu1PXrrzwlw07/Zpllc=",
"lastModified": 1736165297,
"narHash": "sha256-OT+sF4eNDFN/OdyUfIQwyp28+CFQL7PAdWn0wGU7F0U=",
"owner": "nix-community",
"repo": "disko",
"rev": "0d8c6ad4a43906d14abd5c60e0ffe7b587b213de",
"rev": "76816af65d5294761636a838917e335992a52e0c",
"type": "github"
},
"original": {
@ -56,65 +56,6 @@
"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"
}
},
"git-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat_2",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks-nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1702272962,
@ -133,11 +74,11 @@
},
"nixpkgs-nixos-stable": {
"locked": {
"lastModified": 1743231893,
"narHash": "sha256-tpJsHMUPEhEnzySoQxx7+kA+KUtgWqvlcUBqROYNNt0=",
"lastModified": 1736061677,
"narHash": "sha256-DjkQPnkAfd7eB522PwnkGhOMuT9QVCZspDpJJYyOj60=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c570c1f5304493cafe133b8d843c7c1c4a10d3a6",
"rev": "cbd8ec4de4469333c82ff40d057350c30e9f7d36",
"type": "github"
},
"original": {
@ -147,13 +88,13 @@
"type": "github"
}
},
"nixpkgs_2": {
"nixpkgs-nixos-unstable": {
"locked": {
"lastModified": 1743095683,
"narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=",
"lastModified": 1736012469,
"narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6",
"rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d",
"type": "github"
},
"original": {
@ -165,16 +106,15 @@
},
"private": {
"inputs": {
"nixpkgs": [
"nixpkgs"
"nixpkgs-nixos-unstable": [
"nixpkgs-nixos-unstable"
]
},
"locked": {
"lastModified": 1743779182,
"narHash": "sha256-0wQ+22g6dfnIBIvw2Mji37R7OmCMrmqcp/4zSLQRKSQ=",
"ref": "refs/heads/main",
"rev": "8e7ec0d7f4a571a55d0e6b99fcd6f85fea474f1e",
"revCount": 16,
"lastModified": 1734984619,
"narHash": "sha256-D9awD3ArJ+8jCPr96HruGS4xpkJ7h2+V0Yiaay/9pyE=",
"rev": "18d3b3b703a6139b9ebd5ec64311717cf2a6f9bc",
"revCount": 7,
"type": "git",
"url": "file:./private"
},
@ -187,26 +127,24 @@
"inputs": {
"deploy-rs": "deploy-rs",
"disko": "disko",
"git-hooks-nix": "git-hooks-nix",
"nixpkgs": "nixpkgs_2",
"nixpkgs-nixos-stable": "nixpkgs-nixos-stable",
"nixpkgs-nixos-unstable": "nixpkgs-nixos-unstable",
"private": "private",
"sops-nix": "sops-nix",
"treefmt-nix": "treefmt-nix"
"sops-nix": "sops-nix"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
"nixpkgs-nixos-unstable"
]
},
"locked": {
"lastModified": 1743305778,
"narHash": "sha256-Ux/UohNtnM5mn9SFjaHp6IZe2aAnUCzklMluNtV6zFo=",
"lastModified": 1736064798,
"narHash": "sha256-xJRN0FmX9QJ6+w8eIIIxzBU1AyQcLKJ1M/Gp6lnSD20=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "8e873886bbfc32163fe027b8676c75637b7da114",
"rev": "5dc08f9cc77f03b43aacffdfbc8316807773c930",
"type": "github"
},
"original": {
@ -230,26 +168,6 @@
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1743081648,
"narHash": "sha256-WRAylyYptt6OX5eCEBWyTwOEqEtD6zt33rlUkr6u3cE=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "29a3d7b768c70addce17af0869f6e2bd8f5be4b7",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"

132
flake.nix
View file

@ -5,48 +5,34 @@
extra-trusted-public-keys = "qois-infrastructure:lh35ymN7Aoxm5Hz0S6JusxE+cYzMU+x9OMKjDVIpfuE=";
};
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-nixos-stable.url = "github:NixOS/nixpkgs/nixos-24.11";
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
git-hooks-nix = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
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.11";
sops-nix = {
url = "github:Mic92/sops-nix";
inputs = {
nixpkgs.follows = "nixpkgs-nixos-unstable";
};
};
private.url = "git+file:./private";
private.inputs.nixpkgs.follows = "nixpkgs";
private.inputs.nixpkgs-nixos-unstable.follows = "nixpkgs-nixos-unstable";
};
outputs =
{
self,
nixpkgs,
deploy-rs,
treefmt-nix,
...
}@inputs:
{ nixpkgs-nixos-unstable, deploy-rs, ... }@inputs:
let
system = "x86_64-linux";
# Packages for development and build process
pkgs = import nixpkgs { inherit system; };
deployPkgs = import nixpkgs {
pkgs = import nixpkgs-nixos-unstable { inherit system; };
deployPkgs = import nixpkgs-nixos-unstable {
inherit system;
overlays = [
deploy-rs.overlay
(_self: super: {
(self: super: {
deploy-rs = {
inherit (pkgs) deploy-rs;
lib = super.deploy-rs.lib;
@ -54,86 +40,22 @@
})
];
};
treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix;
importParams = {
inherit (inputs)
deploy-rs
disko
nixpkgs-nixos-stable
sops-nix
private
git-hooks-nix
;
inherit
deployPkgs
pkgs
system
treefmtEval
;
flakeSelf = self;
importParams = inputs // {
inherit pkgs;
inherit deployPkgs;
inherit system;
};
in
{
checks = import ./checks/default.nix (
importParams
// {
self = {
inherit (self)
lib
packages
nixosModules
nixosConfigurations
deploy
;
};
}
);
deploy = import ./deploy/default.nix (
importParams
// {
self = {
inherit (self)
lib
packages
nixosModules
nixosConfigurations
;
};
}
);
devShells = import ./dev-shells/default.nix (
importParams
// {
self = {
inherit (self) lib packages;
};
}
);
formatter.${system} = treefmtEval.config.build.wrapper;
nixosConfigurations = import ./nixos-configurations/default.nix (
importParams
// {
self = {
inherit (self) lib packages nixosModules;
};
}
);
nixosModules = import ./nixos-modules/default.nix (
importParams
// {
self = {
inherit (self) lib packages;
};
}
);
packages = import ./packages/default.nix (
importParams
// {
self = {
inherit (self) lib packages;
};
}
);
lib = import ./lib/default.nix { inherit pkgs; };
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;
};
}

View file

@ -1,26 +1,18 @@
{ pkgs, ... }:
let
inherit (pkgs.lib)
attrNames
filterAttrs
filter
pathExists
path
;
# Get a list of all subdirectories of a directory.
getSubDirs = base: attrNames (filterAttrs (_n: t: t == "directory") (builtins.readDir base));
# Check if a folder with a base path and folder name contains a file with a specific name
isFolderWithFile =
fileName: basePath: folderName:
(pathExists (path.append basePath "./${folderName}/${fileName}"));
# Get a list of subfolders that contain a default.nix file.
foldersWithNix = base: filter (isFolderWithFile "default.nix" base) (getSubDirs base);
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 getSubDirs isFolderWithFile foldersWithNix;
inherit foldersWithNix;
# Get a list of default.nix files that are nix submodules of the current folder.
loadSubmodulesFrom =
basePath: map (folder: path.append basePath "./${folder}/default.nix") (foldersWithNix basePath);
path: map (folder: lib.path.append path "./${folder}/default.nix") (foldersWithNix path);
}

View file

@ -1,4 +1,4 @@
{ ... }:
{ config, pkgs, ... }:
{
imports = [
@ -6,9 +6,10 @@
./filesystems.nix
../../defaults/hardware/apu.nix
];
qois.system.physical.enable = true;
../../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

View file

@ -1,4 +1,4 @@
{ ... }:
{ config, pkgs, ... }:
{
fileSystems."/" = {

View file

@ -1,4 +1,4 @@
{ config, ... }:
{ config, pkgs, ... }:
let
meta = config.qois.meta;
@ -32,7 +32,7 @@ in
qois.backplane-net.enable = true;
# TODO: Metaize ips
qois.router = {
services.qois.router = {
enable = true;
wanInterface = "enp4s0";
wirelessInterfaces = [ "wlp5s0" ];

View file

@ -1,6 +1,6 @@
# Host: Cyprianspitz
# Host: Cyprianspitz (+Router: Caral)
## Operations {#\_operations}
## Operations {#_operations}
Reboot requires passphrase.
@ -11,6 +11,7 @@ sops decrypt --extract '["system"]["hdd"]' private/nixos-configurations/cyprians
ssh -p 8223 root@calanda.plessur-ext.net.qo.is
```
Direct remote ssh access:
```
@ -23,6 +24,40 @@ TODO
- [Mainboard Manual](docs/z790m-itx-wifi.pdf)
### Networking: Caral Internet Router
A [MikroTik `CCR2004-1G-2XS-PCIe`](https://mikrotik.com/product/ccr2004_1g_2xs_pcie#fndtn-downloads) is used for internet access.
It's a fiber card with build in router, supporting 2x 25Gbit SFP28 cages and 1Gbit RJ45 eth.
- [RouterOS Docs](https://help.mikrotik.com/docs/spaces/ROS/pages/328059/RouterOS)
[The manual](docs/CCR2004-1G-2XS-PCIe_241138.pdf) states:
> This form-factor does come with certain limitations that you should keep in mind.
> The CCR NIC card needs some time to boot up compared to ASIC-based setups.
> If the host system is up before the CCR card, it will not appear among the available devices.
> You should add a PCIe device initialization delay after power-up in the BIOS.
> Or you will need to re-initialize the PCIe devices from the HOST system.
In our case, since networking is reinitialized after the LUKS password promt, this should not be a issue in practice. However, if networking would not be available, contact someone for a physical reboot and wait longer before entering the HDD password.
To reload the card's virtual interfaces on a running system:
```bash
echo "1" > /sys/bus/pci/devices/0000\:01\:00.0/remove
sleep 2
echo "1" > /sys/bus/pci/rescan
```
To restart the card on a running system:
```bash
echo "1" > /sys/bus/pci/devices/0000\:01\:00.0/reset
sleep 2m # Wait for reboot
echo "1" > /sys/bus/pci/rescan
```
### Top Overview
![](docs/top-view.jpg)

View file

@ -1,4 +1,4 @@
{ config, ... }:
{ pkgs, config, ... }:
{
qois.backup-server = {

View file

@ -1,4 +1,6 @@
{
config,
pkgs,
lib,
...
}:

View file

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

View file

@ -1,4 +1,4 @@
{ ... }:
{ config, pkgs, ... }:
{
imports = [
@ -10,9 +10,10 @@
./virtualisation.nix
../../defaults/hardware/asrock-z790m.nix
];
qois.system.physical.enable = true;
../../defaults/base
../../defaults/meta
];
# Set your time zone.
time.timeZone = "Europe/Amsterdam";

View file

@ -1,4 +1,4 @@
{ config, ... }:
{ config, pkgs, ... }:
let
meta = config.qois.meta;
@ -14,10 +14,26 @@ in
networking.nameservers = [ calandaIp ];
networking.useDHCP = false;
networking.interfaces.enp0s31f6.ipv4.addresses = [
networking.interfaces = {
# enp0s31f6: 1 Gbit mainboard interface
enp0s31f6.ipv4.addresses = [
(getNetV4Ip meta.network.physical.plessur-lan)
];
# wlp0s20f3: Mainboard Wireless interface
# enp3s0: 2.5 Gbit mainboard interface: Connected to ether1
#enp3s0.useDHCP = true;
# enp1s0f0: mikrotik sfp28-1: ether-pcie1 passthrough
enp1s0f0.useDHCP = true;
# enp1s0f1: mikrotik sfp28-2: ether-pcie2 passthrough
enp1s0f1.useDHCP = true;
# enp1s0f2: mikrotik ether1/bridge1: ether-pcie3 bridge \
enp1s0f2.useDHCP = true;
# enp1s0f3: mikrotik ether1/bridge1: ether-pcie4 bridge > connected to enp3s0
enp1s0f3.useDHCP = true;
};
networking.defaultGateway = {
address = calandaIp;
interface = "enp0s31f6";
@ -73,7 +89,7 @@ in
};
# Boot
qois.luks-ssh = {
services.qois.luks-ssh = {
enable = true;
interface = "eth0";

View file

@ -1,4 +1,4 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
virtualisation.libvirtd = {
enable = true;

View file

@ -2,16 +2,16 @@
self,
pkgs,
nixpkgs-nixos-stable,
disko,
sops-nix,
...
}@inputs:
let
inherit (pkgs.lib) genAttrs;
inherit (nixpkgs-nixos-stable.lib) nixosSystem;
configs = self.lib.foldersWithNix ./.;
in
genAttrs configs (
pkgs.lib.genAttrs configs (
config:
nixosSystem {
nixpkgs-nixos-stable.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {
inherit inputs;
@ -19,6 +19,15 @@ genAttrs configs (
modules = [
self.nixosModules.default
./${config}/default.nix
disko.nixosModules.disko
sops-nix.nixosModules.sops
(
{ ... }:
{
system.extraSystemBuilderCmds = "ln -s ${self} $out/nixos-configuration";
imports = [ ./secrets.nix ];
}
)
];
}
)

View file

@ -1,4 +1,5 @@
{
config,
pkgs,
lib,
...
@ -12,7 +13,6 @@
qois.git-ci-runner.enable = true;
qois.attic.enable = true;
qois.postgresql.package = pkgs.postgresql_15;
qois.renovate.enable = true;
# Remove substituters that are hosted on this node, to prevent lockups
# since the current nix implementation is not forgiving with unavailable subsituters.

View file

@ -1,7 +1,8 @@
{ config, ... }:
{ 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

@ -1,14 +1,15 @@
{ ... }:
{ config, pkgs, ... }:
{
imports = [
../../defaults/base-vm
../../defaults/meta
./applications
./disko-config.nix
./networking.nix
./secrets.nix
];
qois.system.virtual-machine.enable = true;
# Set your time zone.
time.timeZone = "Europe/Amsterdam";

View file

@ -1,4 +1,4 @@
{ config, ... }:
{ config, pkgs, ... }:
{

View file

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

View file

@ -1,9 +1,7 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
qois.postgresql.package = pkgs.postgresql_14;
qois.cloud = {
enable = true;
package = pkgs.nextcloud30;
};
imports = [ ./cloud.nix ];
qois.postgresql.package = pkgs.postgresql_14;
}

View file

@ -1,4 +1,4 @@
{ ... }:
{ config, pkgs, ... }:
{

View file

@ -1,14 +1,14 @@
{ config, ... }:
{ config, pkgs, ... }:
{
imports = [
../../defaults/base-vm
../../defaults/meta
./applications
./backup.nix
./secrets.nix
];
qois.system.virtual-machine.enable = true;
boot.loader.grub.device = "/dev/vda";
fileSystems."/" = {
device = "/dev/disk/by-uuid/5b6823ec-921f-400a-a7c0-3fe34d56ae12";

View file

@ -1,16 +1,3 @@
# Web Apps
## Setting up new static sites
Generate ssh key for deployment:
```bash
export SSH_KEYFILE=$(mktemp --dry-run -- /dev/shm/key-XXXXXXXXX)
mkfifo -m 600 $SSH_KEYFILE
ssh-keygen -q -t ed25519 -C "ci@git.qo.is" -N "" -f $SSH_KEYFILE <<< "y\ny\n" &
wl-copy --trim-newline --foreground --paste-once < $SSH_KEYFILE
# Paste private key in CI secret "SSH_DEPLOY_KEY" now
# Configure public key:
wl-copy --trim-newline < ${SSH_KEYFILE}.pub
```
## fabianhauser.ch

View file

@ -1,4 +1,4 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
imports = [ ];

View file

@ -1,15 +1,16 @@
{ ... }:
{ config, pkgs, ... }:
{
imports = [
../../defaults/base-vm
../../defaults/meta
./applications
./disko-config.nix
./networking.nix
./secrets.nix
];
qois.system.virtual-machine.enable = true;
# Set your time zone.
time.timeZone = "Europe/Amsterdam";

View file

@ -1,4 +1,4 @@
{ config, ... }:
{ config, pkgs, ... }:
{

View file

@ -1,14 +1,10 @@
# Host: Lindberg
## Operations {#\_operations}
## Operations {#_operations}
Reboot requires passphrase:
Reboot requires passphrase (see pass `host/lindberg/hdd_luks`)
``` bash
# Get passphrase
sops decrypt --extract '["system"]["hdd"]' private/nixos-configurations/lindberg/secrets.sops.yaml
# Insert passphrase:
ssh -p 2222 root@lindberg.riedbach-ext.net.qo.is
```
@ -16,6 +12,7 @@ ssh -p 2222 root@lindberg.riedbach-ext.net.qo.is
- [Mainboard Manual](docs/X570Pro4-mainboard-manual.pdf)
### Front / Back
#### Front Overview

View file

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

View file

@ -1,4 +1,7 @@
{
config,
pkgs,
lib,
...
}:

View file

@ -1,4 +1,4 @@
{ ... }:
{ config, pkgs, ... }:
{
qois.backup-client.includePaths = [ "/mnt/data" ];

View file

@ -1,4 +1,4 @@
{ ... }:
{ config, pkgs, ... }:
{
imports = [
@ -11,9 +11,10 @@
./virtualisation.nix
../../defaults/hardware/asrock.nix
];
qois.system.physical.enable = true;
../../defaults/base
../../defaults/meta
];
# Set your time zone.
time.timeZone = "Europe/Amsterdam";

View file

@ -9,8 +9,12 @@
MAILADDR root
ARRAY /dev/md/raid_system metadata=1.2 name=any:raid_system UUID=1becc692:aeb83b67:1c65da45:b8bd4b93
ARRAY /dev/md/raid_data metadata=1.2 name=any:raid_data UUID=576eabb1:0722bc27:84d9314f:d0145000
INACTIVE-ARRAY /dev/md125 metadata=1.2 name=nixos:md_data UUID=b9c36b6d:a2e0fa86:f6dbfe57:857cd0d2
'';
# 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.
@ -24,11 +28,11 @@
path = "/boot-primary";
efiBootloaderId = "NixOS primary";
}
{
devices = [ "nodev" ];
path = "/boot-secondary";
efiBootloaderId = "NixOS secondary";
}
#{
# devices = [ "nodev" ];
# path = "/boot-secondary";
# efiBootloaderId = "NixOS secondary";
#}
];
};
}

View file

@ -1,4 +1,4 @@
{ config, ... }:
{ config, pkgs, ... }:
let
meta = config.qois.meta;
@ -73,7 +73,7 @@ in
# Boot
boot.initrd.network.udhcpc.enable = true;
qois.luks-ssh = {
services.qois.luks-ssh = {
enable = true;
interface = "eth0";
sshPort = 2222;

View file

@ -1,4 +1,4 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
virtualisation.libvirtd = {
enable = true;

View file

@ -0,0 +1,11 @@
{ inputs, ... }:
{
sops.secrets =
let
allHostsSecretsFile = "${inputs.private}/nixos-configurations/secrets.sops.yaml";
in
{
"msmtp/password".sopsFile = allHostsSecretsFile;
"wgautomesh/gossip-secret".sopsFile = allHostsSecretsFile;
};
}

View file

@ -3,8 +3,8 @@
## Prepare Remote Machine
1. Boot nixos installer image
1. Set a root password: `sudo passwd root`
1. Get host ip to connect to ssh with `ip a`
2. Set a root password: `sudo passwd root`
3. Get host ip to connect to ssh with `ip a`
## Verify configuration
@ -12,7 +12,7 @@
## Installation
````bash
```bash
nix develop
# Set according to what we want
@ -60,11 +60,11 @@ sops exec-file --no-fifo --filename secret.key private/nixos-configurations/$REM
--disk-encryption-keys /run/secrets/system/hdd.key <(yq --raw-output '.system.hdd' {}) \
--disk-encryption-keys /run/secrets/system/initrd-ssh-key <(yq --raw-output '.system.\"initrd-ssh-key\"' {})
"
````
```
## Post-Setup
- Add backplane-vpn pubkey to `network-virtual.nix` configuration with
* Add backplane-vpn pubkey to `network-virtual.nix` configuration with
```bash
wg pubkey < /secrets/wireguard/private/backplane
```

View file

@ -1,4 +1,4 @@
# Operations {#\_operations}
# Operations {#_operations}
Reboot requires passphrase (see pass `host/stompert/hdd_luks`)

View file

@ -2,15 +2,15 @@
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running nixos-help).
{ ... }:
{ config, pkgs, ... }:
{
imports = [
../../defaults/hardware/apu.nix
../../defaults/base
../../defaults/meta
];
qois.system.physical.enable = true;
boot.initrd.luks.devices."systems".device =
"/dev/disk/by-uuid/5718bd19-cb7a-4728-9ec4-6b2be48215fc";
@ -35,7 +35,7 @@
# Define on which hard drive you want to install Grub.
boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
qois.luks-ssh = {
services.qois.luks-ssh = {
enable = true;
interface = "eth1";
sshPort = 2222;

View file

@ -1,80 +0,0 @@
{
config,
lib,
...
}:
with lib;
let
cfg = config.qois.backplane-net;
in
{
options.qois.backplane-net = {
enable = mkEnableOption "Enable backplane server services";
netName = mkOption {
description = "Network Name";
type = types.str;
default = "backplane";
};
port = mkOption {
description = "Wireguard Default Port";
type = types.number;
default = 51825;
};
};
config = lib.mkIf cfg.enable (
let
hostName = config.networking.hostName;
netConfig = config.qois.meta.network.virtual.${cfg.netName};
hostNetConfig = netConfig.hosts.${hostName};
interface = "wg-${cfg.netName}";
wgService = [ "wireguard-${interface}.service" ];
in
{
sops.secrets."wgautomesh/gossip-secret".restartUnits = [ "wgautomesh.service" ];
networking.wireguard.enable = true;
networking.wireguard.interfaces."wg-${cfg.netName}" = {
ips = [ "${hostNetConfig.v4.ip}/${toString netConfig.v4.prefixLength}" ];
listenPort = if hostNetConfig.endpoint != null then hostNetConfig.endpoint.port else cfg.port;
privateKeyFile = "/secrets/wireguard/private/${cfg.netName}";
generatePrivateKeyFile = true;
};
systemd.network.wait-online.ignoredInterfaces = [ interface ];
networking.firewall.allowedUDPPorts =
if hostNetConfig.endpoint != null then [ hostNetConfig.endpoint.port ] else [ cfg.port ];
# Configure wgautomesh to setup peers. Make sure that the name is not used in the VPN module
services.wgautomesh = {
enable = true;
gossipSecretFile = config.sops.secrets."wgautomesh/gossip-secret".path;
openFirewall = true;
settings = {
inherit interface;
# Map meta network configuration to the format of wgautomesh and filter out peers with endpoints
peers = pipe netConfig.hosts [
(filterAttrs (peerHostName: _: peerHostName != hostName)) # Not this host
(mapAttrsToList (
_: peerConfig: {
address = peerConfig.v4.ip;
endpoint =
if (peerConfig.endpoint != null) then
with peerConfig.endpoint; "${fqdn}:${toString port}"
else
null;
pubkey = peerConfig.publicKey;
}
))
];
};
};
systemd.services.wgautomesh = {
requires = wgService;
after = wgService;
};
}
);
}

View file

@ -1,135 +0,0 @@
# Default configuration for hosts
{
config,
lib,
pkgs,
...
}:
let
cfg = config.qois.cloud;
in
with lib;
{
options.qois.cloud = {
enable = mkEnableOption "Enable qois cloud service";
domain = mkOption {
type = types.str;
default = "cloud.qo.is";
description = "Domain, under which the service is served.";
};
package = mkOption {
type = types.package;
description = "Which package to use for the Nextcloud instance.";
relatedPackages = [
"nextcloud28"
"nextcloud29"
"nextcloud30"
];
};
};
config = mkIf cfg.enable {
services.nginx.virtualHosts."${cfg.domain}" = {
forceSSL = true;
enableACME = true;
kTLS = true;
};
sops.secrets."nextcloud/admin" = with config.users.users.nextcloud; {
inherit group;
owner = name;
};
services.postgresql.enable = true;
qois.backup-client.includePaths = [ config.services.nextcloud.home ];
services.nextcloud = {
inherit (cfg) package;
enable = true;
hostName = cfg.domain;
https = true;
webfinger = true;
maxUploadSize = "10G";
database.createLocally = true;
config = {
adminpassFile = config.sops.secrets."nextcloud/admin".path;
adminuser = "root";
dbtype = "pgsql";
};
appstoreEnable = false;
extraApps = {
inherit (config.services.nextcloud.package.passthru.packages.apps)
calendar
contacts
deck
groupfolders
maps
memories
music
news
notes
notify_push
tasks
twofactor_webauthn
;
};
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";
default_phone_region = "CH";
};
};
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
];
};
}

View file

@ -1,20 +1,8 @@
{
private,
self,
disko,
sops-nix,
...
}:
{
inputs: {
default =
{ ... }:
{ config, pkgs, ... }:
{
imports = (self.lib.loadSubmodulesFrom ./.) ++ [
../defaults/meta
disko.nixosModules.disko
sops-nix.nixosModules.sops
private.nixosModules.default
];
imports = (inputs.self.lib.loadSubmodulesFrom ./.) ++ [ inputs.private.nixosModules.default ];
};
}

View file

@ -1,16 +1,17 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.qois.luks-ssh;
cfg = config.services.qois.luks-ssh;
in
{
options.qois.luks-ssh = {
options.services.qois.luks-ssh = {
enable = mkEnableOption "luks-ssh service";
interface = mkOption {
@ -82,7 +83,7 @@ in
with lib;
concatLists (
mapAttrsToList (
_name: user: if elem "wheel" user.extraGroups then user.openssh.authorizedKeys.keys else [ ]
name: user: if elem "wheel" user.extraGroups then user.openssh.authorizedKeys.keys else [ ]
) config.users.users
);
hostKeys = [ cfg.sshHostKey ];

View file

@ -1,4 +1,8 @@
{
config,
lib,
pkgs,
options,
...
}:
{

View file

@ -1,6 +1,7 @@
{
config,
lib,
pkgs,
options,
...
}:
@ -42,10 +43,10 @@ in
};
config =
let
hostsWithSshKey = lib.filterAttrs (_name: hostCfg: hostCfg.sshKey != null) cfg;
hostsWithSshKey = lib.filterAttrs (name: hostCfg: hostCfg.sshKey != null) cfg;
in
{
programs.ssh.knownHosts = lib.mapAttrs (_name: hostCfg: {
programs.ssh.knownHosts = lib.mapAttrs (name: hostCfg: {
publicKey = hostCfg.sshKey;
}) hostsWithSshKey;
};

View file

@ -1,6 +1,7 @@
{
config,
lib,
pkgs,
options,
...
}:
@ -16,6 +17,13 @@ let
type = str;
inherit description;
});
mkOptStr =
description:
(mkOption {
type = nullOr str;
default = null;
inherit description;
});
mkNetworkIdOpts =
v:
@ -217,10 +225,10 @@ in
(getHostNamesForNetworks hostname cfg.virtual) ++ (getHostNamesForNetworks hostname cfg.physical);
hostsWithPublicKey = lib.filterAttrs (
_hostName: hostConfig: hostConfig.sshKey != null
hostName: hostConfig: hostConfig.sshKey != null
) config.qois.meta.hosts;
in
mapAttrs (name: _hostCfg: { extraHostNames = getHostNames name; }) hostsWithPublicKey;
mapAttrs (name: hostCfg: { extraHostNames = getHostNames name; }) hostsWithPublicKey;
};
}

View file

@ -1,5 +1,6 @@
{
config,
pkgs,
lib,
...
}:
@ -15,7 +16,6 @@ with lib;
hostname = mkOption {
type = types.str;
example = "mycache.myhost.org";
default = "nixpkgs-cache.qo.is";
description = "Hostname, under which the cache is served";
};

View file

@ -40,7 +40,7 @@ with lib;
host = "mail.cyon.ch";
user = "system@qo.is";
from = "no-reply@qo.is";
passwordeval = "${pkgs.coreutils}/bin/cat ${config.sops.secrets."msmtp/password".path}";
passwordeval = "${pkgs.busybox}/bin/cat ${config.sops.secrets."msmtp/password".path}";
};
};
};

View file

@ -1,5 +1,6 @@
{
config,
pkgs,
lib,
...
}:
@ -84,7 +85,7 @@ in
services.nginx = {
enable = true;
clientMaxBodySize = "10G";
clientMaxBodySize = "1g";
virtualHosts.${cfg.domain} = {
kTLS = true;
forceSSL = true;

View file

@ -1,5 +1,6 @@
{
config,
pkgs,
lib,
...
}:
@ -34,7 +35,7 @@ in
networking.hosts = pipe cfg.loadbalancers [
(map (hostname: config.qois.meta.network.virtual.backplane.hosts.${hostname}.v4.ip))
(flip genAttrs (_lb: cfg.domains))
(flip genAttrs (lb: cfg.domains))
];
};

View file

@ -0,0 +1,83 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.qois.backplane-net;
hostName = config.networking.hostName;
netConfig = config.qois.meta.network.virtual.${cfg.netName};
hostNetConfig = netConfig.hosts.${hostName};
interface = "wg-${cfg.netName}";
wgService = [ "wireguard-${interface}.service" ];
in
{
options.qois.backplane-net = {
enable = mkEnableOption "Enable backplane server services";
netName = mkOption {
description = "Network Name";
type = types.str;
default = "backplane";
};
domain = mkOption {
description = "Domain";
type = types.str;
default = hostNetConfig;
};
port = mkOption {
description = "Wireguard Default Port";
type = types.number;
default = 51825;
};
};
config = {
sops.secrets."wgautomesh/gossip-secret".restartUnits = [ "wgautomesh.service" ];
networking.wireguard.enable = true;
networking.wireguard.interfaces."wg-${cfg.netName}" = {
ips = [ "${hostNetConfig.v4.ip}/${toString netConfig.v4.prefixLength}" ];
listenPort = if hostNetConfig.endpoint != null then hostNetConfig.endpoint.port else cfg.port;
privateKeyFile = "/secrets/wireguard/private/${cfg.netName}";
generatePrivateKeyFile = true;
};
systemd.network.wait-online.ignoredInterfaces = [ interface ];
networking.firewall.allowedUDPPorts =
if hostNetConfig.endpoint != null then [ hostNetConfig.endpoint.port ] else [ cfg.port ];
# Configure wgautomesh to setup peers. Make sure that the name is not used in the VPN module
services.wgautomesh = {
enable = true;
gossipSecretFile = config.sops.secrets."wgautomesh/gossip-secret".path;
openFirewall = true;
settings = {
inherit interface;
# Map meta network configuration to the format of wgautomesh and filter out peers with endpoints
peers = pipe netConfig.hosts [
(filterAttrs (peerHostName: _: peerHostName != hostName)) # Not this host
(mapAttrsToList (
_: peerConfig: {
address = peerConfig.v4.ip;
endpoint =
if (peerConfig.endpoint != null) then
with peerConfig.endpoint; "${fqdn}:${toString port}"
else
null;
pubkey = peerConfig.publicKey;
}
))
];
};
};
systemd.services.wgautomesh = {
requires = wgService;
after = wgService;
};
};
}

View file

@ -2,6 +2,8 @@
config,
lib,
options,
pkgs,
self,
...
}:

View file

@ -2,6 +2,8 @@
config,
lib,
options,
pkgs,
self,
...
}:

View file

@ -0,0 +1,10 @@
{
config,
pkgs,
inputs,
...
}:
{
imports = inputs.self.lib.loadSubmodulesFrom ./.;
}

View file

@ -2,6 +2,7 @@
Runner for the [Forgejo git instance](../git/README.md).
## Default docker/ubuntu Runner
Registers a default runner with ubuntu OS or executes user's OCI container with podman.
@ -18,14 +19,3 @@ Consequentially, don't use to build nix things that should stay secret (which is
## Create Secret Token
To create a new token for registration, follow the steps outlined in the [Forgejo documentation](https://forgejo.org/docs/latest/user/actions/#forgejo-runner).
## Clear Runner Caches
Under some circumstances, runner caches need to be cleared. This can be done with:
```bash
cd /var/lib/private/gitea-runner/
systemctl stop --all gitea-runner-*
rm -r */.cache/
systemctl start --all gitea-runner-*
```

View file

@ -25,35 +25,6 @@ with lib;
default = 10;
description = "How many nix runner instances to start";
};
trustedSubstituters = mkOption {
type = types.listOf types.str;
default = [
# General substitutors (also elsewhere defined defaults, but without priority params)
"https://cache.nixos.org"
"https://${config.qois.nixpkgs-cache.hostname}"
"https://cache.garnix.io"
# Project builds
"https://attic.qo.is/qois-infrastructure" # https://git.qo.is/qo.is/infrastructure
"https://attic.qo.is/dotfiles" # https://git.qo.is/fabianhauser/dotfiles
];
description = "Substitutors that are trusted by the host.";
};
trustedPublicKeys = mkOption {
type = types.listOf types.str;
default = [
# General subsitutors
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
# Project builds
"qois-infrastructure:lh35ymN7Aoxm5Hz0S6JusxE+cYzMU+x9OMKjDVIpfuE=" # https://git.qo.is/qo.is/infrastructure
"dotfiles:KpLi0qe5O5rb8E8N8vntZWBDqFwG3Ksx4AFGizYCLoU=" # https://git.qo.is/fabianhauser/dotfiles
];
description = "Substitutor public keys that are trusted by the host.";
};
};
config = mkIf cfg.enable (mkMerge [
@ -63,12 +34,6 @@ with lib;
"gitea-runner-${defaultInstanceName}.service"
] ++ (genList (n: "gitea-runner-nix${builtins.toString n}.service") cfg.nixInstances);
nix.settings = {
trusted-substituters = cfg.trustedSubstituters;
trusted-public-keys = cfg.trustedPublicKeys;
};
virtualisation.podman = {
enable = true;
dockerCompat = true;
@ -181,7 +146,7 @@ with lib;
{
systemd.services =
genAttrs (genList (n: "gitea-runner-nix${builtins.toString n}") cfg.nixInstances)
(_name: {
(name: {
after = [
"gitea-runner-nix-image.service"
];
@ -272,9 +237,7 @@ with lib;
pkgs.findutils
pkgs.gawk
pkgs.git
pkgs.git-lfs
pkgs.gnugrep
pkgs.gnused
pkgs.jq
pkgs.nix
pkgs.nodejs

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