Compare commits

...

2 commits

19 changed files with 357 additions and 252 deletions

28
.claude/devil-advocate.md Normal file
View file

@ -0,0 +1,28 @@
# Devil's Advocate Review — Project Conventions
## Critical rules (must never be violated)
- **Never update `system.stateVersion`** — it must remain at the original installation version. NixOS upgrades are done by updating the `channel` file, not `stateVersion`.
- **Never modify `secrets.yaml` directly** — these are SOPS-encrypted. Changes must be made via `nix-shell -p sops --run 'sops <file>'`.
- **No plaintext secrets in Nix files** — passwords, API keys, tokens, and private keys must use `sops.secrets`, never hardcoded strings.
## Architecture rules
- **Explicit module imports only** — no wildcard or directory-level imports. Each module must be imported by its explicit path.
- **Host structure** — each host in `hosts/<name>/` must have `configuration.nix` and `hardware-configuration.nix`. Symlinks `fleet.nix` and `utils/` point to root level.
- **Shared modules** go in `utils/modules/`, not duplicated across hosts.
- **Custom packages** in `utils/pkgs/` must include an `update.sh` script for automated version updates.
## Code style
- **Two-space indentation** in all Nix files.
- **Lower kebab-case** for file and directory naming.
- **Conventional Commits** format: `fix:`, `feat:`, `chore:`, with optional scope by host (e.g., `fix(mail):`).
- No "Generated with Claude Code" or "Co-Authored-By: Claude" footers in commits.
## Common review checks
- New network services must have corresponding `networking.firewall.allowedTCPPorts` or `allowedUDPPorts` entries.
- New `sops.secrets.<name>` references must have a corresponding entry in the host's `secrets.yaml` (or the relevant module's `secrets.yaml`).
- Changes to `utils/` affect all hosts — verify cross-host compatibility.
- Package modifications should be testable with a direct `nix-build`, not just `test-configuration`.

26
.claude/lint-fixer.md Normal file
View file

@ -0,0 +1,26 @@
# Lint Fixer Instructions
## Formatter
This project uses **nixpkgs-fmt** for Nix file formatting.
## How to run
1. Find changed `.nix` files:
```bash
git diff --name-only --diff-filter=d HEAD | grep '\.nix$'
```
2. Format only those files:
```bash
nix run nixpkgs#nixpkgs-fmt -- <file1.nix> <file2.nix> ...
```
3. Stage any formatting changes:
```bash
git add <formatted files>
```
## Notes
- Only format files that were actually modified, not the entire repo.
- There is no `.editorconfig` or other formatter config; `nixpkgs-fmt` uses its own defaults.
- Non-Nix files do not need formatting.

35
.claude/secret-scanner.md Normal file
View file

@ -0,0 +1,35 @@
# Secret Scanner Allowlist
## False positive patterns to ignore
### SOPS-encrypted secrets files
All `secrets.yaml` files in this repo are **SOPS-encrypted** (not plaintext). They contain encrypted ciphertext, not actual secrets. Ignore:
- `hosts/*/secrets.yaml`
- `hosts/*/modules/*/secrets.yaml`
- `utils/modules/*/secrets.yaml`
- Any `.yaml` file matching a `path_regex` in `.sops.yaml`
### Age public keys
The file `.sops.yaml` contains **age public keys** (prefix `age1...`). These are public keys used for encryption, not private keys. Ignore:
- Age public keys (`age1...`) in `.sops.yaml`
- Age public key references (YAML anchors like `&dominik`, `&fw`, etc.) in `.sops.yaml`
### Nix hashes and store paths
Nix derivations contain SHA256/SRI hashes for source integrity verification. These are not secrets. Ignore:
- `sha256` / `hash` attributes in `.nix` files (e.g., `sha256 = "sha256-..."` or `hash = "sha256-..."`)
- `npmDepsHash`, `vendorHash`, `cargoHash`, and similar dependency hashes
- Nix store paths (`/nix/store/...`)
- `nix-prefetch-url` output hashes
- SRI hashes (`sha256-...`, `sha512-...`)
### sops-nix module configuration
Nix files reference sops secret paths as configuration, not actual secret values. Ignore:
- `sops.secrets.<name>` attribute sets
- `sopsFile` path references
- `key` attributes within `sops.secrets` blocks (these are YAML key paths, not cryptographic keys)
- `neededForUsers` attributes
### Other safe patterns
- `flake.lock` — contains Nix flake input hashes (integrity, not secrets)
- SSH **public** key strings in NixOS configuration (e.g., `openssh.authorizedKeys.keys`)
- Wireguard **public** keys in NixOS configuration

35
.claude/test-runner.md Normal file
View file

@ -0,0 +1,35 @@
# Test Runner Instructions
## Determine affected hosts
Run `git diff --name-only HEAD` (or `git diff --name-only` for unstaged changes) to find changed files.
### Scope rules
- If changes are **only** under `hosts/<name>/`, test only that specific host:
```bash
./scripts/test-configuration <name>
```
- If changes touch `utils/`, root-level `.nix` files, or any shared configuration, test **all** hosts:
```bash
for host in amzebs-01 fw mail nas nb web-arm; do
./scripts/test-configuration "$host"
done
```
### Custom package changes
If any files under `utils/pkgs/<package-name>/` were modified, also build the package directly:
```bash
nix-build -E 'with import <nixpkgs> { overlays = [ (import ./utils/overlays/packages.nix) ]; config.allowUnfree = true; }; <package-name>'
```
This catches build failures that `test-configuration` (evaluation-only) would miss.
## Notes
- Each `test-configuration` run performs a `nix-instantiate` dry-build (evaluation only, no binary builds).
- A non-zero exit code from any host means the test failed.
- Testable hosts: `amzebs-01`, `fw`, `mail`, `nas`, `nb`, `web-arm`.
- `dev` is not independently testable (no `hardware-configuration.nix`); it is deployed as a MicroVM via the `fw` host, so testing `fw` covers `dev`.

View file

@ -89,7 +89,7 @@ nix-build -E 'with import <nixpkgs> { overlays = [ (import ./utils/overlays/pack
## Workflow ## Workflow
**IMPORTANT: Always run `./scripts/test-configuration <hostname>` after making any changes** to verify the NixOS configuration builds successfully. This is required before committing. Run `./scripts/test-configuration <hostname>` to verify NixOS configuration changes build successfully.
## Conventions ## Conventions

View file

@ -46,11 +46,7 @@
networking.domain = "cloonar.com"; networking.domain = "cloonar.com";
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ./utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFshMhXwS0FQFPlITipshvNKrV8sA52ZFlnaoHd1thKg"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
programs.ssh = { programs.ssh = {
knownHosts = { knownHosts = {

View file

@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
projectsDir = "projects"; # Relative to /home/dominik projectsDir = "projects"; # Relative to /home/dominik
repositories = [ repositories = [
{ url = "forgejo@git.cloonar.com:Cloonar/wohnservice-wien-typo3.git"; path = "cloonar/wohnservice-wien"; } { url = "forgejo@git.cloonar.com:Cloonar/wohnservice-wien-typo3.git"; path = "cloonar/wohnservice-wien"; }
@ -46,12 +46,9 @@ in
uid = 1000; uid = 1000;
home = "/home/dominik"; home = "/home/dominik";
extraGroups = [ "wheel" "docker" ]; extraGroups = [ "wheel" "docker" ];
openssh.authorizedKeys.keys = [ openssh.authorizedKeys.keys = import ./utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
}; };
users.groups.users = {}; users.groups.users = { };
services.openssh.enable = true; services.openssh.enable = true;
programs.zsh.enable = true; programs.zsh.enable = true;
@ -103,12 +100,12 @@ in
Group = "users"; Group = "users";
}; };
script = '' script = ''
mkdir -p /home/dominik/.ddev mkdir -p /home/dominik/.ddev
if [ ! -f /home/dominik/.ddev/global_config.yaml ]; then if [ ! -f /home/dominik/.ddev/global_config.yaml ]; then
cat > /home/dominik/.ddev/global_config.yaml << 'EOF' cat > /home/dominik/.ddev/global_config.yaml << 'EOF'
router_bind_all_interfaces: true router_bind_all_interfaces: true
EOF EOF
fi fi
''; '';
}; };
} }

View file

@ -184,10 +184,7 @@
zramSwap.enable = true; zramSwap.enable = true;
networking.hostName = "fw"; networking.hostName = "fw";
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ./utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
# backups # backups
borgbackup.repo = "u149513-sub2@u149513-sub2.your-backup.de:borg"; borgbackup.repo = "u149513-sub2@u149513-sub2.your-backup.de:borg";

View file

@ -1,87 +1,89 @@
{ config, lib, pkgs, ... }: let { config, lib, pkgs, ... }:
let
# Short names to fit Linux interface name limit (15 chars for vm-fj-runner-1) # Short names to fit Linux interface name limit (15 chars for vm-fj-runner-1)
runners = ["fj-runner-1" "fj-runner-2"]; runners = [ "fj-runner-1" "fj-runner-2" ];
# Offset by 5 to avoid conflicts with Gitea runners (01-02) # Offset by 5 to avoid conflicts with Gitea runners (01-02)
runnerOffset = 5; runnerOffset = 5;
in { in
microvm.vms = lib.mapAttrs (runner: idx: { {
config = { microvm.vms = lib.mapAttrs
microvm = { (runner: idx: {
mem = 8096; config = {
shares = [ microvm = {
{ mem = 8096;
source = "/nix/store"; shares = [
mountPoint = "/nix/.ro-store"; {
tag = "ro-store"; source = "/nix/store";
proto = "virtiofs"; mountPoint = "/nix/.ro-store";
} tag = "ro-store";
{ proto = "virtiofs";
source = "/run/secrets"; }
mountPoint = "/run/secrets"; {
tag = "ro-token"; source = "/run/secrets";
proto = "virtiofs"; mountPoint = "/run/secrets";
} tag = "ro-token";
]; proto = "virtiofs";
volumes = [ }
{ ];
image = "rootfs.img"; volumes = [
mountPoint = "/"; {
size = 51200; image = "rootfs.img";
} mountPoint = "/";
]; size = 51200;
interfaces = [ }
{ ];
type = "tap"; interfaces = [
id = "vm-${runner}"; {
mac = "02:00:00:00:00:0${toString (idx + runnerOffset)}"; type = "tap";
} id = "vm-${runner}";
]; mac = "02:00:00:00:00:0${toString (idx + runnerOffset)}";
}; }
];
};
systemd.network.networks."10-lan" = { systemd.network.networks."10-lan" = {
matchConfig.PermanentMACAddress = "02:00:00:00:00:0${toString (idx + runnerOffset)}"; matchConfig.PermanentMACAddress = "02:00:00:00:00:0${toString (idx + runnerOffset)}";
address = [ "${config.networkPrefix}.97.5${toString (idx + runnerOffset)}/24" ]; address = [ "${config.networkPrefix}.97.5${toString (idx + runnerOffset)}/24" ];
gateway = [ "${config.networkPrefix}.97.1" ]; gateway = [ "${config.networkPrefix}.97.1" ];
dns = [ "${config.networkPrefix}.97.1" ]; dns = [ "${config.networkPrefix}.97.1" ];
}; };
networking.hostName = runner; networking.hostName = runner;
virtualisation.podman.enable = true; virtualisation.podman.enable = true;
services.gitea-actions-runner.instances.${runner} = { services.gitea-actions-runner.instances.${runner} = {
enable = true; enable = true;
url = "https://git.cloonar.com"; url = "https://git.cloonar.com";
name = runner; name = runner;
tokenFile = "/run/secrets/forgejo-runner-token"; tokenFile = "/run/secrets/forgejo-runner-token";
labels = [ labels = [
"ubuntu-latest:docker://git.cloonar.com/infrastructure/gitea-runner:1.0.0" "ubuntu-latest:docker://git.cloonar.com/infrastructure/gitea-runner:1.0.0"
]; ];
settings = { settings = {
container = { container = {
network = "podman"; network = "podman";
}; };
cache = { cache = {
enabled = true; enabled = true;
host = "${config.networkPrefix}.97.5${toString (idx + runnerOffset)}"; host = "${config.networkPrefix}.97.5${toString (idx + runnerOffset)}";
port = 8088; port = 8088;
};
}; };
}; };
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = import ../utils/ssh-keys.nix;
networking.firewall = {
enable = true;
allowedTCPPorts = [ 8088 ];
};
system.stateVersion = "22.05";
}; };
})
(lib.listToAttrs (lib.lists.imap1 (i: v: { name = v; value = i; }) runners));
services.openssh.enable = true; sops.secrets.forgejo-runner-token = { };
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
];
networking.firewall = {
enable = true;
allowedTCPPorts = [ 8088 ];
};
system.stateVersion = "22.05";
};
}) (lib.listToAttrs (lib.lists.imap1 (i: v: { name=v; value=i; }) runners));
sops.secrets.forgejo-runner-token = {};
} }

View file

@ -29,7 +29,7 @@ in
privateNetwork = true; privateNetwork = true;
hostBridge = "server"; hostBridge = "server";
hostAddress = "${networkPrefix}.97.1"; hostAddress = "${networkPrefix}.97.1";
localAddress = "${networkPrefix}.97.55/24"; # Different from gitea's .50 localAddress = "${networkPrefix}.97.55/24"; # Different from gitea's .50
bindMounts = { bindMounts = {
"/var/lib/forgejo" = { "/var/lib/forgejo" = {
hostPath = "/var/lib/forgejo/"; hostPath = "/var/lib/forgejo/";
@ -50,7 +50,7 @@ in
]; ];
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
vim # my preferred editor vim # my preferred editor
]; ];
networking = { networking = {
@ -96,29 +96,29 @@ in
openid = { openid = {
ENABLE_OPENID_SIGNIN = false; ENABLE_OPENID_SIGNIN = false;
ENABLE_OPENID_SIGNUP = true; ENABLE_OPENID_SIGNUP = true;
WHITELISTED_URIS = "auth.cloonar.com"; WHITELISTED_URIS = "auth.cloonar.com";
}; };
service = { service = {
DISABLE_REGISTRATION = false; DISABLE_REGISTRATION = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true; ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
SHOW_REGISTRATION_BUTTON = false; SHOW_REGISTRATION_BUTTON = false;
ENABLE_NOTIFY_MAIL = true; ENABLE_NOTIFY_MAIL = true;
REQUIRE_SIGNIN_VIEW = false; REQUIRE_SIGNIN_VIEW = false;
}; };
mailer = { mailer = {
ENABLED = true; ENABLED = true;
FROM = "Forgejo Cloonar <gitea@cloonar.com>"; FROM = "Forgejo Cloonar <gitea@cloonar.com>";
PROTOCOL = "smtp+starttls"; PROTOCOL = "smtp+starttls";
SMTP_ADDR = "mail.cloonar.com"; SMTP_ADDR = "mail.cloonar.com";
SMTP_PORT = 587; SMTP_PORT = 587;
USER = "gitea@cloonar.com"; USER = "gitea@cloonar.com";
}; };
actions = { actions = {
ENABLED = true; ENABLED = true;
DEFAULT_ACTIONS_URL = "github"; # Pull actions from GitHub DEFAULT_ACTIONS_URL = "github"; # Pull actions from GitHub
}; };
attachment = { attachment = {
MAX_SIZE = 2048; # 2GB in MB for general attachments MAX_SIZE = 2048; # 2GB in MB for general attachments
}; };
packages = { packages = {
ENABLED = true; ENABLED = true;
@ -130,10 +130,7 @@ in
systemd.services.forgejo.serviceConfig.EnvironmentFile = "/run/secrets/forgejo-mailer-password"; systemd.services.forgejo.serviceConfig.EnvironmentFile = "/run/secrets/forgejo-mailer-password";
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ../utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
users.users.forgejo = user; users.users.forgejo = user;
users.groups.forgejo = group; users.groups.forgejo = group;

View file

@ -1,88 +1,90 @@
{ config, lib, nixpkgs, pkgs, ... }: let { config, lib, nixpkgs, pkgs, ... }:
let
# hostname = "git-02"; # hostname = "git-02";
# json = pkgs.formats.json { }; # json = pkgs.formats.json { };
runners = ["git-runner-1" "git-runner-2"]; runners = [ "git-runner-1" "git-runner-2" ];
indexedRunners = lib.lists.imap1 (i: v: { name=v; value=i; }) runners; indexedRunners = lib.lists.imap1 (i: v: { name = v; value = i; }) runners;
in { in
microvm.vms = lib.mapAttrs (runner: idx: { {
config = { microvm.vms = lib.mapAttrs
microvm = { (runner: idx: {
mem = 8096; config = {
shares = [ microvm = {
{ mem = 8096;
source = "/nix/store"; shares = [
mountPoint = "/nix/.ro-store"; {
tag = "ro-store"; source = "/nix/store";
proto = "virtiofs"; mountPoint = "/nix/.ro-store";
} tag = "ro-store";
{ proto = "virtiofs";
source = "/run/secrets"; }
mountPoint = "/run/secrets"; {
tag = "ro-token"; source = "/run/secrets";
proto = "virtiofs"; mountPoint = "/run/secrets";
} tag = "ro-token";
]; proto = "virtiofs";
volumes = [ }
{ ];
image = "rootfs.img"; volumes = [
mountPoint = "/"; {
size = 51200; image = "rootfs.img";
} mountPoint = "/";
]; size = 51200;
interfaces = [ }
{ ];
type = "tap"; interfaces = [
id = "vm-${runner}"; {
mac = "02:00:00:00:00:0${toString idx}"; type = "tap";
} id = "vm-${runner}";
]; mac = "02:00:00:00:00:0${toString idx}";
}; }
];
};
systemd.network.networks."10-lan" = { systemd.network.networks."10-lan" = {
matchConfig.PermanentMACAddress = "02:00:00:00:00:0${toString idx}"; matchConfig.PermanentMACAddress = "02:00:00:00:00:0${toString idx}";
address = [ "${config.networkPrefix}.97.5${toString idx}/24" ]; address = [ "${config.networkPrefix}.97.5${toString idx}/24" ];
gateway = [ "${config.networkPrefix}.97.1" ]; gateway = [ "${config.networkPrefix}.97.1" ];
dns = [ "${config.networkPrefix}.97.1" ]; dns = [ "${config.networkPrefix}.97.1" ];
}; };
networking.hostName = runner; networking.hostName = runner;
virtualisation.podman.enable = true; virtualisation.podman.enable = true;
services.gitea-actions-runner.instances.${runner} = { services.gitea-actions-runner.instances.${runner} = {
enable = true; enable = true;
url = "https://git.cloonar.com"; url = "https://git.cloonar.com";
name = runner; name = runner;
tokenFile = "/run/secrets/gitea-runner-token"; tokenFile = "/run/secrets/gitea-runner-token";
labels = [ labels = [
# "ubuntu-latest:docker://shivammathur/node:latest" # "ubuntu-latest:docker://shivammathur/node:latest"
"ubuntu-latest:docker://git.cloonar.com/infrastructure/gitea-runner:1.0.0" "ubuntu-latest:docker://git.cloonar.com/infrastructure/gitea-runner:1.0.0"
]; ];
settings = { settings = {
container = { container = {
network = "podman"; network = "podman";
}; };
cache = { cache = {
enabled = true; enabled = true;
host = "${config.networkPrefix}.97.5${toString idx}"; # LAN IP of the machine running act_runner host = "${config.networkPrefix}.97.5${toString idx}"; # LAN IP of the machine running act_runner
port = 8088; # any free TCP port port = 8088; # any free TCP port
};
}; };
}; };
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = import ../utils/ssh-keys.nix;
networking.firewall = {
enable = true; # default, but being explicit is fine
allowedTCPPorts = [ 8088 ];
};
system.stateVersion = "22.05";
}; };
})
services.openssh.enable = true; (lib.listToAttrs (lib.lists.imap1 (i: v: { name = v; value = i; }) runners));
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
];
networking.firewall = {
enable = true; # default, but being explicit is fine
allowedTCPPorts = [ 8088 ];
};
system.stateVersion = "22.05";
};
}) (lib.listToAttrs (lib.lists.imap1 (i: v: { name=v; value=i; }) runners));
# microvm.vms = { # microvm.vms = {
# gitea = { # gitea = {
@ -240,10 +242,10 @@ in {
# }; # };
# }; # };
sops.secrets.gitea-runner-token = {}; sops.secrets.gitea-runner-token = { };
environment = { environment = {
systemPackages = [ systemPackages = [
pkgs.qemu pkgs.qemu
pkgs.quickemu pkgs.quickemu
]; ];

View file

@ -3,7 +3,7 @@ let
cids = import ../modules/staticids.nix; cids = import ../modules/staticids.nix;
domain = "git.cloonar.com"; domain = "git.cloonar.com";
networkPrefix = config.networkPrefix; networkPrefix = config.networkPrefix;
user = { user = {
isSystemUser = true; isSystemUser = true;
uid = cids.uids.gitea; uid = cids.uids.gitea;
@ -50,7 +50,7 @@ in
]; ];
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
vim # my preferred editor vim # my preferred editor
]; ];
networking = { networking = {
@ -94,26 +94,26 @@ in
openid = { openid = {
ENABLE_OPENID_SIGNIN = false; ENABLE_OPENID_SIGNIN = false;
ENABLE_OPENID_SIGNUP = true; ENABLE_OPENID_SIGNUP = true;
WHITELISTED_URIS = "auth.cloonar.com"; WHITELISTED_URIS = "auth.cloonar.com";
}; };
service = { service = {
DISABLE_REGISTRATION = false; DISABLE_REGISTRATION = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true; ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
SHOW_REGISTRATION_BUTTON = false; SHOW_REGISTRATION_BUTTON = false;
ENABLE_NOTIFY_MAIL = true; ENABLE_NOTIFY_MAIL = true;
REQUIRE_SIGNIN_VIEW = false; REQUIRE_SIGNIN_VIEW = false;
}; };
mailer = { mailer = {
ENABLED = true; ENABLED = true;
FROM = "Gitea Cloonar <gitea@cloonar.com>"; FROM = "Gitea Cloonar <gitea@cloonar.com>";
PROTOCOL = "smtp+starttls"; PROTOCOL = "smtp+starttls";
SMTP_ADDR = "mail.cloonar.com"; SMTP_ADDR = "mail.cloonar.com";
SMTP_PORT = 587; SMTP_PORT = 587;
USER = "gitea@cloonar.com"; USER = "gitea@cloonar.com";
}; };
actions.ENABLED=true; actions.ENABLED = true;
attachment = { attachment = {
MAX_SIZE = 2048; # 2GB in MB for general attachments MAX_SIZE = 2048; # 2GB in MB for general attachments
}; };
packages = { packages = {
ENABLED = true; ENABLED = true;
@ -122,10 +122,7 @@ in
}; };
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ../utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
users.users.gitea = user; users.users.gitea = user;
users.groups.gitea = group; users.groups.gitea = group;
@ -134,7 +131,7 @@ in
}; };
}; };
sops.secrets.gitea-runner = {}; sops.secrets.gitea-runner = { };
sops.secrets.gitea-mailer-password = { sops.secrets.gitea-mailer-password = {
owner = "gitea"; owner = "gitea";
restartUnits = [ "container@git.service" ]; restartUnits = [ "container@git.service" ];

View file

@ -11,10 +11,7 @@ let
gateway = "${config.networkPrefix}.97.1"; gateway = "${config.networkPrefix}.97.1";
tapDevice = "vm-openclaw"; tapDevice = "vm-openclaw";
sshAuthorizedKeys = [ sshAuthorizedKeys = import ../utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
gitRepoUrl = "https://git.cloonar.com/openclawd/config.git"; gitRepoUrl = "https://git.cloonar.com/openclawd/config.git";

View file

@ -1,8 +1,10 @@
{ lib, pkgs, config, ... }: let { lib, pkgs, config, ... }:
let
hostname = "web-02"; hostname = "web-02";
json = pkgs.formats.json { }; json = pkgs.formats.json { };
impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz"; impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in { in
{
microvm.vms = { microvm.vms = {
web = { web = {
pkgs = import pkgs.path { pkgs = import pkgs.path {
@ -96,7 +98,7 @@ in {
}; };
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
vim # my preferred editor vim # my preferred editor
]; ];
networking.hostName = hostname; networking.hostName = hostname;
@ -104,21 +106,18 @@ in {
services.openssh = { services.openssh = {
enable = true; enable = true;
hostKeys = [ hostKeys = [
{ {
path = "/persist/etc/ssh/ssh_host_ed25519_key"; path = "/persist/etc/ssh/ssh_host_ed25519_key";
type = "ed25519"; type = "ed25519";
} }
{ {
path = "/persist/etc/ssh/ssh_host_rsa_key"; path = "/persist/etc/ssh/ssh_host_rsa_key";
type = "rsa"; type = "rsa";
bits = 4096; bits = 4096;
} }
]; ];
}; };
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ../../utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
services.nginx = { services.nginx = {
enable = true; enable = true;

View file

@ -1,5 +1,5 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
{ {
imports = [ imports = [
./utils/bento.nix ./utils/bento.nix
./utils/modules/sops.nix ./utils/modules/sops.nix
@ -29,10 +29,7 @@
environment.systemPackages = with pkgs; [ vim ]; environment.systemPackages = with pkgs; [ vim ];
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ./utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
# backups # backups
borgbackup.repo = "u149513-sub7@u149513-sub7.your-backup.de:borg"; borgbackup.repo = "u149513-sub7@u149513-sub7.your-backup.de:borg";

View file

@ -2,7 +2,8 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz"; impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in { in
{
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
imports = [ imports = [
@ -38,10 +39,7 @@ in {
# SSH server # SSH server
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ./utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
# Firewall # Firewall
networking.firewall.enable = true; networking.firewall.enable = true;

View file

@ -81,10 +81,7 @@
networking.hostName = "web-arm"; networking.hostName = "web-arm";
networking.domain = "cloonar.com"; networking.domain = "cloonar.com";
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ users.users.root.openssh.authorizedKeys.keys = import ./utils/ssh-keys.nix;
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
programs.ssh = { programs.ssh = {
knownHosts = { knownHosts = {

View file

@ -6,4 +6,4 @@ switch from gitea to forgejo
## chache server ## chache server
https://github.com/zhaofengli/attic https://github.com/zhaofengli/attic
set ssh keys that each server should accept somewhere globally in utils and each server should implement it # TODO: set ssh keys that each server should accept somewhere globally in utils and each server should implement it

5
utils/ssh-keys.nix Normal file
View file

@ -0,0 +1,5 @@
[
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFshMhXwS0FQFPlITipshvNKrV8sA52ZFlnaoHd1thKg"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
]