first commit

This commit is contained in:
Sebastian Wendel 2024-07-04 13:47:18 +02:00
commit b910dc465e
429 changed files with 21127 additions and 0 deletions

5
.envrc Normal file
View file

@ -0,0 +1,5 @@
use flake
dotenv_if_exists .envrc.local
eval "$shellHook"

53
.gitignore vendored Normal file
View file

@ -0,0 +1,53 @@
.envrc.local
.*.swo
.*.swp
.direnv
.DS_Store
.pre-commit-config.yaml
result*
*.qcow2
*.log
temp
*.pyc
TODOS.md
.devenv
# terraform
.terraform
*.tf.json
*.tfstate
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc

11
.vscode/extensions.json vendored Executable file
View file

@ -0,0 +1,11 @@
{
"recommendations": [
"arrterian.nix-env-selector",
"bbenoist.nix",
"brettm12345.nixfmt-vscode",
"hashicorp.hcl",
"jnoortheen.nix-ide",
"mikestead.dotenv",
],
"unwantedRecommendations": []
}

33
.vscode/settings.json vendored Executable file
View file

@ -0,0 +1,33 @@
{
"files.associations": {
"flake.lock": "json",
"*.hcl": "hcl",
},
"[terraform]": {
"editor.defaultFormatter": "hashicorp.terraform",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
},
"[terraform-vars]": {
"editor.defaultFormatter": "hashicorp.terraform",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
},
"[hcl]": {
"editor.defaultFormatter": "hashicorp.terraform",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
},
"[nix]": {
"editor.defaultFormatter": "jnoortheen.nix-ide",
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"editor.tabSize": 2,
"editor.codeLens": true,
"emmet.triggerExpansionOnTab": true,
"editor.quickSuggestions": {
"comments": "on",
"strings": "on"
}
}
}

24
LICENSE.md Normal file
View file

@ -0,0 +1,24 @@
# The MIT License (MIT)
Copyright © 2023 Sebastian Wendel
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the “Software”), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

202
README.md Normal file
View file

@ -0,0 +1,202 @@
# srx digital - nix platform repository
<img src="https://raw.githubusercontent.com/NixOS/nixos-artwork/master/logo/nix-snowflake-colours.svg" alt="Nix Flake Logo" style="float: right; width: 150px; height: auto;">
This is the platform repository of [srx.digital](https://srx.digital), a Nix development and operations company based in Hamburg, Germany.
[NixOS](https://nixos.org/) is a Linux distribution built on the [Nix package manager](https://nixos.wiki/wiki/Nix_package_manager), utilizing declarative configuration to ensure reproducible and reliable system setups.
This repository contains opinionated configurations for deploying NixOS systems and cloud infrastructures with [Terraform](https://www.terraform.io/), written in pure [Nix](https://nixos.org/learn) expressions. It offers developers and DevOps engineers an insight into the potential of Nix.
## 📜 Principles of Operation
This repository uses 100% Infrastructure as Code and does not need to be configured manually. All services are monitored and backed up. Common services are modularized for reuse, but the separation of custom configurations is in progress. Reusable components will be moved to a separate Flake module in the near future.
📌 **Note**
> Some customer-specific configurations are stored in a private Git repository and imported as the `srx-nixos-shadow` flake to protect customer infrastructure. Due to its private nature, some tasks may fail and should be commented out. Generally, all expressions should evaluate without issues.
## 🛠️ Components
- [flake-parts](https://github.com/hercules-ci/flake-parts): Simplify Nix Flakes with the module system.
- [git-hooks](https://github.com/cachix/git-hooks.nix): Seamless integration of git hooks with Nix.
- [agenix](https://github.com/ryantm/agenix): Encrypted secrets for NixOS and Home Manager.
- [deploy-rs](https://github.com/serokell/deploy-rs): A multi-profile Nix-flake deployment tool.
- [nixos-anywhere](https://github.com/nix-community/nixos-anywhere): Install NixOS anywhere via SSH.
- [disko](https://github.com/nix-community/disko): Declarative disk partitioning.
- [srvos](https://github.com/nix-community/srvos): NixOS profiles for servers.
- [Tang & Clevis](https://fosdem.org/2024/schedule/event/fosdem-2024-3044-clevis-tang-unattended-boot-of-an-encrypted-nixos-system/): An automated encryption framework for full disk encryption.
- [Lanzaboote](https://github.com/nix-community/lanzaboote): Secure Boot for NixOS.
- [home-manager](https://github.com/nix-community/home-manager): Manage user environments using Nix.
- [terranix](https://terranix.org/): Create [OpenTofu](https://opentofu.org/) JSON files the NixOS way.
- [kubenix](https://kubenix.org/): Kubernetes management with Nix.
- [stylix](https://stylix.danth.me/): Apply consistent color schemes, fonts, and wallpapers.
- [hydra](https://github.com/NixOS/hydra): The Nix-based continuous build system.
## 📁 Repository layout
```txt
├── hosts - NixOS server configurations
├── lib - Reusable Nix libraries
├── modules - Reusable NixOS modules
├── nix - Flake-parts modules
├── overlays - Nix package overlays
├── secrets.nix - Age-encrypted secrets
├── terranix - Terraform Nix expressions
├── default.nix - Legacy support with flake-compat
├── flake.lock - Lock file for version pinning
└── flake.nix - Flakes configuration
```
## 🚀 Getting started
### 📋 Prerequisites
Before proceeding, ensure the following tools are installed:
- [Git](https://git-scm.com/): For cloning and managing the repository.
- [direnv](https://direnv.net/): To automatically enter Nix environments.
- [Nix package manager](https://nixos.org/download#download-nix): Essential for Nix or NixOS operations.
### 🛠️ Commands
Run `menu` or `nix flake show` to view all commands and aliases provided by the devshell, as defined in [nix/devshell.nix](nix/devshell.nix).
### 🖥️ NixOS
#### 🔐 Secrets
Secrets are encrypted using [agenix](https://github.com/ryantm/agenix). To add secret files and new hosts with their SSH public key, edit [nix/hosts.nix](nix/hosts.nix).
- `agenix --edit` edits FILE using $EDITOR
- `agenix --decrypt` decrypts FILE to STDOUT
- `agenix --rekey` re-encrypts all secrets with specified recipients
#### 🏭 Development
To begin, run `nix develop` in the source tree to enter the development shell, or use [direnv](https://direnv.net/) for automatic entry. Check [flake.nix](flake.nix) or run `nix flake show` to view the flake definition. Server definitions are described in the [hosts](hosts) folder. Module definitions are located in the [nix/modules.nix](nix/modules.nix) and [modules](modules) folders.
#### 🧪 local Testing
To build and run a local [QEMU](https://www.qemu.org/) VM, use the following steps:
1. Build the system with:
```sh
nixos-rebuild build-vm --flake .#dev-vm
```
2. Configure the network settings:
```sh
export QEMU_NET_OPTS="hostfwd=tcp::2221-:22"
```
3. Start the VM:
```sh
result/bin/run-dev-vm-vm
```
4. Access the VM via SSH:
```sh
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@localhost -p 2221
```
#### 🎯 Deployment
[deploy-rs](https://github.com/serokell/deploy-rs) is a straightforward deployment tool for NixOS systems. It is configured in [nix/deploy.nix](nix/deploy.nix), where you can adjust `autoRollback` or `magicRollback` options.
To deploy, run:
```sh
deploy .#dev-vm
```
For more information on usage, refer to the [`deploy --help`](https://github.com/serokell/deploy-rs) documentation.
### 🪐 Terraform
This project uses [OpenTofu](https://opentofu.org/) and [terranix](https://terranix.org/) for creating Terraform JSON files the Nix way.
#### 🔗 Environment Variables
Create a local and private [.envrc.local](.envrc.local) file to authenticate with remote services during local development and operations.
- **AWS_ACCESS_KEY_ID** and **AWS_SECRET_ACCESS_KEY**: Required for accessing S3 services, crucial for state management. Refer to [Terraform S3 State](https://developer.hashicorp.com/terraform/language/settings/backends/s3) for more details.
- **GITHUB_TOKEN**: Authenticates against GitHub for repository access and API interactions. [Details](https://docs.github.com/en/actions/security-guides/automatic-token-authentication).
- **HYDRA_HOST**, **HYDRA_USERNAME**, **HYDRA_PASSWORD**: Configures and authenticates with a Hydra server for CI/CD operations. [Hydra provider documentation](https://github.com/DeterminateSystems/terraform-provider-hydra).
- **GRAFANA_AUTH**: Enables Grafana server authentication for dashboard access and API use. [Grafana provider usage](https://registry.terraform.io/providers/grafana/grafana/).
- **MINIO_ENABLE_HTTPS**, **MINIO_ENDPOINT**, **MINIO_ROOT_USER**, **MINIO_ROOT_PASSWORD**: Sets up MinIO services, ensuring secure and authenticated connections. [MinIO provider usage](https://registry.terraform.io/providers/aminueza/minio).
#### 📦 State Management
The [Terraform state](https://developer.hashicorp.com/terraform/language/state) is stored outside the repository in an S3 Bucket, configured in [terraform.nix](terranix/minio/terraform.nix) and hosted by [minio.nix](hosts/srxgp00/services/minio/default.nix).
#### 🔧 Configuration
Terraform version and providers are pinned and configured in [nix/terranix.nix](nix/terranix.nix). Terraform resources are declared in the [terranix](terranix) folder.
#### 🕹️ Terraform Commands
- `nix run .#tf-init` - Initializes the working directory.
- `nix run .#tf-state` - Performs basic state modifications.
- `nix run .#tf-import` - Import existing infrastructure resources.
- `nix run .#tf-validate` - Validates using [tfsec](https://github.com/aquasecurity/tfsec), configured in [tfsec.nix](terranix/tfsec.nix).
- `nix run .#tf-plan` - Creates the execution plan.
- `nix run .#tf-apply` - Executes the actions proposed in the plan.
- `nix run .#tf-destroy` - Destroys all remote objects.
#### 🧰 Helpers
- `nix run .#tf2nix resource.tf` - Converts HCL files to Nix.
- `nix run .#json2nix resource.yaml` - Converts JSON or YAML files to Nix.
#### 🧩 Common Functions
Generalized functions for reuse are in [lib/terraform.nix](lib/terraform.nix) and [nix/terranix.nix](nix/terranix.nix).
### 🔄 Updates
This project uses [nix flakes](https://nixos.wiki/wiki/Flakes) to manage [nixpkgs](https://github.com/NixOS/nixpkgs) versions. To upgrade, use `nix flake update` for all inputs or `nix flake update nixpkgs` to update a single flake input.
To check if a remote system is behind your flake state, run `nix run .#nix-upgrades`:
```sh
🔍 Scanning for upgradable hosts...
dev-vm: ⚠️ Modified: 24.05.20240618.938aa15
```
### 🤖 CI/CD
I utilize the [Nix Hydra project](https://github.com/NixOS/hydra) to test and build all packages and hosts, strictly following the `Zero Hydra Failures` paradigm to ensure every build is successful and stable.
You can view all our jobs and their statuses at [build.nix.srx.digital](https://build.nix.srx.digital/).
## 🚧 Reporting issues
If you experience any issues with the infrastructure, please [post a new issue to this repository](https://code.srx.digital/srx/srx-platform-nix/issues).
## 💬 Contact
Need help with Nix? Write me an e-mail to book an appointment for Nix/NixOS/DevOps related topics. You can find me at:
- [SRX Digital - Development & Operations](https://srx.digital/)
- [Nix Hamburg Matrix channel](https://matrix.to/#/#nix-hh:curious.bio) for live discussions.
## 📚 Links
- [Nix packages search](https://search.nixos.org/packages)
- [NixOS options search](https://search.nixos.org/options)
- [Nix Manual](https://nix.dev/manual/nix/stable/)
- [NixOS Manual](https://nixos.org/manual/nixos/stable/)
- [NixOS & Flakes Book](https://nixos-and-flakes.thiscute.world/)
- [NixOS Wiki](https://nixos.wiki/wiki/Main_Page)
- [Awesome Nix](https://github.com/nix-communi/awesome-nix)
## 📜 License
All files in this repository are licensed under the terms of the MIT License (MIT). Please refer to the full license text in [LICENSE](LICENSE.md).

14
default.nix Normal file
View file

@ -0,0 +1,14 @@
{ system ? builtins.currentSystem, src ? ./. }:
let
inherit (lock.nodes.flake-compat.locked) owner repo rev narHash;
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
flake-compat = fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz";
sha256 = narHash;
};
flake = import flake-compat { inherit src system; };
in
flake.defaultNix

2550
flake.lock Normal file

File diff suppressed because it is too large Load diff

285
flake.nix Normal file
View file

@ -0,0 +1,285 @@
{
description = "SRX NixOS Platform";
nixConfig = {
extra-trusted-substituters = [
"https://nix-config.cachix.org"
"https://nix-community.cachix.org"
"https://tweag-jupyter.cachix.org"
"https://serokell.cachix.org"
"https://cache.nix.srx.digital"
];
extra-trusted-public-keys = [
"nix-config.cachix.org-1:Vd6raEuldeIZpttVQfrUbLvXJHzzzkS0pezXCVVjDG4="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"tweag-jupyter.cachix.org-1:UtNH4Zs6hVUFpFBTLaA4ejYavPo5EFFqgd7G7FxGW9g="
"serokell.cachix.org-1:5DscEJD6c1dD1Mc/phTIbs13+iW22AVbx0HqiSb+Lq8="
"cache.nix.srx.digital-1:+sJ/hAmjuhTpRakfhkCj7i2LoqNyBgm+YItJUSAWWlg="
];
};
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
nixos-hardware.url = "github:NixOS/nixos-hardware";
nur.url = "github:nix-community/NUR";
nixos-anywhere = {
url = "github:nix-community/nixos-anywhere";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-parts.follows = "flake-parts";
disko.follows = "disko";
treefmt-nix.follows = "treefmt-nix";
};
};
nixos-generators = {
url = "github:nix-community/nixos-generators";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-index-database = {
url = "github:nix-community/nix-index-database";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
flake-utils.url = "github:numtide/flake-utils";
flake-compat.url = "github:nix-community/flake-compat";
devenv = {
url = "github:cachix/devenv";
inputs = {
nixpkgs.follows = "nixpkgs";
pre-commit-hooks.follows = "git-hooks";
flake-compat.follows = "flake-compat";
};
};
devshell = {
url = "github:numtide/devshell";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";
};
};
flake-root.url = "github:srid/flake-root";
srvos = {
url = "github:nix-community/srvos";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager/release-24.05";
inputs.nixpkgs.follows = "nixpkgs";
};
lanzaboote = {
url = "github:nix-community/lanzaboote";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "flake-compat";
flake-parts.follows = "flake-parts";
flake-utils.follows = "flake-utils";
pre-commit-hooks-nix.follows = "git-hooks";
};
};
nix-fast-build = {
url = "github:Mic92/nix-fast-build";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-parts.follows = "flake-parts";
treefmt-nix.follows = "treefmt-nix";
};
};
systems.url = "github:nix-systems/default";
agenix = {
url = "github:ryantm/agenix";
inputs = {
home-manager.follows = "home-manager";
nixpkgs.follows = "nixpkgs";
};
};
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
nixvim = {
url = "github:nix-community/nixvim";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-parts.follows = "flake-parts";
flake-compat.follows = "flake-compat";
devshell.follows = "devshell";
home-manager.follows = "home-manager";
treefmt-nix.follows = "treefmt-nix";
};
};
deploy-rs = {
url = "github:serokell/deploy-rs";
inputs = {
flake-compat.follows = "flake-compat";
nixpkgs.follows = "nixpkgs";
utils.follows = "flake-utils";
};
};
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
git-hooks = {
url = "github:cachix/git-hooks.nix";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "flake-compat";
};
};
base16-schemes = {
url = "github:tinted-theming/base16-schemes";
flake = false;
};
stylix = {
url = "github:danth/stylix";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "flake-compat";
home-manager.follows = "home-manager";
};
};
kubenix = {
url = "github:hall/kubenix";
inputs.nixpkgs.follows = "nixpkgs";
};
microvm = {
url = "github:astro/microvm.nix";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";
};
};
vault-secrets = {
url = "github:serokell/vault-secrets";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "flake-compat";
};
};
dns = {
url = "github:nix-community/dns.nix";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";
};
};
impermanence.url = "github:nix-community/impermanence";
simple-nixos-mailserver = {
url = "gitlab:simple-nixos-mailserver/nixos-mailserver";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "flake-compat";
};
};
terranix = {
url = "github:terranix/terranix";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";
};
};
nix-vscode-extensions = {
url = "github:nix-community/nix-vscode-extensions";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";
flake-compat.follows = "flake-compat";
};
};
firefox-addons = {
url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";
};
};
cq-flake = {
url = "github:vinszent/cq-flake";
inputs.flake-utils.follows = "flake-utils";
};
srx-nixos-shadow = {
url = "git+ssh://forgejo@code.srx.digital/srx/srx-nixos-shadow";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "flake-compat";
flake-parts.follows = "flake-parts";
pre-commit-hooks.follows = "git-hooks";
treefmt-nix.follows = "treefmt-nix";
};
};
srx-digital-website = {
url = "git+ssh://forgejo@code.srx.digital/srx/srx.astro.nix";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-parts.follows = "flake-parts";
treefmt-nix.follows = "treefmt-nix";
pre-commit.follows = "git-hooks";
};
};
nix-hamburg-website = {
url = "git+ssh://forgejo@code.srx.digital/nix-hamburg/nix-hamburg.astro.nix";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-parts.follows = "flake-parts";
treefmt-nix.follows = "treefmt-nix";
pre-commit.follows = "git-hooks";
};
};
};
outputs = inputs@ { flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" "aarch64-linux" ];
imports = [
./nix/hosts.nix
./nix/packages.nix
./nix/modules.nix
./nix/overlay.nix
./nix/nixos.nix
./nix/home-manager.nix
./nix/deploy.nix
./nix/devshell.nix
./nix/terranix.nix
];
};
}

BIN
hosts/dev-vm/clevis.age Normal file

Binary file not shown.

27
hosts/dev-vm/default.nix Normal file
View file

@ -0,0 +1,27 @@
{ self, ... }:
{
imports = [
self.nixosModules.roles-workstation
./hardware.nix
./storage.nix
];
system.stateVersion = "24.05";
networking = {
hostName = "dev-vm";
hostId = "ea0023ed";
};
systemd.network = {
enable = true;
networks."10-uplink" = {
matchConfig.Name = "enp1s0";
address = [ "192.168.122.26/24" ];
routes = [
{ routeConfig.Gateway = "192.168.122.1"; }
{ routeConfig.Gateway = "fe80::1"; }
];
};
};
}

37
hosts/dev-vm/hardware.nix Normal file
View file

@ -0,0 +1,37 @@
{ self, modulesPath, config, ... }:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
(modulesPath + "/profiles/qemu-guest.nix")
self.nixosModules.filesystems-zfs
];
age.secrets.clevisSystem.file = ./clevis.age;
boot = {
initrd = {
systemd = {
enable = true;
inherit (config.systemd) network;
};
network.enable = true;
availableKernelModules = [
"ahci"
"xhci_pci"
"virtio_pci"
"virtio_scsi"
"sr_mod"
"virtio_blk"
];
clevis = {
enable = true;
useTang = true;
devices."vda".secretFile = config.age.secrets.clevisSystem.path;
};
};
loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
};
}

143
hosts/dev-vm/storage.nix Normal file
View file

@ -0,0 +1,143 @@
{ lib, ... }:
let
disks = [
"/dev/vda"
# "/dev/vdb"
];
compression = "zstd";
rootFsOptions = {
acltype = "posixacl";
dnodesize = "auto";
normalization = "formD";
xattr = "sa";
relatime = "on";
canmount = "off";
mountpoint = "none";
inherit compression;
"com.sun:auto-snapshot" = "false";
};
passwordFile = "/etc/hostname";
system = lib.genAttrs disks
(device:
let
name = builtins.replaceStrings [ "_" ] [ "-" ] (lib.lists.last (builtins.split "/" device));
in
{
type = "disk";
inherit name device;
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02";
};
esp = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
swap = {
size = "8G";
type = "8200";
content.type = "swap";
};
system = {
size = "100%";
content = {
type = "luks";
inherit name;
content = {
type = "zfs";
pool = "system";
};
inherit passwordFile;
settings.allowDiscards = true;
};
};
};
};
});
default = mountpoint: {
type = "zfs_fs";
options = {
inherit mountpoint;
};
inherit mountpoint;
};
auto_snapshot = mountpoint: {
type = "zfs_fs";
options = {
inherit mountpoint;
"com.sun:auto-snapshot" = "true";
};
inherit mountpoint;
};
can_not_mount = {
type = "zfs_fs";
options = {
canmount = "off";
mountpoint = "none";
};
mountpoint = null;
mountOptions = [ ];
};
in
{
disko.devices = {
disk = system;
zpool = {
system = {
type = "zpool";
options = {
ashift = "12";
autotrim = "on";
};
inherit rootFsOptions;
mountpoint = null;
datasets = {
"reserved" = {
type = "zfs_fs";
options = {
canmount = "off";
mountpoint = "none";
refreservation = "10GiB";
};
mountpoint = null;
mountOptions = [ ];
};
"nixos" = can_not_mount;
"nixos/etc" = default "/etc";
"nixos/nix" = default "/nix";
"user" = can_not_mount;
"user/root" = default "/root";
"user/home" = auto_snapshot "/home";
"data" = can_not_mount;
"data/lib" = auto_snapshot "/var/lib";
"data/log" = default "/var/log";
"data/cache" = default "/var/cache";
"data/backup" = default "/var/backup";
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"defaults"
"size=1G"
"mode=755"
"noatime"
];
};
"/tmp" = {
fsType = "tmpfs";
mountOptions = [ "size=1G" ];
};
};
};
}

View file

@ -0,0 +1,16 @@
{ self, ... }:
{
imports = [
self.nixosModules.roles-core
./hardware.nix
./services/wireguard.nix
# ./services/moonraker.nix
# ./services/fluidd.nix
# ./services/klipper
# ./services/octoprint.nix
];
networking.hostName = "srxfdm00";
system.stateVersion = "24.05";
}

View file

@ -0,0 +1,11 @@
{ self, ... }:
{
imports = [
self.nixosModules.hardware-rpi4
];
fileSystems."/" = {
device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
fsType = "ext4";
};
}

View file

@ -0,0 +1,19 @@
{
services = {
fluidd = {
enable = true;
nginx = {
default = true;
locations."/webcam".proxyPass = "http://127.0.0.1:8080/stream";
};
};
nginx.clientMaxBodySize = "1000m";
};
networking.firewall = {
allowedTCPPorts = [ 80 ];
allowedUDPPorts = [ 80 ];
};
}

View file

@ -0,0 +1,33 @@
{ lib, config, ... }:
{
services.klipper = {
enable = true;
octoprintIntegration = lib.mkIf config.services.octoprint.enable true;
user = lib.mkForce "klipper";
group = lib.mkForce "klipper";
firmwares.mcu = {
enable = true;
serial = /dev/serial/by-id/usb-Klipper_stm32f446xx_360015001750535556323420-if00;
configFile = ./firmware.ini;
enableKlipperFlash = true;
};
configFile = ./settings.cfg;
};
users = {
groups.${config.services.klipper.group} = { };
users.${config.services.klipper.user} = {
group = "${config.services.klipper.group}";
extraGroups = [ "dialout" ];
isSystemUser = true;
};
};
security.sudo.extraRules = [{
commands = [{
command = "/run/current-system/sw/bin/poweroff";
options = [ "NOPASSWD" ];
}];
groups = [ config.services.klipper.group ];
}];
}

View file

@ -0,0 +1,109 @@
CONFIG_LOW_LEVEL_OPTIONS=y
# CONFIG_MACH_AVR is not set
# CONFIG_MACH_ATSAM is not set
# CONFIG_MACH_ATSAMD is not set
# CONFIG_MACH_LPC176X is not set
CONFIG_MACH_STM32=y
# CONFIG_MACH_HC32F460 is not set
# CONFIG_MACH_RP2040 is not set
# CONFIG_MACH_PRU is not set
# CONFIG_MACH_AR100 is not set
# CONFIG_MACH_LINUX is not set
# CONFIG_MACH_SIMU is not set
CONFIG_BOARD_DIRECTORY="stm32"
CONFIG_MCU="stm32f446xx"
CONFIG_CLOCK_FREQ=180000000
CONFIG_USBSERIAL=y
CONFIG_FLASH_SIZE=0x80000
CONFIG_FLASH_BOOT_ADDRESS=0x8000000
CONFIG_RAM_START=0x20000000
CONFIG_RAM_SIZE=0x20000
CONFIG_STACK_SIZE=512
CONFIG_FLASH_APPLICATION_ADDRESS=0x8008000
CONFIG_STM32_SELECT=y
# CONFIG_MACH_STM32F103 is not set
# CONFIG_MACH_STM32F207 is not set
# CONFIG_MACH_STM32F401 is not set
# CONFIG_MACH_STM32F405 is not set
# CONFIG_MACH_STM32F407 is not set
# CONFIG_MACH_STM32F429 is not set
CONFIG_MACH_STM32F446=y
# CONFIG_MACH_STM32F765 is not set
# CONFIG_MACH_STM32F031 is not set
# CONFIG_MACH_STM32F042 is not set
# CONFIG_MACH_STM32F070 is not set
# CONFIG_MACH_STM32F072 is not set
# CONFIG_MACH_STM32G070 is not set
# CONFIG_MACH_STM32G071 is not set
# CONFIG_MACH_STM32G0B0 is not set
# CONFIG_MACH_STM32G0B1 is not set
# CONFIG_MACH_STM32G431 is not set
# CONFIG_MACH_STM32H723 is not set
# CONFIG_MACH_STM32H743 is not set
# CONFIG_MACH_STM32H750 is not set
# CONFIG_MACH_STM32L412 is not set
# CONFIG_MACH_N32G452 is not set
# CONFIG_MACH_N32G455 is not set
CONFIG_MACH_STM32F4=y
CONFIG_HAVE_STM32_USBOTG=y
CONFIG_HAVE_STM32_CANBUS=y
CONFIG_HAVE_STM32_USBCANBUS=y
CONFIG_STM32_DFU_ROM_ADDRESS=0x1fff0000
CONFIG_STM32_FLASH_START_8000=y
# CONFIG_STM32_FLASH_START_10000 is not set
# CONFIG_STM32_FLASH_START_0000 is not set
# CONFIG_STM32_CLOCK_REF_8M is not set
CONFIG_STM32_CLOCK_REF_12M=y
# CONFIG_STM32_CLOCK_REF_16M is not set
# CONFIG_STM32_CLOCK_REF_20M is not set
# CONFIG_STM32_CLOCK_REF_24M is not set
# CONFIG_STM32_CLOCK_REF_25M is not set
# CONFIG_STM32_CLOCK_REF_INTERNAL is not set
CONFIG_CLOCK_REF_FREQ=12000000
CONFIG_STM32F0_TRIM=16
CONFIG_STM32_USB_PA11_PA12=y
# CONFIG_STM32_SERIAL_USART1 is not set
# CONFIG_STM32_SERIAL_USART1_ALT_PB7_PB6 is not set
# CONFIG_STM32_SERIAL_USART2 is not set
# CONFIG_STM32_SERIAL_USART2_ALT_PD6_PD5 is not set
# CONFIG_STM32_SERIAL_USART3 is not set
# CONFIG_STM32_SERIAL_USART3_ALT_PD9_PD8 is not set
# CONFIG_STM32_CANBUS_PA11_PA12 is not set
# CONFIG_STM32_CANBUS_PA11_PB9 is not set
# CONFIG_STM32_MMENU_CANBUS_PB8_PB9 is not set
# CONFIG_STM32_MMENU_CANBUS_PI9_PH13 is not set
# CONFIG_STM32_MMENU_CANBUS_PB5_PB6 is not set
# CONFIG_STM32_MMENU_CANBUS_PB12_PB13 is not set
# CONFIG_STM32_MMENU_CANBUS_PD0_PD1 is not set
# CONFIG_STM32_USBCANBUS_PA11_PA12 is not set
CONFIG_USB=y
CONFIG_USB_VENDOR_ID=0x1d50
CONFIG_USB_DEVICE_ID=0x614e
CONFIG_USB_SERIAL_NUMBER_CHIPID=y
CONFIG_USB_SERIAL_NUMBER="12345"
#
# USB ids
#
# end of USB ids
CONFIG_WANT_GPIO_BITBANGING=y
CONFIG_WANT_DISPLAYS=y
CONFIG_WANT_SENSORS=y
CONFIG_WANT_LIS2DW=y
CONFIG_WANT_SOFTWARE_I2C=y
CONFIG_WANT_SOFTWARE_SPI=y
CONFIG_NEED_SENSOR_BULK=y
CONFIG_CANBUS_FREQUENCY=1000000
CONFIG_INITIAL_PINS=""
CONFIG_HAVE_GPIO=y
CONFIG_HAVE_GPIO_ADC=y
CONFIG_HAVE_GPIO_SPI=y
CONFIG_HAVE_GPIO_SDIO=y
CONFIG_HAVE_GPIO_I2C=y
CONFIG_HAVE_GPIO_HARD_PWM=y
CONFIG_HAVE_STRICT_TIMING=y
CONFIG_HAVE_CHIPID=y
CONFIG_HAVE_STEPPER_BOTH_EDGE=y
CONFIG_HAVE_BOOTLOADER_REQUEST=y
CONFIG_INLINE_STEPPER_HACK=y

View file

@ -0,0 +1,583 @@
# Copymaster3D Voron2 V2.4 R2-SB Kit
# 350 x 350 x 350mm - With StealthBurner
# E3D V6 Hotend Kit 0.4 nozzle
# BigtreeTech Octopus V1.1 STM32F446ZET6
# TMC2209 UART
[mcu]
serial: /dev/serial/by-id/usb-Klipper_stm32f446xx_360015001750535556323420-if00
restart_method: command
[printer]
kinematics: corexy
max_velocity: 300
max_accel: 4000
max_z_velocity: 25
max_z_accel: 400
square_corner_velocity: 5.0
#####################################################################
# X/Y Stepper Settings
#####################################################################
## B Stepper - Left
## Connected to MOTOR_0
## Endstop connected to DIAG_0
[stepper_x]
step_pin: PF13
dir_pin: PF12
enable_pin: !PF14
rotation_distance: 40
microsteps: 32
full_steps_per_rotation: 400
endstop_pin: ^PG6
position_min: 0
position_endstop: 350
position_max: 350
homing_speed: 100
homing_retract_dist: 3
homing_positive_dir: true
[tmc2209 stepper_x]
uart_pin: PC4
interpolate: false
run_current: 0.8
sense_resistor: 0.110
stealthchop_threshold: 0
## A Stepper - Right
## Connected to MOTOR_1
## Endstop connected to DIAG_1
[stepper_y]
step_pin: PG0
dir_pin: PG1
enable_pin: !PF15
rotation_distance: 40
microsteps: 32
full_steps_per_rotation: 400
endstop_pin: PG9
position_min: 0
position_endstop: 350
position_max: 350
homing_speed: 100
homing_retract_dist: 3
homing_positive_dir: true
[tmc2209 stepper_y]
uart_pin: PD11
interpolate: false
run_current: 0.8
sense_resistor: 0.110
stealthchop_threshold: 0
#####################################################################
# Z Stepper Settings
#####################################################################
## Z0 Stepper - Front Left
## Connected to MOTOR_2
## Endstop connected to DIAG_2
[stepper_z]
step_pin: PF11
dir_pin: PG3
enable_pin: !PG5
rotation_distance: 40
gear_ratio: 80:16
microsteps: 32
endstop_pin: ^PG10
position_endstop: 1.725
position_max: 310
position_min: -5
homing_speed: 100
second_homing_speed: 3
homing_retract_dist: 3
[tmc2209 stepper_z]
uart_pin: PC6
interpolate: false
run_current: 0.8
sense_resistor: 0.110
stealthchop_threshold: 0
## Z1 Stepper - Rear Left
## Connected to MOTOR_3
[stepper_z1]
step_pin: PG4
dir_pin: !PC1
enable_pin: !PA0
rotation_distance: 40
gear_ratio: 80:16
microsteps: 32
[tmc2209 stepper_z1]
uart_pin: PC7
interpolate: false
run_current: 0.8
sense_resistor: 0.110
stealthchop_threshold: 0
## Z2 Stepper - Rear Right
## Connected to MOTOR_4
[stepper_z2]
step_pin: PF9
dir_pin: PF10
enable_pin: !PG2
rotation_distance: 40
gear_ratio: 80:16
microsteps: 32
[tmc2209 stepper_z2]
uart_pin: PF2
interpolate: false
run_current: 0.8
sense_resistor: 0.110
stealthchop_threshold: 0
## Z3 Stepper - Front Right
## Connected to MOTOR_5
[stepper_z3]
step_pin: PC13
dir_pin: !PF0
enable_pin: !PF1
rotation_distance: 40
gear_ratio: 80:16
microsteps: 32
[tmc2209 stepper_z3]
uart_pin: PE4
interpolate: false
run_current: 0.8
sense_resistor: 0.110
stealthchop_threshold: 0
#####################################################################
# Extruder
#####################################################################
## Connected to MOTOR_6
## Heater - HE0
## Thermistor - T0
[extruder]
step_pin: PE2
dir_pin: !PE3
enable_pin: !PD4
## Update value below when you perform extruder calibration
## If you ask for 100mm of filament, but in reality it is 98mm:
## rotation_distance = <previous_rotation_distance> * <actual_extrude_distance> / 100
rotation_distance: 22.6789511
gear_ratio: 50:10
microsteps: 32
full_steps_per_rotation: 200
nozzle_diameter: 0.400
filament_diameter: 1.75
heater_pin: PA2
sensor_type: ATC Semitec 104GT-2
sensor_pin: PF4
min_temp: 10
max_temp: 270
max_power: 1.0
min_extrude_temp: 170
control: pid
pid_Kp: 21.464
pid_Ki: 0.878
pid_Kd: 131.198
pressure_advance: 0.05
pressure_advance_smooth_time: 0.040
## E0 on MOTOR6
[tmc2209 extruder]
uart_pin: PE1
interpolate: false
run_current: 0.5
sense_resistor: 0.110
stealthchop_threshold: 0
#####################################################################
# Bed Heater
#####################################################################
## SSR Pin - HE1
## Thermistor - TB
[heater_bed]
heater_pin: PA3
sensor_type: Generic 3950
sensor_pin: PF3
## Adjust max_power so it doesn't exceed the SSR rating. The Omron G3NA-210B-DC5 SSR is rated at 4 amps without a heatsink.
## The formula is "4 / (Wattage_of_bed_heater / Mains_voltage) = max_power"
## If max_power is greater than 1.0, use 1.0
max_power: 0.6
min_temp: 0
max_temp: 120
control: pid
pid_Kp: 38.507
pid_Ki: 0.820
pid_Kd: 451.976
#####################################################################
# Probe
#####################################################################
## Inductive Probe
## This probe is not used for Z height, only Quad Gantry Leveling
[probe]
# Omron TL-Q5MC2 NPN Inductive Probe (NC)
# https://www.mouser.de/datasheet/2/307/omrns03968_1-2279740.pdf
pin: PG15
x_offset: 0
y_offset: 25.0
z_offset: 0
speed: 10.0
samples: 3
samples_result: median
sample_retract_dist: 3.0
samples_tolerance: 0.006
samples_tolerance_retries: 3
#####################################################################
# Fan Control
#####################################################################
## Print Cooling Fan - FAN0
[fan]
pin: PA8
kick_start_time: 0.5
## Depending on your fan, you may need to increase this value
## if your fan will not start. Can change cycle_time (increase)
## if your fan is not able to slow down effectively
off_below: 0.10
## Hotend Fan - FAN1
[heater_fan hotend_fan]
pin: PE5
max_power: 1.0
kick_start_time: 0.5
heater: extruder
heater_temp: 50.0
## If you are experiencing back flow, you can reduce fan_speed
#fan_speed: 1.0
## Controller fan - FAN2
[controller_fan controller_fan]
pin: PD12
kick_start_time: 0.5
heater: heater_bed
## Exhaust fan - FAN3
#[heater_fan exhaust_fan]
#pin: PD13
#max_power: 1.0
#shutdown_speed: 0.0
#kick_start_time: 5.0
#heater: heater_bed
#heater_temp: 60
#fan_speed: 1.0
#####################################################################
# LED Control
#####################################################################
## Chamber Lighting - HE2 Connector
#[output_pin caselight]
##Octopus 1.0 & 1.1, Octopus PRO 1.0
#pin: PB10
##Octopus PRO 1.1
#pin: PB0
#pwm:true
#shutdown_value: 0
#value:1
#cycle_time: 0.01
[neopixel headlight]
## Stealthburner lighting - RGB_LED
pin: PB0
chain_count: 3
color_order: GRBW
initial_RED: 1.0
initial_GREEN: 0.0
initial_BLUE: 0.0
initial_WHITE: 0.0
#####################################################################
# Additional Sensors
#####################################################################
# [temperature_sensor chamber_temp]
# ## Chamber Temperature - T1
# sensor_type: ATC Semitec 104NT-4-R025H42G
# sensor_pin: PF5
# min_temp: 0
# max_temp: 100
# gcode_id: chamber_th
#####################################################################
# Homing and Gantry Adjustment Routines
#####################################################################
[idle_timeout]
timeout: 1800
[safe_z_home]
home_xy_position: 233,346
speed:100
z_hop:10
z_hop_speed:10
[quad_gantry_level]
gantry_corners:
-60,-10
410,420
points:
50,25
50,275
300,275
300,25
speed: 100
horizontal_move_z: 10
retries: 5
retry_tolerance: 0.0075
max_adjust: 10
[bed_mesh]
speed: 300
horizontal_move_z: 15
mesh_min: 40, 40
mesh_max: 310,310
fade_start: 0.6
fade_end: 10.0
probe_count: 5,5
algorithm: bicubic
[bed_mesh default]
version: 1
points:
-0.095299, -0.102799, -0.102799, -0.097799, -0.102799
-0.144049, -0.146549, -0.135299, -0.137799, -0.141549
-0.159049, -0.156549, -0.141549, -0.141549, -0.159049
-0.141549, -0.154049, -0.157799, -0.159049, -0.172799
-0.097799, -0.120299, -0.120299, -0.110299, -0.109049
x_count: 5
y_count: 5
mesh_x_pps: 2
mesh_y_pps: 2
algo: bicubic
tension: 0.2
min_x: 40.0
max_x: 310.0
min_y: 40.0
max_y: 310.0
########################################
# EXP1 / EXP2 (display) pins
########################################
[board_pins]
aliases:
EXP1_1=PE8,
EXP1_2=PE7,
EXP1_3=PE9,
EXP1_4=PE10,
EXP1_5=PE12,
EXP1_6=PE13,
EXP1_7=PE14,
EXP1_8=PE15,
EXP1_9=<GND>,
EXP1_10=<5V>,
EXP2_1=PA6,
EXP2_2=PA5,
EXP2_3=PB1,
EXP2_4=PA4,
EXP2_5=PB2,
EXP2_6=PA7,
EXP2_7=PC15,
EXP2_8=<RST>,
EXP2_9=<GND>,
EXP2_10=<5V>
#####################################################################
# Displays
#####################################################################
[display] # mini12864 LCD Display
lcd_type: uc1701
cs_pin: EXP1_3
a0_pin: EXP1_4
rst_pin: EXP1_5
encoder_pins: ^EXP2_5, ^EXP2_3
click_pin: ^!EXP1_2
contrast: 63
spi_software_miso_pin: EXP2_1
spi_software_mosi_pin: EXP2_6
spi_software_sclk_pin: EXP2_2
[neopixel btt_mini12864] # Neopixel RGB in mini12864
pin: EXP1_6
chain_count: 3
initial_RED: 0.1
initial_GREEN: 0.5
initial_BLUE: 0.0
color_order: RGB
[delayed_gcode setdisplayneopixel] # Index 1 = display, Index 2 and 3 = Knob
initial_duration: 1
gcode:
SET_LED LED=btt_mini12864 RED=1 GREEN=1 BLUE=1 INDEX=1 TRANSMIT=0
SET_LED LED=btt_mini12864 RED=1 GREEN=0 BLUE=0 INDEX=2 TRANSMIT=0
SET_LED LED=btt_mini12864 RED=1 GREEN=0 BLUE=0 INDEX=3
[virtual_sdcard]
path: /var/lib/moonraker/gcodes
[pause_resume]
#####################################################################
# Macros
#####################################################################
[gcode_macro PARK]
gcode:
{% set th = printer.toolhead %}
G0 X{th.axis_maximum.x // 2} Y{th.axis_maximum.y // 2} Z30
[gcode_macro G32]
gcode:
SAVE_GCODE_STATE NAME=STATE_G32
G90
G28
QUAD_GANTRY_LEVEL
BED_MESH_CALIBRATE
G28
PARK
RESTORE_GCODE_STATE NAME=STATE_G32
[gcode_macro PRINT_START]
gcode:
G32 ; home all axes
G90 ; absolute positioning
G1 Z20 F3000 ; move nozzle away from bed
[gcode_macro PRINT_END]
gcode:
# safe anti-stringing move coords
{% set th = printer.toolhead %}
{% set x_safe = th.position.x + 20 * (1 if th.axis_maximum.x - th.position.x > 20 else -1) %}
{% set y_safe = th.position.y + 20 * (1 if th.axis_maximum.y - th.position.y > 20 else -1) %}
{% set z_safe = [th.position.z + 2, th.axis_maximum.z]|min %}
SAVE_GCODE_STATE NAME=STATE_PRINT_END
M400 ; wait for buffer to clear
G92 E0 ; zero the extruder
G1 E-5.0 F1800 ; retract filament
TURN_OFF_HEATERS
G90 ; absolute positioning
G0 X{x_safe} Y{y_safe} Z{z_safe} F20000 ; move nozzle to remove stringing
G0 X{th.axis_maximum.x//2} Y{th.axis_maximum.y - 2} F3600 ; park nozzle at rear
M107 ; turn off fan
BED_MESH_CLEAR
# The purpose of the SAVE_GCODE_STATE/RESTORE_GCODE_STATE
# command pair is to restore the printer's coordinate system
# and speed settings since the commands above change them.
# However, to prevent any accidental, unintentional toolhead
# moves when restoring the state, explicitly set MOVE=0.
RESTORE_GCODE_STATE NAME=STATE_PRINT_END MOVE=0
[gcode_macro CANCEL_PRINT]
rename_existing: BASE_CANCEL_PRINT
gcode:
M220 S100 ; Reset Speed factor override percentage to default (100%)
M221 S100 ; Reset Extrude factor override percentage to default (100%)
G91 ; Set coordinates to relative
{% if printer.extruder.temperature >= 170 %}
G1 F1800 E-1 ; Retract filament 3 mm to prevent oozing
{% endif %}
;if all axis are homed, lift the hotend to leave room for hot filament to ooze and to keep it clear of the bed.
{% if printer.toolhead.homed_axes == "xyz" %}
G1 F6000 Z10 ; Move Z Axis up 10 mm to allow filament ooze freely
G90 ; Set coordinates to absolute
G1 X10 Y221 F1000 ; Move Printer Head Out of Way
{% endif %}
;set part fan speed to zero.
M106 S0
;bed and hotend are left at the print temps in case I want to restart.
CLEAR_PAUSE
BASE_CANCEL_PRINT
[gcode_macro INJECT_FILAMENT]
gcode:
{% set E = params.E|default(62) %}
M117 Injecting filament
G91
G1 E{E} F2100
G90
[gcode_macro CLEAN_NOZZLE]
gcode:
{% if "xyz" in printer.toolhead.homed_axes %}
{% if printer.extruder.temperature >= 200 %}
M117 Cleaning nozzle
G91
G0 Z10 F10000
SAVE_GCODE_STATE NAME=clean_nozzle
G90
G0 X200 Y305 F10000
SAVE_GCODE_STATE NAME=clean_nozzle_above
G0 Z1.4 F10000
G0 X250 F10000
G0 Y304 X200
G0 Y305 X250
G0 Y304 X200
G0 Y305 X250
G0 Y304 X200
G0 Y304 X250
G0 Y305 X200
G0 Y304 X250
G0 Y303 X200
G0 Y303 X250
G0 Y304 X200
G0 Y303 X250
G0 Y304 X200
G0 Y305 X250
G0 Y303 X200
G0 Y305 X250
RESTORE_GCODE_STATE NAME=clean_nozzle_above MOVE=1
M117 Cleaned!
RESTORE_GCODE_STATE NAME=clean_nozzle MOVE=1
G91
G0 Z-10 F10000
G90
{% endif %}
{% else %}
{ action_raise_error("Please home your axes!") }
M117 Please home first!
{% endif %}
[gcode_macro PRIME_NOZZLE]
gcode:
{% set E = params.E|default(0.5) %} # retract size
{% if not "xyz" in printer.toolhead.homed_axes %}
{ action_raise_error("Please home your axes!") }
M117 Please home first!
{% else %}
{% if not printer.extruder.temperature >= 200 %}
{ action_raise_error("Heat up nozzle first!") }
M117 Heat up nozzle first!
{% else %}
{% if not printer["filament_switch_sensor toolhead_sensor"].filament_detected %}
G90
G0 X200 Y305 F10000
INJECT_FILAMENT
CLEAN_NOZZLE
{% endif %}
{% endif %}
{% endif %}
[gcode_macro EXTRUDER_MAINTENANCE]
gcode:
{% set th = printer.toolhead %}
G0 X{th.axis_maximum.x // 2} Y0 Z{th.axis_maximum.z // 2}

View file

@ -0,0 +1,44 @@
{ lib, config, ... }:
{
services.moonraker = {
enable = true;
group = "${config.services.klipper.group}";
allowSystemControl = true;
settings = {
server = {
max_upload_size = 16384;
};
octoprint_compat = {
enable_ufp = true;
webcam_enabled = true;
};
zeroconf = { };
history = { };
authorization = {
force_logins = true;
cors_domains = [
"*.local"
"*.lan"
"*://localhost:*"
"http://*.lan"
"http://*.local"
];
trusted_clients = [
"10.0.0.0/8"
"127.0.0.0/8"
"169.254.0.0/16"
"172.16.0.0/12"
"192.168.1.0/24"
"FE80::/10"
"::1/128"
];
};
};
};
security.polkit.enable = lib.mkIf config.services.moonraker.allowSystemControl true;
networking.firewall.allowedTCPPorts = [ config.services.moonraker.port ];
}

View file

@ -0,0 +1,11 @@
{ pkgs, ... }:
{
services.octoprint = {
enable = true;
openFirewall = true;
plugins = with pkgs.octoprint.plugins; [
themeify
stlviewer
];
};
}

View file

@ -0,0 +1,39 @@
{ config, ... }:
{
age.secrets.vpnSrx = {
file = ../vpn_srx.age;
owner = "systemd-network";
};
systemd.network = {
netdevs."50-vpn_srx" = {
netdevConfig = {
Kind = "wireguard";
Name = "vpn_srx";
MTUBytes = "1300";
};
wireguardConfig = {
PrivateKeyFile = config.age.secrets.vpnSrx.path;
ListenPort = 51820;
};
wireguardPeers = [{
wireguardPeerConfig = {
PublicKey = "MPvns6jFwZPJvzZtxEDIMSIBBBtBQKBWQ8us3Wgj0mc=";
AllowedIPs = [ "10.80.0.0/24" ];
Endpoint = "65.108.77.254:51820";
};
}];
};
networks."vpn_srx" = {
matchConfig.Name = "vpn_srx";
address = [ "10.80.0.12/24" ];
networkConfig = {
IPMasquerade = "ipv4";
IPForward = true;
};
};
};
networking.firewall.trustedInterfaces = [ "vpn_srx" ];
}

BIN
hosts/srxfdm00/vpn_srx.age Normal file

Binary file not shown.

56
hosts/srxgp00/default.nix Normal file
View file

@ -0,0 +1,56 @@
{ self, lib, ... }:
{
imports = [
self.nixosModules.roles-server
self.nixosModules.filesystems-zfs
self.nixosModules.services-security-tang
self.nixosModules.services-netboot
self.nixosModules.services-monitoring-loki
self.nixosModules.custom-dns-knot
./hardware.nix
./storage.nix
./services/wireguard
./services/nginx
./services/oauth2-proxy
# ./services/openldap
./services/mysql
./services/influxdb
./services/postgresql
./services/minio
./services/restic
./services/grafana
./services/prometheus
./services/keycloak
./services/mailserver
./services/coturn
./services/dendrite
./services/hydra
./services/plausible
./services/forgejo
./services/vaultwarden
./services/hedgedoc
./services/nextcloud
./services/paperless
./services/jellyfin
];
system.stateVersion = "24.05";
networking = {
hostName = "srxgp00";
domain = "srx.digital";
hostId = "8556b001";
};
systemd.network.networks."10-uplink".networkConfig.Address = "2a01:4f9:6b:2573::1/64";
services.knot.settings.server = {
identity = "ns1.srx.dev";
listen = lib.mkForce [
"10.80.0.1@53"
"65.108.77.254@53"
"2a01:4f9:6b:2573::1@53"
];
};
}

View file

@ -0,0 +1,22 @@
{ self, inputs, modulesPath, ... }:
{
imports = with inputs; [
(modulesPath + "/installer/scan/not-detected.nix")
self.nixosModules.hardware
self.nixosModules.hardware-cpu-amd
srvos.nixosModules.hardware-hetzner-online-amd
];
boot = {
initrd = {
availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
];
kernelModules = [ ];
};
swraid.enable = true;
zfs.devNodes = "/dev/disk/by-uuid";
};
}

View file

@ -0,0 +1,22 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw RrFIECbXxeuPEyGxWOHSWr23wh7cJ2ojtxeOuiQG1Tw
HL4pQADIbm67noLw+WHr7VWbkCeGLW0Gg9iW2FinQP4
-> ssh-ed25519 JzjriQ mhhYpWUGZoDWLO9KHbikn+P/sm//F+Rr7+KXWx9elW4
qe/oMpinF6trDHeb5gu1dJ+f4AIbydlQ5Kko+7hcDBE
-> ssh-rsa 6hPx7A
JadRDjvcFPCFeXxmyk3Xlns1jpnlACM6AYX7XCiPwlqxjsdM3z3Ffo3rXg3CD2C+
v7nJ4WJ0vxiGwKy5R2RIpKpV+ufCEpifjlwxmyJMiyW2XT4zKGdFKvDmz487yhIV
l1xQYEfxL9rLrGRu14nFT7ogmpQL1XxDjBu+wRaBamLL/XoMtDQnOh0WxOy606jW
o2BXUpibE637bTQ9I5W8CjRg0vmx9DxfRpe1K/v91dB39TTekxPiK4hqZKN4CfvU
vKua/lT102auTHT9CVnUS9hbfgAlMYL/iOpV7RgjpMLbMn1km9seTzLwmq0ZaKIO
ZFmhPtSAFPziUtgsejlJHF2ubGoOyeZ29Ufch8QemEF4JTg53bavJrSBeo3GH0Ed
khu874/lgzD7qEj+ZobZrY+asHb5vN4bjdiMsSC2P3/h/P1I+a3/UzU1Q+l4zIdi
IUNvEuZassxef1IN7WFMX+58Mnhe5xFIACGDyGmFANX1CwW/SHGJVR50kuBYlivD
tzhCs09LSvA3XCLTmv3xAlPNuVo4yWMtBSIA5wjyI9Aii3LiwrNu7WJZVHm/P7HR
8vtLiUJHqE4/cntL3hdu1y9JT6Q5p+iIOorkVOxP/YYkzber1E4tlB53gX3nn9Zc
UlwcjYu98Rlr7ZQpBHfNlG2kPVk4eB+wQ0FANEpN/DM
-> ssh-ed25519 Dfencg Hhxyaq+w1z3iliBW3YkShIb2/UozH4iDMnkPKINJkn0
tjpcXjYEam9qj6MNlLXbc4i/pTK1UXek2zK7K9E7rfc
--- 8hw3Ug1U7dk7EF0kMJXJVFKGralF+GDOzh6D1gTihHY
 ë
FëUJŽ÷ÅqiïŸÞú±uíX½Þ×fLW,sŽ»Ÿ4zQÝ-û*µØÈYžØPÓÓ¿ùZõtªË½

View file

@ -0,0 +1,81 @@
{ config, ... }:
{
age.secrets.coturnAuthSecret = {
file = ./auth-secret.age;
owner = "turnserver";
};
services = {
coturn = rec {
enable = true;
no-cli = true;
min-port = 49000;
max-port = 50000;
realm = "turn.srx.digital";
use-auth-secret = true;
static-auth-secret-file = config.age.secrets.coturnAuthSecret.path;
cert = "${config.security.acme.certs.${realm}.directory}/fullchain.pem";
pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
extraConfig = ''
prometheus
listening-ip=65.108.77.254
listening-ip=2a01:4f9:6b:2573::1
no-multicast-peers
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
'';
};
nginx.virtualHosts.${config.services.coturn.realm} = {
forceSSL = true;
enableACME = true;
};
prometheus.scrapeConfigs = [{
job_name = "coturn";
scrape_interval = "60s";
metrics_path = "/metrics";
scheme = "http";
static_configs = [{ targets = [ "${config.services.coturn.realm}:9641" ]; }];
}];
};
networking.firewall =
let
range = with config.services.coturn; [{
from = min-port;
to = max-port;
}];
in
{
allowedTCPPorts = [ 3478 ];
allowedTCPPortRanges = range;
allowedUDPPorts = [ 3478 ];
allowedUDPPortRanges = range;
};
}

View file

@ -0,0 +1,185 @@
{ pkgs, config, ... }:
let
name = "dendrite";
domain_name = "srx.digital";
server_name = "matrix.${domain_name}";
admin_domain = "admin.${server_name}";
chat_domain = "chat.${server_name}";
bindHost = "localhost";
in
{
age.secrets = {
dendriteEnvironment.file = ./environment.age;
dendritePrivateKey.file = ./private-key.age;
};
services = {
dendrite = {
enable = true;
openRegistration = false;
environmentFile = config.age.secrets.dendriteEnvironment.path;
settings = {
global = {
server_name = domain_name;
private_key = "$CREDENTIALS_DIRECTORY/private_key";
database = {
connection_string = "postgres:///${name}?host=/run/postgresql";
max_open_conns = 90;
max_idle_conns = 5;
conn_max_lifetime = -1;
};
logging = [{
type = "std";
level = "info";
}];
metrics.enabled = true;
trusted_third_party_id_servers = [
"matrix.org"
"vector.im"
"nixos.org"
];
dns_cache = {
enabled = true;
cache_size = 4096;
cache_lifetime = "600s";
};
};
media_api.dynamic_thumbnails = true;
mscs.mscs = [ "msc2836" ];
client_api = {
registration_disabled = true;
rate_limiting.enabled = false;
registration_shared_secret = "$REGISTRATION_SHARED_SECRET";
};
sync_api = {
search.enable = true;
real_ip_header = "X-Real-IP";
};
federation_api = {
key_perspectives = [{
server_name = "matrix.org";
keys = [
{
key_id = "ed25519:auto";
public_key = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
}
{
key_id = "ed25519:a_RXGa";
public_key = "l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ";
}
];
}];
};
turn = {
turn_uris = [
"turn:${config.services.coturn.realm}:3478?transport=udp"
"turn:${config.services.coturn.realm}:3478?transport=tcp"
];
turn_shared_secret = config.services.coturn.static-auth-secret-file;
};
};
};
postgresql = {
ensureDatabases = [ name ];
ensureUsers = [{
inherit name;
ensureDBOwnership = true;
}];
};
postgresqlBackup.databases = [ name ];
nginx.virtualHosts = {
${domain_name} = {
forceSSL = true;
enableACME = true;
locations."= /.well-known/matrix/client".alias = pkgs.writeText "matrix-client" (
builtins.toJSON { "m.homeserver".base_url = "https://${server_name}"; }
);
locations."= /.well-known/matrix/server".extraConfig =
let
server."m.server" = "${server_name}:443";
in
''
add_header Content-Type application/json;
return 200 '${builtins.toJSON server}';
'';
};
${server_name} = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${bindHost}:${toString config.services.dendrite.httpPort}";
};
${admin_domain} = {
enableACME = true;
forceSSL = true;
locations."/".root = pkgs.synapse-admin;
};
${chat_domain} = {
enableACME = true;
forceSSL = true;
locations."/".root = pkgs.element-web.override {
conf = {
brand = "srx digital";
branding = {
welcome_background_url = "https://images.unsplash.com/photo-1480843669328-3f7e37d196ae?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D";
auth_header_logo_url = "https://${domain_name}/favicon.svg";
};
default_server_config = {
"m.homeserver" = {
base_url = "https://${server_name}";
server_name = "${server_name}";
};
"m.identity_server".base_url = "https://${server_name}";
};
help_url = "https://${domain_name}";
};
};
};
};
prometheus.scrapeConfigs = [{
job_name = "dendrite";
scrape_interval = "60s";
metrics_path = "/metrics";
scheme = "http";
static_configs = [{ targets = [ "${bindHost}:${toString config.services.dendrite.httpPort}" ]; }];
}];
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${server_name}:443" ];
tags.host = server_name;
interval = "10m";
}];
http_response = [{
urls = [ "https://${server_name}" ];
tags.host = server_name;
interval = "10m";
}];
};
};
systemd.services.dendrite = {
serviceConfig.LoadCredential = [ "private_key:${config.age.secrets.dendritePrivateKey.path}" ];
requires = [ "postgresql.service" "keycloak.service" ];
after = [ "postgresql.service" "keycloak.service" ];
};
}

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw /AFAsp3kPh6uhjFn9GjZN84mywODGn3dO4JDRu/Ltk8
9SWydf6/WZl1V9KpxVhslYZ2afTfFIoSxa1qeKi34KA
-> ssh-ed25519 JzjriQ YsdZUPErOFyRsPnDh/A7t7JnrGWVvs8lkcQh/vRVDh8
20qNy3Ll2CXS6xcmcNOAUNFCvD7jvFrzmWnNPSdNAjo
-> ssh-rsa 6hPx7A
uQJDNP/W09WfODdzLW/oHrby2IbHsPwYqIqrnhnJQpMBaE27GcQyQ3/oX0fpFVOY
yY3LSMSa16O5pP+T9f/SC8Kklt/AKJ9bn/9E35olnhv9X9Qh/5aCk7FmAXvACguu
0OYyB/c6r8e8t2C9uitvYNwa2W5v7I6QdpiTVZBCncp/VN0aPsS+kAVxtKQndbjF
7rUmN0MtqeMP37/cIJHnK9ZoGuotkW0h+heI1Wjd808Kx4kdCJnon/NPJ873CEFQ
XEK29u0wRRRBJPKdNnWu6KDViUIoHynl4Rl0Qzi4LA20X+6+nlx2Gel99tSBSu7p
Bc5m0xiYYy69QamuSmEesvyUB//zIBPQToxiQdZD4n++9d/+HDQ/QYOOEHlpi5DA
KpHqCte6eKStSlKAvAt7A4ccSMuVXebT9NhsAUcuTp8i6z8oLH0N/H/L/MDgzgvi
FtiKdThJYlyiLDziKX6LcRVaeJxg+sLBwnNfrWVuWoka2q37LbcUlFrSDF0Us0dl
0/Y/rAC6KJztcc/zBkbnuWCZlaaBtVcLE6vpbicCTss8R+UhJCC7cin1Cnhvqgl3
lKmln3MpsAniHCje/SWgqRyXx6fVpCDKwe+qIW1SaCekQWUo/hpJuTLfoPvBdozx
kXCiAhp9ZvHB3+mA9cUzET/DQ6WVjIGpHNncv/oKEW0
-> ssh-ed25519 Dfencg qd8p5uFbUwDuSQn2Ql6uDYvB49V1auQCsVge0jUJzGo
amF9Mfa/Wgym1Wf0s8KoutpZvIFaYIELGd54ZG+0VSM
--- cBsNcjTDQXyuYeSA60Buo9IhJ45tzsBjplwrtbVgLEI
ÑÏûCÓ|-š}ѼPÞ êýµr†[ú)‚¼+6úÄŽÔqü”å@$1—㻥7t…ªPwZ‰K¡²ô®-–®‹(gcNÿ0ÈkZœâ·»ä](>;…v˜qßìrÉöjv®5þ\UÎ{ÊZ×a^Òè§ÊÇ¢ù3dh÷q,[àB|VÞFêù¢Ÿ«˜<0E>lu|%S‡~<7E>¥/±îÙgwh

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw JXCOHhmEwyJskFn5WwcNHZmg7QlabhxVgxgikGertgE
5LlKRT0UivqcDoaXPFxtMmCsbmpRmexqK1iOcthJW7k
-> ssh-ed25519 JzjriQ fcLWfzAqfTVyWTf0ac8PqX0WtxbhSmxVoy95CPHjEFQ
IAASir2G1vnmkwBWTI/YLo0l39/cllqxGnuleMiEZmI
-> ssh-rsa 6hPx7A
Y+TtSaCA6ViMsNbsCOSUeTv0jhtR6oKEn7A+H+lwJ778lmqxjSAAiChiU8aIGQ5H
+n27+w696fISNkvRYYr1qzkqCA5qCL/gBAzTVMt5zungOem6pyF2jsdiwUiW4jFv
caVcfa0LhnHkQtgcc4qxSomxmQ8jnSzmsXBpR0EQddTZl++fxmhlpO47JI1zHUV1
rTU7kYR4/6+Y8+qeImySexDAfxfSbig+O9v3Ph5yBM/dQKTKGCWilUSXGvy9sG/0
hru1lWZ9KU/Hqu+19Ng28dbg1+nVjq13Q7vkwg4dgf5s9CJBzEp+A3kjZn6e5m+G
G3pv5LwfrihKfrEJVT10ARBw8ZVJIX/MD54xvIdOIndTg7LHJq0CIdL6yBjnpm5U
J+jf13HMoXLgf/HzxBA2o2dsz6zIiLfz5kLGXwNAO1nKrmQZs1jOpRcR1kksM4Ih
XYGT4w3unkpEhRHwnNZg6W3d6+5impU7OLGGRAZYdu7LwpcgXY1IA0XfqLc7n+VW
AKfaDlHHqwZAQy/ccNyaeuFf28R3uxkNvFcGeWOFw3A5iEyzBqyAHsbJJ5vD/LmB
3LktOTfrqz6JUKQSy4OI+zlt3yKLC4x/ot9rAWMjHKrtU4Ugf/ZSgzkXao+RfpiZ
FURQfpoHxT3in6bxjo+p5gsbtdElhqDL9bQdOLXNnx8
-> ssh-ed25519 Dfencg rGosZgGJT6tEZiJcZte/yBr8ymn7R/VCoa9KimfNNQk
S0JvXPtI/qwfA5J+M19ecFk0QsUM8KJfeivzzzvKyFA
--- PQcVC0cWPhooEvu7xzoT0mUVGX9H0gqbewEtLogLMEU
p\ lüm$Õ­Xa£—.«‡°®m0 ˜ K8þψqMSEq<>¦RÏ«Wýô(ëØ}à¤<C3A0>Üð<>íl<tUmLW€/£ŽöBaA²·½còR–¦¼@: RbéL{œ8Á‡6HÇ™ãÝ«R:A%—òݯA4ÍP/½‘[ÑrꉗJVû\§ÓƱWŸÚGÛZµüi©{”Ó8ü*™yýij» ¬

View file

@ -0,0 +1,108 @@
{ lib, config, ... }:
{
# imports = [ ./runner.nix ];
age.secrets.forgejoMailerPassword = {
file = ./mailerPassword.age;
owner = config.services.forgejo.user;
};
services = {
forgejo = {
enable = true;
database.type = "postgres";
mailerPasswordFile = config.age.secrets.forgejoMailerPassword.path;
lfs.enable = true;
settings = {
# https://github.com/go-forgejo/forgejo/blob/main/custom/conf/app.example.ini
DEFAULT.APP_NAME = "srx digital code base";
server = {
HTTP_PORT = 3030;
HTTP_ADDR = "127.0.0.1";
DOMAIN = "code.srx.digital";
ROOT_URL = "https://${config.services.forgejo.settings.server.DOMAIN}/";
LANDING_PAGE = "explore";
};
database.LOG_SQL = false;
metrics = {
ENABLED = true;
ENABLED_ISSUE_BY_REPOSITORY = true;
ENABLED_ISSUE_BY_LABEL = true;
};
service = {
ENABLED_USER_HEATMAP = true;
ENABLE_TIMETRACKING = true;
REGISTER_EMAIL_CONFIRM = true;
ENABLE_NOTIFY_MAIL = true;
DEFAULT_KEEP_EMAIL_PRIVATE = true;
ENABLE_CAPTCHA = true;
CAPTCHA_TYPE = "image";
VALID_SITE_URL_SCHEMES = "https";
};
mailer = {
ENABLED = true;
FROM = "no-reply@srx.digital";
USER = "no-reply@srx.digital";
SMTP_ADDR = "mail.srx.digital";
PROTOCOL = "smtps";
SMTP_PORT = 465;
};
cron = {
ENABLED = true;
RUN_AT_START = true;
};
api.ENABLED_SWAGGER = true;
actions.ENABLED = true;
picture.DISABLE_GRAVATAR = true;
other = {
SHOW_FOOTER_VERSION = false;
SHOW_FOOTER_BRANDING = false;
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
};
};
};
nginx.virtualHosts."${toString config.services.forgejo.settings.server.DOMAIN}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${toString config.services.forgejo.settings.server.HTTP_ADDR}:${toString config.services.forgejo.settings.server.HTTP_PORT}";
};
postgresqlBackup.databases = lib.optionals config.services.forgejo.enable [
config.services.forgejo.database.name
];
prometheus.scrapeConfigs = [{
job_name = "forgejo";
scrape_interval = "60s";
metrics_path = "/metrics";
scheme = "https";
static_configs = [{ targets = [ "${config.services.forgejo.settings.server.DOMAIN}" ]; }];
}];
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${config.services.forgejo.settings.server.DOMAIN}:443" ];
tags.host = config.services.forgejo.settings.server.DOMAIN;
interval = "10m";
}];
http_response = [{
urls = [ "https://${config.services.forgejo.settings.server.DOMAIN}" ];
tags.host = config.services.forgejo.settings.server.DOMAIN;
interval = "10m";
}];
};
};
networking.firewall.allowedTCPPorts = [ config.services.forgejo.settings.server.SSH_PORT ];
}

View file

@ -0,0 +1,22 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw jT12Vok7oCoHwjYXiJbXak9fKwfcAoZvniEBmaDcPBs
Qn0Pt+4ItG3W/4FmGwS4bmoKLOolJIDvBMe7/yjJDpA
-> ssh-ed25519 JzjriQ L9XwYsGzK7D5bse4rtJI3NlgTsb6kShilXXKLK4wjD0
LrZ7K+tOp38fh0i0v6y0ngWSHIuDiu1TA7OX5AWrNWs
-> ssh-rsa 6hPx7A
CVypkGjpStOfHiWyi0/edlGJVO2CsLj2DGnbpCh+EiTPp5MIimXJsv547uPNCdqV
XxFwbGU833q8QLju3ESfx6bfPAwd7waaEXmovcPgIz3YXO+9cZNITFYwwfBWUWx0
w7e5mp6XtYxxLIvIjz9wb/S9W+/h9LAqxoOVdevj6WQt5estpNgkBVg9eilZInsL
h66xccTpuEdzhPgTcxmFTPYJf0fScm6DQoao1MzZV7Pe9XD/ryqngw0JHBlokd68
HNvLy49tR9MmPbML5tVhK5SHkUYD7DqCkKKxafXiVtX/kAn2MqTMPVw8nqbivgvZ
6zy5/nrQfbCS8GyFRkjcIydAFzaVh4u1bczuBS7X1YEkqJRyw3OcdaAfo/7IxHK6
TLBR1IXELRIsVhwysDaZSsr2cLnuTC8HRC9p2qdnb0/ijKBt/Ud6vIlozUB6nD5l
ybG/AQaUS3qniieFVHKCZ1ky8Fc52qIuRrRQ77cFg6a947ag79YyJV26hbyEum8Y
/rV47QEI1o8NxSXDs8ivQN32b5OKEHVEqi5G82gmA+JR1txsnYwxgvwRcpqfxhp0
yYQIltOoauObii0FpeL7CgcInTZR6M5Pirci/ockBOvRh69VvSx9galc7ae4izCR
mkT8p14JkDcbkkCvA3StBdcHJn3cYk8nS21A5np2R9U
-> ssh-ed25519 Dfencg sRiDIIupHKr2ntrv85rAARp4d/v4Nu+eh7WbVZHBFRU
X5mxkEQkN1zaDBOKVP6bjwCkO+BbFllT+IviAhjXutg
--- s9jOcB3p945ujdml4LdOf7UGJeXWl+HPwCOItXCDMtY
zñÁ:”(•+kIÝC®»PXœ Äa
õF¾0*ÇäÊ-Ú{¿'4<>]:Ý<ñ<>•?êq¢`óŒ+|®

View file

@ -0,0 +1,59 @@
{ self, lib, pkgs, config, ... }:
{
imports = [
self.nixosModules.services-container-docker
];
age.secrets.giteaRunnerTokenSRX = {
file = ./runnerToken.age;
owner = config.users.users.gitea-runner.name;
};
services.gitea-actions-runner = {
package = pkgs.forgejo-runner;
instances."${config.networking.hostName}" = {
enable = true;
name = config.networking.hostName;
url = config.services.forgejo.settings.server.ROOT_URL;
tokenFile = config.age.secrets.giteaRunnerTokenSRX.path;
hostPackages = with pkgs; [
coreutils
direnv
git-lfs
gitFull
nixVersions.latest
nodejs
opentofu
];
labels = [ "ubuntu-latest" ];
};
};
users.groups.gitea-runner = { };
users.users.gitea-runner = {
group = "gitea-runner";
extraGroups = [ "docker" ];
isSystemUser = true;
};
systemd.services."gitea-runner-${config.networking.hostName}" = {
serviceConfig = {
DynamicUser = lib.mkForce false;
Group = config.users.users.gitea-runner.group;
Restart = lib.mkForce "always";
};
## FIXME: act_runner level=error msg="fail to invoke Declare" error="unavailable: dial tcp [2a01:4f9:6b:2573::1]:443: connect: connection refused"
requires = [
"nginx.service"
"forgejo.service"
];
after = [
"network.target"
"nginx.service"
"forgejo.service"
];
};
}

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw 1QcQUWrDCoAbMO+vVjHW8/7TVERv/u/yYg5FEWDNIjs
9grSDCY6vp1JCEVFMlSPVl52/3hAXid955IlwwEQ8N4
-> ssh-ed25519 JzjriQ EixF6itE/FEMMmVg5tvuvolhRzljxHOtHrNUBDSX/2E
6EGocnqczp6fV/hNtD4r9cQuNP3AXrHBzwkI4umsnNE
-> ssh-rsa 6hPx7A
Zvw2oBZweTCbDjm/246mYtzuES9+qb0NHIp1c2ins+yDqD3WWEz0clfb1zw+/QNL
5g9lKuADFhBYksMA7M+ZmixMfWFeYt/Df88qpNrXCTLzkKdM31tc2xBmcDRsaC3m
qhrUu+V45rZJAy2hmvEcizWhuOEbvIFkpWuX72FEjXPNUHECyawBC5fl9xstGGZV
jPXKt0rq7cMnmFEw/q510ApsY8ZYa1B7Oz6ySx0YBD0cLnTheqPnWAQ0apXtca4a
k6w/jd1B0nJ9rF8HotQ+HUC579GoeGhWqMPaXIAEUlM1rwxik3lW3C5eA/JqRqNq
ZFvmG7ivQ0eySBxSHmaDp/IM6joLY1k4ECi6duRepmKt+B8rPfP79XLRqtyf+oiL
9ELrXCg4je6qSZZcrWI56Ag6Hz7/Ox4hlKnGjFfSqpZnix1EF6H5MXfidztJIftb
mQEFt1sst1Z3x5x3ILjAb+8xGHoMaAQd/QKdlfqzPrF8Q7uV5xoMBEOYggjmG33L
TKxJobVpE5vXEVO68whdPTcZ+mCZc06W7+ouJOWCmSJ1sfY8sK5qKiEU/2URvzQR
vslFUJ2DgwHNY5qoHs+FmUCoBT8t4eS+3rKJWDPAF+enBD+L5z2AeRX5vFNmrlFV
5+De4ZreFd41lTSS+hTrnxCHHqbVrTVctTp3z/9cR0A
-> ssh-ed25519 Dfencg AhPbUI130CBwlCyxzxGLpQS2K7KfKfqEV/QxcSDMzBE
FBM5u2+4EhYvPlCjr/txAZ+hf1RkM7D8GuBEBm4NXyc
--- My/nXxUyWQWXzxxUF/buVxAJa1fQsChLTVq7Ccq8HP8
Ÿ‡érz áj}ú˜Ý&/—iæ™G7ê%.èA zß@¥EI¹ÇÚlm÷c€O½9Žqr<71>nôŽîy½æ>A/ÀÂÃ3}<7D>ñ

View file

@ -0,0 +1,99 @@
{ config, ... }:
let
name = "grafana";
domain = "metrics.srx.digital";
in
{
age.secrets = {
grafanaOidcSecret.file = ./oidc-secret.age;
grafanaOidcSecret.owner = name;
};
services = {
grafana = {
enable = true;
settings = {
analytics.reporting_enabled = false;
server = {
inherit domain;
root_url = "https://${config.services.grafana.settings.server.domain}";
http_port = 3003;
enforce_domain = true;
enable_gzip = true;
};
database = {
type = "postgres";
url = "postgres:///${name}?host=/run/postgresql&user=${name}";
};
smtp = {
enabled = true;
host = "mail.srx.digital";
from_name = "SRX Metrics";
from_address = "no-reply@srx.digital";
user = "no-reply@srx.digital";
key_file = config.age.secrets.forgejoMailerPassword.path;
startTLS_policy = "MandatoryStartTLS";
};
security = {
cookie_secure = true;
disable_gravatar = true;
strict_transport_security = true;
admin_user = "service";
};
"auth.generic_oauth" = {
enabled = true;
auto_login = false;
name = "OpenID";
icon = "signin";
allow_sign_up = true;
client_id = "metrics";
client_secret = "$__file{${toString config.age.secrets.grafanaOidcSecret.path}}";
scopes = "openid profile email";
auth_url = "https://id.srx.digital/realms/srx/protocol/openid-connect/auth";
token_url = "https://id.srx.digital/realms/srx/protocol/openid-connect/token";
api_url = "https://id.srx.digital/realms/srx/protocol/openid-connect/userinfo";
};
};
provision.enable = true;
};
postgresql = {
ensureDatabases = [ name ];
ensureUsers = [{
inherit name;
ensureDBOwnership = true;
}];
};
postgresqlBackup.databases = [ name ];
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${config.services.grafana.settings.server.domain}:443" ];
tags.host = config.services.grafana.settings.server.domain;
interval = "10m";
}];
http_response = [{
urls = [ "https://${config.services.grafana.settings.server.domain}" ];
tags.host = config.services.grafana.settings.server.domain;
interval = "10m";
}];
};
};
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://${config.services.grafana.settings.server.http_addr}:${toString config.services.grafana.settings.server.http_port}";
proxyWebsockets = true;
};
};
}

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw C7WQ0F+OHuWXvlbxVoaAyZh1+CVIISqijYrINi6qywU
V3Kupx4Ht1qZKNwxBDBQ1i6JZBY0DYuLv6rn7EYc01Y
-> ssh-ed25519 JzjriQ LxStntQkytwa0u8nL3B7RyicbKDdCRWxeDmi8Ao0NQ4
c52RbPkcOILVtf2SOhomRElFYEO4YKHJAWAqF0uupvc
-> ssh-rsa 6hPx7A
OKfMxzebbZAlQSgfxsryiMb9KIS7q2nlMHy4ZAyH+2+zGLK4l+FsnyAMosY5vDcF
Iw5qa1CbRijtRDg5g9jh2tDej+Q35Z+xF7r+AauCEFfEz0LDxF74wrWOp4GtxWU6
NdQaDmlGRcibx07W1joQAruJJLuxijw0MRz4zczZ6sc72HLbBz3xE6cCub4CDv7I
QSGvMQ0cCKj/tlFRA35Ju3GlWIZaMBtRli5DSBbsjkkb+0lr2g/KxLF7nNlKb8DV
9wSRZ34DBB/cEAbKqHxnr4Dbg8qOWPuYN47KjZQf2Fp4B1TXw0EAQb0a+JTWDI+g
uM0kq26k4TjiLObo/dGXQw/70XLPSCXlquwI81a3XXWUjE2vkoq2wgTPIbcO+Zka
WesYDIbMiVMkH9CORtn/00goJjU2KWfbHp3rxQ8PVu6juXPnxJuDNAPNWmE1/mxN
TIe2QKuvomf+8oWpM428CFObPTgSvV1A1pWTRmX8CSgAzBfzgjUfuco1UrzOqM5R
qpS7YfgpoHmOGWFILwjxwgEa6/VLYajvNmxpZtF1XTkReWJsvfmcan7g9P6+kW+a
C4CRAyZ4UotJWdurcMIYhpG0AbxnOQ43uqWuXSx26rQdPZSt+DxdrnigoyqMCmFR
byiIg97v/7aRNApPjoo9LiCE3kWNhD71Z0oAxY7U7uM
-> ssh-ed25519 Dfencg dTb8RftZcE9uEQfKul4jVnO2lHlLPigN4U8PAYHEcBk
bTFhBh7Rw7grDwXnZSuol/pppLMolAxafYVdNVELxHg
--- HTwqUOKnllUy3ADExAh6DdIHGj9dCMRBHEIIxRE7f0g
”-Ř—Ło¨$űJUw|x©";yüJ !ôÉBŽ÷;Sz­¤Ł˙D”v÷ç+J˘%Ăufő´śţEq%Ť€u(ś

View file

@ -0,0 +1,97 @@
{ config, ... }:
{
age.secrets.hedgedocEnvironment = {
file = ./environment.age;
owner = "hedgedoc";
};
services = {
hedgedoc = {
enable = true;
environmentFile = config.age.secrets.hedgedocEnvironment.path;
settings = {
domain = "pad.srx.digital";
host = "127.0.0.1";
port = 3005;
allowAnonymous = true;
allowAnonymousEdits = true;
allowEmailRegister = true;
allowFreeURL = true;
allowGravatar = false;
enableStatsAp = true;
hsts.enable = true;
protocolUseSSL = true;
useCDN = false;
csp = {
enable = true;
upgradeInsecureRequest = "auto";
addDefaults = true;
allowFraming = false;
allowPDFEmbed = false;
};
db = {
database = "hedgedoc";
dialect = "postgres";
host = "/run/postgresql";
};
oauth2 = {
baseURL = "https://pad.srx.dev";
userProfileURL = "https://id.srx.digital/realms/srx/protocol/openid-connect/userinfo";
userProfileUsernameAttr = "preferred_username";
userProfileDisplayNameAttr = "name";
userProfileEmailAttr = "email";
tokenURL = "https://id.srx.digital/realms/srx/protocol/openid-connect/token";
authorizationURL = "https://id.srx.digital/realms/srx/protocol/openid-connect/auth";
scope = "openid email profile roles";
clientID = "pad";
clientSecret = "$CMD_OAUTH2_CLIENT_SECRET";
};
};
};
postgresql = {
ensureDatabases = [ config.services.hedgedoc.settings.db.database ];
ensureUsers = [{
name = config.services.hedgedoc.settings.db.database;
ensureDBOwnership = true;
}];
};
postgresqlBackup.databases = [ config.services.hedgedoc.settings.db.database ];
nginx.virtualHosts."${toString config.services.hedgedoc.settings.domain}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass =
"http://${config.services.hedgedoc.settings.host}:${toString config.services.hedgedoc.settings.port}";
};
prometheus.scrapeConfigs = [{
job_name = "hedgedoc";
scrape_interval = "60s";
metrics_path = "/metrics";
scheme = "http";
static_configs = [{ targets = [ "${config.services.hedgedoc.settings.host}:${toString config.services.hedgedoc.settings.port}" ]; }];
}];
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${config.services.hedgedoc.settings.domain}:443" ];
tags.host = config.services.hedgedoc.settings.domain;
interval = "10m";
}];
http_response = [{
urls = [ "https://${config.services.hedgedoc.settings.domain}" ];
tags.host = config.services.hedgedoc.settings.domain;
interval = "10m";
}];
};
};
}

Binary file not shown.

View file

@ -0,0 +1,209 @@
{ lib, pkgs, config, ... }:
let
host = {
hydra = "build.nix.srx.digital";
cache = "cache.nix.srx.digital";
};
job_name = "hydra";
listenHost = "127.0.0.1";
s3 = {
host = "s3.srx.digital";
bucket = "nix-cache";
region = "eu-central-1";
scheme = "https";
};
metrics = {
notify = 9161;
runner = 9162;
};
in
{
age.secrets = {
hydra-env-secret = {
file = ./secrets.age;
group = "hydra";
mode = "0440";
};
hydra-private-key = {
file = ./private-key.age;
group = "hydra";
mode = "0440";
};
};
nix = {
settings = {
allowed-uris = [
"github:"
"gitlab:"
"https:"
"git+https:"
"git+ssh:"
];
trusted-users = [
"hydra"
"hydra-evaluator"
"hydra-queue-runner"
];
allowed-users = [ "hydra-www" ];
builders-use-substitutes = true;
secret-key-files = config.age.secrets.hydra-private-key.path;
};
buildMachines = [{
hostName = "localhost";
sshUser = "hydra-queue-runner";
systems = [ "x86_64-linux" "aarch64-linux" ];
supportedFeatures = [ "kvm" "nixos-test" "big-parallel" "benchmark" ];
maxJobs = 8;
}];
};
systemd.services = {
hydra-server.path = [ pkgs.msmtp ];
hydra-queue-runner = {
path = [ pkgs.msmtp ];
serviceConfig = {
EnvironmentFile = config.age.secrets.hydra-env-secret.path;
LimitNOFILE = 65535;
};
restartIfChanged = false;
wantedBy = lib.mkForce [ ];
requires = lib.mkForce [ ];
};
hydra-notify = {
path = [ pkgs.msmtp ];
serviceConfig.EnvironmentFile = config.age.secrets.hydra-env-secret.path;
};
hydra-evaluator.serviceConfig.EnvironmentFile = config.age.secrets.hydra-env-secret.path;
};
services = {
hydra = {
enable = true;
inherit listenHost;
hydraURL = "https://${host.hydra}";
logo = pkgs.fetchurl {
url = "https://code.srx.digital/avatars/af0c14fc152841cfdcbe4ab098278c35d6fab7cfb236a9744c19cd299d45ef5d";
hash = "sha256-mEpreI0azCqPMA3jGT4V5FJN0e94nFTMAIzP+HfufCQ=";
};
notificationSender = "no-reply@srx.digital";
smtpHost = "mail.srx.digital";
useSubstitutes = true;
extraConfig = ''
email_notification = 1
max_db_connections = 350
max_concurrent_evals = 1
<git-input>
timeout = ${toString (60 * 60)}
</git-input>
<Plugin::Session>
cache_size = 32m
</Plugin::Session>
<hydra_notify>
<prometheus>
listen_address = ${config.services.hydra.listenHost}
port = ${toString metrics.notify}
</prometheus>
</hydra_notify>
queue_runner_metrics_address = ${config.services.hydra.listenHost}:${toString metrics.runner}
store_uri = s3://${s3.bucket}?endpoint=${s3.host}&region=${s3.region}&secret-key=${config.age.secrets.hydra-private-key.path}&write-nar-listing=1&ls-compression=br&log-compression=br
binary_cache_public_uri = https://${host.cache}
upload_logs_to_binary_cache = true
compress_build_logs = false
max_output_size = ${toString (10 * 1024 * 1024 * 1024)}
'';
};
nginx.virtualHosts = {
"${host.hydra}" = {
forceSSL = true;
enableACME = true;
locations = {
"/".proxyPass = "http://${config.services.hydra.listenHost}:${toString config.services.hydra.port}";
"/static/".alias = "${config.services.hydra.package}/libexec/hydra/root/static/";
};
};
"${host.cache}" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost${config.services.minio.listenAddress}/${s3.bucket}/";
extraConfig = ''
proxy_set_header Connection "";
chunked_transfer_encoding off;
'';
};
extraConfig = ''
ignore_invalid_headers off;
client_max_body_size 0;
proxy_buffering off;
'';
};
};
postgresqlBackup.databases = lib.optionals config.services.hydra.enable [ job_name ];
prometheus.scrapeConfigs = [
{
job_name = "hydra";
metrics_path = "/prometheus";
scheme = "https";
static_configs = [{ targets = [ "${host.hydra}:443" ]; }];
}
{
job_name = "hydra_notify";
metrics_path = "/metrics";
scheme = "http";
static_configs = [
{ targets = [ "${config.services.hydra.listenHost}:${toString metrics.notify}" ]; }
];
}
{
job_name = "hydra_queue_runner";
metrics_path = "/metrics";
scheme = "http";
static_configs = [
{ targets = [ "${config.services.hydra.listenHost}:${toString metrics.runner}" ]; }
];
}
{
job_name = "hydra-webserver";
metrics_path = "/metrics";
scheme = "http";
static_configs = [{
targets = [ "${config.services.hydra.listenHost}:${toString config.services.hydra.port}" ];
}];
}
];
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host.hydra}:443" ];
tags.host = host.hydra;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host.hydra}" ];
tags.host = host.hydra;
interval = "10m";
}];
};
};
users.users.hydra-queue-runner.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPtiFwECxheHv30ELg61uS6Ixc0QAIdG26BGl5f2+RsJ hydra-queue-runner@srxgp00"
];
}

View file

@ -0,0 +1,24 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw n3Z3q336cK7bhfkYbK4QN2FW15bHXSzEBZ2yMPE16Qs
3xhoh7ajmNN4JVp8FFUeFvFysSRsxW7uwvlecvAeLA0
-> ssh-ed25519 JzjriQ I+ZfvxfUK3bxFHDjqPr1aRNNVRK4wT9EGZSrPNZzWT4
sLyL1zzVQBfvt9+WjqBXtiDKOsEpzzh9t0I0AfOx7dk
-> ssh-rsa 6hPx7A
WW4cpr4fyH58Z1PoM0S/O9sHATvN0r043jj7WLd6nhff0LBurAh3x1QVBeWOpAz5
dGekgdxXql3R76fNyUVZeC1pKRKrZD4QI3Ovbcb3VFYLvwuAIf93gTARxJrMpozc
KZ5+jBX7tw0cwyvAcZBY5pMQmJOFsMX2qmh06rufX4SpvPeh7I3FtFS8MOGY4hVW
mhMxuyryT9DhKXT0ck06AAjcr5x4pZP8CjbVZipjQ0HXsg44Lsqn4XQTI2W2VN7K
8bSml5nJ9oyeKBKKJccdlq7ydMqCKyRS1SEvET4S6tbNnuAW4KYKXfc6FvoQSJ/X
Yc+HnnbivSCTfsbA2AW0i1n3NuXpA2Oxo+z3BDXemX6mmbqKFgjxVyl3rUtAakAW
BsjpVKFnsJ1cbIrt0uyup7WQ+DnHKf0b/zqOCKvmBj04kQfEvcSPhI/N3e8bqtDr
X9D5ej7f5LbH/0NMNMAIVQuVqYRZ9AkA0ewautyc8KnE+lmW/5rDR0FwtC0ZupK0
RFevUeHg1p6bWqtiF9aFn3TAR9M2lCKK8IIo5k5oc07uKdQpUdxTtqdw7ABRM0m7
TvbxYHwYMfSNucKWpW9grJDMcczfdtivdlnI61m5B1VDG7FmavmqqCS5GCCZE+bO
iOGFDBO+zk3xuU0KBy9/xfr2SK1eDTKYD4VREa8tAl0
-> ssh-ed25519 Dfencg nTEW+fbvaSeUOA3OekA416m8e+FNTY+BD1qImGj7JRw
Hz5/IOgsOjy4l1V2ajavDlx1cNlO6HQ19y1WurUcYR0
--- 49LYxbN12cDPp4KAg6tlytMyYHjulnZY1NFp9QdWq5E
6‰Eà“ñ‰ªòQÒˆ*[àED
è^øÃ?K“๻xaxûPk¯ŒÚ¬E<>ñ4×ÇÔ/Õ ×ÞR)5Þ¯«]õ.n=Dk@M
m
Ø<EFBFBD>@"›¨¹|ûýV\¥ø[9Ãì8e1­Œ¢êx”ââ©|bˆj¿~Åÿ5N"ºžì3Ç5Dûô€¨Ë}q×¥

Binary file not shown.

View file

@ -0,0 +1,43 @@
{ pkgs, config, ... }:
let
host = "tsdb.srx.digital";
in
{
environment.systemPackages = with pkgs; [ influxdb2-cli ];
services = {
influxdb2 = {
enable = true;
settings = {
reporting-disabled = true;
http-bind-address = "127.0.0.1:8086";
};
};
grafana.provision.datasources.settings.datasources = [{
type = "influxdb";
name = "influxdb";
url = "http://${config.services.influxdb2.settings.http-bind-address}";
}];
nginx.virtualHosts."${host}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${config.services.influxdb2.settings.http-bind-address}";
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,25 @@
let
host = "media.srx.digital";
port = 8096;
in
{
services.nginx.virtualHosts."${toString host}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://srxnas00.vpn.srx.dev:${toString port}";
};
services.telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
}

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw OBYvz2ORZFwdIvca0uIXDXWdpfRiaFp/5G7syyGrcyU
vxYdkAiCVBfqzeqKfLFI9PsuDo7DhCCE5b/igNePfPk
-> ssh-ed25519 JzjriQ Dsy0LuOggSmN77ZPMWFzY6xOGi88uiN2prxrUeF/lW0
BoLCPpQSKQM/GE+oAipiKp1n9Q3ChOXQIdGlnhGof80
-> ssh-rsa 6hPx7A
npus090BSB1GRvneHlBqi1QZtPCIOTN+QMKCXSitoDYpyh7sJu5qGNmFrRE/g9+I
Iwoj+LNiV95YZVLl2Ta3/AiLXNOzcHp/JixDmOfn8cT3NrT3TF6kXLWBodqHIT3b
4tWsUn08rwzG3lrNKabudB/63Qpn8Hm1AzNPhQaBLs2gfHTGsKJluuqDggM/i/B0
t2DkTvYNLvyKkYl4SFV2IYN+QlL0USuNiLsMUObbFpSl1Djh1oI75sibFMywkhbM
iy1/R04JvEXSmsZSVr9OU/q0dKR1w2b1W3rq8018EB9UVvbpD7NkmNqO1UhmECtM
HM/e+gslGHdkZTZuAYx/cUayA4QlWUOiM9KseoXRYjhiEEJ1q/yTst4AkmZIb2bF
ZG5D2WlPF/wP7Y/hwZv4kV76390DRSP8qyetWEKyM5G//DS1S44T6g4FXa1HtOLV
SkuixhTtszX3NYi1/0GiUhKujKicADeL14q0TroKI49ScqZEfYYo2yBunRVqxltQ
dV50wegZRHCAALGYVuC7r33Ke1Y6zLmThZYpaOc5OwpiLHGFNsYFe8VOLw8Z6yz/
YkAZncDH/ej20YzKIWF7A6L32shw2mp8anCxu3i+uguLwLBvrC+BAGgEPfgyRKB5
BXxmupzmXyygijXpHdVlDQzGIFXVVKQMyeSLABSaz58
-> ssh-ed25519 Dfencg D/NzwQbbP1VMl11ezv7RLCZobWPd2jocy/Xx/GG7fQM
gOAksl62iBAesCjnKYCEjaYUCNEnhnpvbwjTthb/YsU
--- uWbkqQse85XYIZmig8Sj9Qthf4bHtSmWorvKnFFonO0
s‰{ àKŒRuÐê™öËpÓ*.A:à8"}J<>¯©  vÜÏé÷ñÑýa1Wx/Ï,'Hc_ËÝ~ª^Å …8

View file

@ -0,0 +1,48 @@
{ config, ... }:
{
age.secrets.keycloakDatabasePassword.file = ./databasePassword.age;
services = {
keycloak = {
enable = true;
database.passwordFile = config.age.secrets.keycloakDatabasePassword.path;
settings = {
hostname = "id.srx.digital";
proxy = "edge";
http-host = "127.0.0.1";
http-port = 8787;
hostname-strict-backchannel = true;
metrics-enabled = true;
};
};
postgresqlBackup.databases = [ config.services.keycloak.database.name ];
nginx.virtualHosts."${config.services.keycloak.settings.hostname}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${config.services.keycloak.settings.http-host}:${toString config.services.keycloak.settings.http-port}";
};
prometheus.scrapeConfigs = [{
job_name = "keycloak";
static_configs = [{
targets = [ "${config.services.keycloak.settings.http-host}:${toString config.services.keycloak.settings.http-port}" ];
}];
}];
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${config.services.keycloak.settings.hostname}:443" ];
tags.host = config.services.keycloak.settings.hostname;
interval = "10m";
}];
http_response = [{
urls = [ "https://${config.services.keycloak.settings.hostname}" ];
tags.host = config.services.keycloak.settings.hostname;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,10 @@
{
age.secrets = {
# nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'
mailbox-Oom7oh.file = ./mailbox-Oom7oh.age;
mailbox-ugai0U.file = ./mailbox-ugai0U.age;
mailbox-Osoo5u.file = ./mailbox-Osoo5u.age;
mailbox-Ies6sh.file = ./mailbox-Ies6sh.age;
mailbox-xaev9B.file = ./mailbox-xaev9B.age;
};
}

View file

@ -0,0 +1,31 @@
{ config, ... }:
let
domains = {
tld = "srx.digital";
mail = "mail.${domains.tld}";
web = "autoconfig.${domains.tld}";
};
in
{
services.go-autoconfig = {
enable = true;
settings = {
service_addr = "127.0.0.1:1327";
domain = "${domains.tld}";
imap = {
server = "${domains.mail}";
port = 993;
};
smtp = {
server = "${domains.mail}";
port = 465;
};
};
};
services.nginx.virtualHosts."${domains.web}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${config.services.go-autoconfig.settings.service_addr}";
};
}

View file

@ -0,0 +1,171 @@
{ lib, config, inputs, ... }:
let
host = "mail.srx.digital";
in
{
imports = with inputs; [
srx-nixos-shadow.nixosModules.mail
simple-nixos-mailserver.nixosModule
./accounts.nix
./autoconfig.nix
./webclient.nix
# ./dmarc.nix
];
mailserver = {
# https://nixos-mailserver.readthedocs.io/en/latest/options.html
enable = true;
fqdn = "${host}";
domains = [
"srx.dev"
"srx.digital"
"sourceindex.de"
"nix-hamburg.de"
];
enableImap = false;
enablePop3 = false;
enablePop3Ssl = true;
enableManageSieve = lib.mkForce true;
localDnsResolver = false;
messageSizeLimit = 52428800;
sieveDirectory = "/var/lib/sieve";
mailDirectory = "/var/lib/vmail";
dkimKeyDirectory = "/var/lib/dkim";
certificateScheme = "acme-nginx";
monitoring.enable = lib.mkForce false;
backup = {
enable = true;
snapshotRoot = "/var/backup/mail";
};
fullTextSearch = {
enable = true;
autoIndex = true;
indexAttachments = true;
enforced = "yes";
autoIndexExclude = [
"\\Trash"
"\\Spam"
"\\Junk"
];
memoryLimit = 20480;
};
policydSPFExtraConfig = ''
skip_addresses = 10.0.0.0/16,127.0.0.0/8,::ffff:,::1
'';
};
services = {
dovecot2 = {
mailPlugins.globally.enable = [ "old_stats" ];
extraConfig = ''
plugin {
old_stats_refresh = 30 secs
old_stats_track_cmds = yes
}
service old-stats {
unix_listener old-stats {
user = ${config.services.prometheus.exporters.dovecot.user}
group = ${config.services.prometheus.exporters.dovecot.group}
mode = 0660
}
fifo_listener old-stats-mail {
mode = 0660
user = ${config.services.dovecot2.user}
group = ${config.services.dovecot2.group}
}
fifo_listener old-stats-user {
mode = 0660
user = ${config.services.dovecot2.user}
group = ${config.services.dovecot2.group}
}
}
metric imap_select_no {
filter = event=imap_command_finished AND cmd_name=SELECT AND tagged_reply_state=NO
}
metric imap_select_no_notfound {
filter = event=imap_command_finished AND cmd_name=SELECT AND tagged_reply="NO*Mailbox doesn't exist:*"
}
metric storage_http_gets {
filter = event=http_request_finished AND category=storage AND method=get
}
metric imap_command {
filter = event=imap_command_finished
group_by = cmd_name tagged_reply_state
}
metric push_notifications {
filter = event=push_notification_finished
}
'';
};
fail2ban = {
enable = true;
jails = {
dovecot = ''
enabled = true
filter = dovecot[mode=aggressive]
maxretry = 5
'';
postfix = ''
enabled = true
filter = postfix
maxretry = 5
'';
postfix-sasl = ''
enabled = true
filter = postfix[mode=auth]
maxretry = 5
'';
};
};
prometheus = {
exporters = {
postfix.enable = true;
dovecot = {
enable = true;
socketPath = "/var/run/dovecot2/old-stats";
};
};
scrapeConfigs = [
{
job_name = "postfix";
scrape_interval = "60s";
metrics_path = "/metrics";
static_configs = [
{ targets = [ "localhost:${toString config.services.prometheus.exporters.postfix.port}" ]; }
];
}
{
job_name = "dovecot";
scrape_interval = "60s";
metrics_path = "/metrics";
static_configs = [
{ targets = [ "localhost:${toString config.services.prometheus.exporters.dovecot.port}" ]; }
];
}
];
};
};
}

View file

@ -0,0 +1,34 @@
{ config, ... }:
{
age.secrets = {
mailBoxDmarc.file = ./mailbox-dmarc.age;
mailBoxDmarcClient = {
file = ./mailbox-dmarc-client.age;
owner = config.services.prometheus.exporters.dmarc.user;
};
};
mailserver.loginAccounts."dmarc@srx.digital".hashedPasswordFile =
config.age.secrets.mailBoxDmarc.path;
services.prometheus = {
exporters.dmarc = {
enable = true;
imap = {
host = "mail.srx.digital";
username = "dmarc@srx.digital";
passwordFile = config.age.secrets.mailBoxDmarcClient.path;
};
};
scrapeConfigs = [{
job_name = "dmarc";
scrape_interval = "60s";
metrics_path = "/metrics";
static_configs = [{
targets = [
"${config.services.prometheus.exporters.dmarc.listenAddress}:${toString config.services.prometheus.exporters.dmarc.port}"
];
}];
}];
};
}

View file

@ -0,0 +1,30 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpEcERxdyBEYzFJ
UnhtcnNmREx5ZkVab05PY25qcUZZYW9oaXNPTmhqWW1PcXJIQVNvClVYQWIwYUgz
TGxWYUNBcXBkMmxBTExRZmhaalJjbjJReUdDZ1FWMThxZEkKLT4gc3NoLWVkMjU1
MTkgSnpqcmlRIEFwU3NtQzRnODVENCtHblhLTW15WnFHSHFPNVI4em9MbG42cFlT
MnlveVkKd0tOWlpIK2lWQkJqa1BVOW44bWQvRy9mRjNUdzUyK3FZZkhNVVg5ZEZY
UQotPiBzc2gtcnNhIDZoUHg3QQp3VUNpNG0zQ0ozZFdFME05Yml2WWxDbTJTSmVS
VFJoa0ZOd2FDcFRxbE1UaTRVcWFWRG5MYktrVk5pNHMzZ3hNCmpOb2dNcnYrUUNO
WUZnYUVhbzcxa1FvVnU4aU5DYTVWaDF4Z00yY1JRTWlabDlvankrdFpnVlVNZGwz
a2R6SFgKcHZsMXphdGQyMjBWOXlSekJBNWpqWExSSy8wbW1wMUJBaTBJZExUOWhr
aUpHVTlBdVIxaTE1TXBzVW9KdjFWegpBTmQ2WklTOEVPNCtWNmVuNVhPV2UzOCs0
c0RGbDBrYUFFVXZrUVNxQnFsVDV0Nnpib1pPTE1KeFZhMm1TQStxCjRsc21qZURp
MS9hbCtxODFQVE9IaUsvVjFzbEdmYjNYMUtuQXZYemRnWDFRYlhPdldpVFFpMHU5
bXdhMENiYUcKV2wrQXltaTJZZm1vVlUzQmJmWjljZi9uQXZwU1VydU1paWdVQkVK
SWNNU2VnMkhUUi9xbWljajk3eFFPcUhBegpGaE1qb2poQU4rNEtZQnNGR0F4TC9Q
V29ORzNXNm9QNTdQemFQUXVCYXd6QTl4T0J4UlVuNU5Ga2drUE9KZllxClYrOXVF
NGJEUGlLaktMQStGcTQ1ZzhQU21pazhrMTdhOTltWlp0aWp5VllDMjc3UTR5ZUcy
ZncyQS9xZ004ekEKcG5oNGdZbTdjMjdWUFdBYnRsRWtiNCtoV2p5OWNNU2xHekJC
ODRkYmNKdTQxNDQ4NURmd2pXSkFGaW9hdXNBbQoxMHFta28vaTZDVDkwMWRHQmVK
WWVqbHVuOEJSRkVhTk5kbmdQeW5TTWRQQys5RVlOV0F2WjdzNHhUcU9xSklUCjdJ
NGhEVzdEWjZYdTNjbEZSdUJYTGFBRlNzc2VVOFNPMWhxN3MzOC9BM2cKLT4gc3No
LWVkMjU1MTkgUUF0anFRIEFWRVFkZ2ViRmRMTUFZWFJsZVlIcEJPZE1reFhjOUdV
d1BwTDRkdnpMbmcKNU5Bem55Q1AzODRWNVplQ2xmTnZybVIyK2ZtTHRzcHRDdURO
VmdhQmxJQQotPiBPbEJjKXBMLWdyZWFzZSBIOyFOPiAiMkRuIDJwcSB6b0E2c3E4
CjROcnlyQVc2enhzUHlBOUtyNk5OL3FIbkx6VE5JUW0weEpjbjlrT1lxMG91ZmRl
OHNQTThHcnFxVnZOSENXUXIKc2s2cWc0NAotLS0gODhIUjFxMHV6K2tMNGlYU1cy
K214VmpGVytBNzFxVU1xTWlKQkxMV253VQoywXcrQeeo8fLE17veSy7lDzOahKD0
oY0GUlps1e6YVBXiFyZpo9NM/DnnNRgFsC3uMBHRIe3c19kwaVPFQTaE2nG99jrp
iLnw
-----END AGE ENCRYPTED FILE-----

View file

@ -0,0 +1,29 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpEcERxdyAxUVA0
MVYySnhnUzI0YnI1eForZko0L2VKSWNHd1lQQzNPYkE3Y2p1alVBCkRMSTNxd3RF
NFZob2loa2dIUkFQclV0VU1YUU5qNUx5OWczYVJzbmx4OE0KLT4gc3NoLWVkMjU1
MTkgSnpqcmlRIEZid2hWMi9yengyZUZtN0U5VnNVbE51UExpN2pQSlMyMHc2WElV
Qm9JU00KYXJaMy8vY0pKR0kraXY0QVhZdFZCWjJjR0p5b04raGNuLzVXVExrV2ZB
cwotPiBzc2gtcnNhIDZoUHg3QQoxUmZqbjRBd2IxQkRJZUZTcXZqNDZScTh6NGlW
ZE42R3A0NEc0UjdhWXd2UEpFekY2aXZ6K29jWDNpNlp5dExXCjJ0ejZlelRjRGcx
WjlvOUVxU1JBN2lUMFRGWkFZUUtHc1JOaXlpTDZSZ3B3NHg1MThESG5aQUVIN29H
Z1lUaVIKVzZYV1A4YjI5bTEzd0E3SWtmcnZkRnQzWTJ2d2pmLzVVZW1wNWlZY3Ur
eWhPMVB1MzJUNEVvY1JEUXAzMlA3RQp3RVJiQnZnZ1lPSFcrSTllM1ZDUnB0RS84
Wm8zTFVzYU92S1JJZHdUN3ArSGNzc0lldUJydVdmMTNqNGd1Y2lXCmFpWEk5d1VS
enA2d3J6TUg5M2FDQU1nRWhVd21kNWRRZVpuTGo4YUk3Q0xXbVhYK2dnT240dnov
K0RUcG1JRXcKbnVBdzFrQWVlQWpyMFhQNmRvekhvRXpzWE9PellNaTEwdDNSVFZF
cWJjNVdWUTIxSWdKckpGdEpBbjZFNm5IZApOcCtZZk9UbUg5ZUNiQy9SN0JHeXQ4
WHlVemlsbERYcDY2VjQyYzJ6UW0wMHZMM2dpaFAvbHBjanN4SXh4NGNxCmhpRWU3
d3YzaktZL2E4UGE0bnVkbUNpYUYzYnZMR1dVZzJwcGFkUm9MRnNoS055QjZYNEFs
aC9aWUtRVCtpN2UKSEZ1VkpCL0dyMXJQNTlpdW9qaEc4SjlnOGY3MnNIcFEvdGJL
RlNvU0xndk8vSVVDeVNyUlY0amw3Ukpzc1JBNQpOeWY2Z0grTWtiTE5VNG5EdWlS
Vk03OFRiSnlsSHB3Qkg0Q1NMMzR3R1JXSElvakVqblNlR3JtWjJubnQxd0haCndw
bDJpZVJUTW9aWURzTW1TeVFnaXZsOHN1cXpGLzNXTmJ1VjFWNXFGTWsKLT4gc3No
LWVkMjU1MTkgUUF0anFRIFhjdmZTb3NUZVY0MWZMbGU1U0hweEFHMWFxKzRwdWgx
bG1sc3JoLzZZbVEKVWhnR1RlL1lVWGNOZnYwRXdSN28yMVY2cG0vcFd0UlRsVW81
UFJrL29YOAotPiBxLWdyZWFzZSBVQGVyICdDNVloNjRsIHNqXzI7JQpaaGowT2NP
YzNoZUFPZTF2clQ3VnExVnRUTkpmVXhKTm5nSGdmRUVCCi0tLSBYY0RsalNZRlMx
RFQ3RzdjOVpGRTJ6MDByU1ZtMDNJWXI4Q1JLSGQxMVhjCma15P6/4SfGzxHhqxMm
mseZxfDqWy6KgTPOIcxGbU3itwueFJgFwYVWHynlx7TAfSJZFVNMYQycE/KeoVE9
TgP8/rHeWD66IaQ=
-----END AGE ENCRYPTED FILE-----

View file

@ -0,0 +1,22 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw IqUKNln5TWRzlKY6CHjia6VuDYlMpjL+D3NKHI+fHjQ
EODXEkWjzP68TC9/JOQ3piORAEECNnYlH71mLUmbHZc
-> ssh-ed25519 JzjriQ 2InSkL416EOGoG70EBVjFSwNWwS6Vx3uatbERPipmng
6f5XnAluFpTpqxPWu/O6DQzvkcfI/ipXJgk3gc8oB38
-> ssh-rsa 6hPx7A
I5U3iYOzM33IXleaRta1ONJKwbh50DLr+65ad8G8XiZ3AMn5e5zhlxKVzXJ9G84V
iNHd1mOn+fdOGXJNs5ze60TyJaVlG/E1uNE7yj+yu9D99KZB7LIh6a+OcO6PYdYQ
339QHQ90hs1nlt9oKc7Z6tHZh4o4ROOPSP0FleB4c1N5l8OXD8NzLbUtiTKfMdTd
4vRQUcgzhe8giqPRl0Qy8arCijS43dCUBNeQPK1MqJXsl76QLaJJDRQ7ouOV918K
+JNLMhEwM9O6berEp++y0VcED3vBNkfbijJhKH0ogE4/kKJhxS4YQQKThLzxjUsd
2OSFlan+9lahEGo0vqFAn5rDpsTEL16RLKYGg1Z234ZZ3ukLqyi4eimfT8Os/PE9
QgCDJ+PkR+z8ZMh38SICNiHoFZXW9ziVXlxtojtHzenHkdi/3NKpsjwFlmFs1eRs
fM5T2Vgyucf5WgBeoPa3wr5MJvnc3KtcTZdB4z/kn26QLQCCMJNC6+6gdg4CZHJj
bSgtzmNwjdsiUg3MNFcmY3u5R4eEnMZFK11XP9/IxCJOYtVymKca6iihDZi9OGa1
WZuSfuHDcxAbcWnaO5b6pSjr3QbrFPOdIdDFzODjjet2843XN7/ToNpsJX3rd50m
ngWwO+amNj182WBy+PWK4FLBYom0hONyq0PJjVqK8ko
-> ssh-ed25519 Dfencg 7Gqsm3fValaJV9P60O+tC7dRHnubFWt3S+M806QGqRQ
VRRS9cdXvEYk1T7eIB9/KDfjO/7YrnPsmiHiYEhV7RU
--- uooLSbExMNA6pRi4MxSHNOS8ziNYp/19fkd1jdIsZjA
<EFBFBD>V¡Ÿ
݉ÿx.×ÃΕ˜ÉŽ$î,«ËÜ ögT:`_GÞ<>¦h?,¦ØÇÀÀ ÎQÙÛ"D0 Î1Žé¶o·ÎrÛÞiÛMƆ° bgÛ»ïRoÍΑöÁ

View file

@ -0,0 +1,22 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw AsYcnmBOaBvP0KN6Fn5eHCY+LH+OtOAS/h9n/QzVFCM
Ev5qsh+Eqd8nSax1BU0vOpk/gw1XUDz306hzoFDpick
-> ssh-ed25519 JzjriQ BeuJoHDNgji4cWsv7iSJFiRVnI0jyPiO96O6Nt2ijEo
eilK7JsfMKkEO1RrjeSPWeGjn1a5GN3OxTN/Vypx+Ng
-> ssh-rsa 6hPx7A
SKD7bmzmJeqjJthm9Q++I+UHfcYWiBNSksF7N0agLso6w5t2EZ41TxY6WyJyeLA7
3oAWHoHlwrFa2GCnazP3Mf4d7l04zJwLNHeok2D7P4q6r0a0saN1z01GHP4UbBnX
fiMKHFWn1bzT3UjtEcbo2RKVbWNsiwFXff5wZBzM2V/buTG9ipV9lf1qsdaCeA2W
MAEtIBqh5LdmacHR2bJkqzFwo4cCOHdYDs5HWxS8iJKXQ0K+He+pFv2fI1wuaLnM
cWC79MvSwAHy0KEbDfbBlUZPVPC7Bl4KInVxAKsqAOILjAAxH9fIVpIV7yGCxBew
lZZjbNYY9lL+b8YnFHKpUd/1Uh8qIJPBmkjVlAIaOT+mlwQMixJ0+nowABtyyKgm
7AoRBUlGlXpJg5xlkHLNJWx9c7Jp3q03VOHDAhR7qP93bXZbKZWVOxcOOijnF+/g
uwGTCGAAxnyTQl/mL5KYAXQCCSa+wDLalVemaToFkenwJryLTghAjvlvwvSYdgMx
t6EbgMEe+VW9XnFhrOlnuUsdC9Lpv59/eylNPu28lfSmg2bEi22b29C+bNweOwWm
9coXVF9GOk+40iC9bBKsLPPQbs+o4x9n/kYF6zCPIOtlNmFHWog63gWc1NR9Y+JH
32JK9OBkJynabaUs8LmgBosDbmfPmSZbnN9QSOKfho0
-> ssh-ed25519 Dfencg jX/fnsqLBNa+wUpuuG9m0jCGlTg6BeylyMEGFQkFEiM
t9/eZWkxCGggy10XfvTperveQbPTthPt6mrwlO4dHn8
--- 64kfsS5RRpZyihA0J+QFREM9cQNjULs/HA3B0BKx8zM
V³*L½3âÓV7¢|ìö6¸DX&ó€e ñ~3¢|sÊO寧gðÅêϧ2cš<63>
·¢Wàp¼ZMN\Ù·æ!%Ç»&§4l˜ «$õ5d;²Nª†A8ðq

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw 0ek8rx0G8dE/y32fvD2NWPNf3BeRpUDyKN6YSag9qnk
zU2szwIaM0luqkXTqjr4qb/WEi8qwIfRfWAv2RZ3Hi8
-> ssh-ed25519 JzjriQ NAMwKbrCpOm9wIl0zcd1JP8byAU64O2/yTmsf0yG3A0
HebNORNtGrhBE3Ujcx56li8FgJUiyw9OimyCDw9kriM
-> ssh-rsa 6hPx7A
R1OIFZEF7GSLllqP+QMVm40hCAx8uChBTN4/lKXl8QdOYQSLZH6L817yWt+UF3Vs
0Q/skWWODUDj6apdh63iypkqSbhxWVHy7XlWki3oJNBp8fmioPtmZ7UHKWxzEtil
7ZtgJ/vkbJywFa8Is0rTs+Q8thTpthn2Ezdz/SbFy9X8f3fQRn7xgYTh08p3dJrF
tecmByC9Q6Jci0Xgw4+ZtjJbjgaYAQaMuyLVT+nx2v7fn/jnIckk6JifulvSxihC
mYf5qy9zG2R0J2ikdE4V/j8dFQS5+o3OU0qPqvXhNSKNWz71qLa3zJytHD6cZEcS
CUPJLQzI3oP8hbOk+NJ0m6ZGa3p219qSiNKq/P/EoiUKzJs4EJHlHWa3VOvjg+KM
uZeFYuHSAZcAjbtK3dCa9pESrsVzb/mDIwlT1K5PcHcyT2SQbTqXNFapqFJ9DzVR
hQTbuy/uL7nTTsMuMq71K+d1T+HGquVaLjqqXCiippppYhaenLvotrT+Zgl0mpin
ds/n9G5BBuXSM6pzOs97xTxUup2j0Xq7ykr3x8Cd6kyGc25b3AVbSHVLbfQ/4U7B
X/RmfIISYwPZvEiygc3mzyzLQEeLxi2q35yfmSJOk6DdUbGsGoF4ufXT0GjKj+gN
iYiHteEqoZ+MGkyviowtCuVSD5soF2pLrdEfm3txnfw
-> ssh-ed25519 Dfencg QVprlje+Zb4bsWPvtqsmd66mMvgAavFzDZYI0MoWA2k
1OqfCxvJ8oESIN0R3yD8f3HX0b7TqmjMqEFn+Hx8q04
--- 9ilMGkwGdMFexB4xqRnlw+Z70McoFNsAJ9bD+4FddDU
`}MãÕ)vöøUoÚ<6F>”…Îd<C38E>SøÀn¨L´2VÜ[œ_6ÒF¢p˜fD«Ïp‡›ÓƒsØÈ0-ˇŒÓ)Nå<4E>a°ûêFfLÞ¦L?!A¬ <0C>ó£ð9

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw VRdFs1jw4vLlb9+U8wQI1O4dR/d5eO01YJXsX51LsxU
h+7Ke2y2TA5MdGp4UpouKUkz3+tSY4M0fB/leuShITo
-> ssh-ed25519 JzjriQ 5ayIpvnxJVz2xjczZfawxKa6v6dY+QjB7m2YFFX/FCg
IwRDDARLTk+ecqvcaAD+s4XGDEIMb9YHV3VsVWCoxVQ
-> ssh-rsa 6hPx7A
wEQcgNdS1VWnw0uQrxBifBRuS3rYEqlPRlLYSnvWOuSv91ySpiKnG53WnRCbMWZ4
Zjrgd5Rqzd5M3ZLebguO5OOW63kuc6ZPtJPigYBjjUIVtMlZDYLeVsx4t1RAzULO
EWTDuuK3npQ3+zkAJ172iUsFzH8CQ3HXNy3BI+SS80SddI5cx8BuPpXhL/1uv8sf
vEQ/dz7oMLX8o4Cy8vLXKM1H6mm01qcqlF2E4eXIvk9OeqnkAbr6y8NU1pY6Q8F4
CCVdVk7CSndnGk5R0x8bEaGnLMtMrNGKwjJoO9pMFKH9Dicnw1rNNxHfz4aGHUNV
XejAzYDtKxRge/wxfpd0sL2ybOhGse1IfjlWcSYeOycJVwGoRVYyFmUGgYUsdV3o
YzVWIyP7rcG7e7rwBd5ch3JqIgMFk3tZzBTaDHqk/NlxlSQd99Foz9FAV2/rT8eG
8fqH7+i3bpukHoV3VjF7boknj8pUqynGsivy7Awb6xF/dZTeOOnjztgScpF02RWW
S23HlI9QSkeir7cSHmtSBNmjGChJ0ruWDxt+qMnttak+eyzSpn/PPJMJZy/VquJL
BlLyE/IUkvuOJ+WBf7ZcyM1MIRTS7zJFrGOX3RV+7bgrHSHc+cZvmPJWK5GmXuJw
ZQtzmwQkbdZaAEADwtbHZScPSqVizxcx0M4yZHW4XCg
-> ssh-ed25519 Dfencg q01T3MDcsYOgbf0M70ohPJVmMxm7uU5zkEcjqpS2MTg
0kqqufEzwCLEBPk7w/nxZxWX5AcAJKQnOhdciUQmJeg
--- d0At3NFxZg2VONRpp4H7HucYjo8u/jmMMoGlono5Y8k
ˆZ|kc _Åtê;Á¸rOØ<æR:høG5¤Åø<C385>¡ í{׊¸Bû”<C3BB>Èü<C388>‡3$öÀKNSÑ

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw LL0r/vdSlTSYHqUh7eBfPr+WGPZU0iaC2+wjLrV77Ek
msGNfMkfyMsUJu3aSG7C7stpgZ0gwqI8IVZlFT1MKbE
-> ssh-ed25519 JzjriQ MYqtyTLYdp4JyA1a/ci6C44/9ye90RebTpfpM099xj0
mCDfmBUgPmo4EjENSKvRkRokuQHqFXoivVBR2Tku5/g
-> ssh-rsa 6hPx7A
NUNRebYgygx3knvHH6jySx9ML0XSbCUy3FxNHWhXB1HSv1MxtPG57XTSsgISvmA0
+Q/4p/1fB99FDvuZZyY1xDoCguJsUGMDzwfeb9J9bYwyuJR0h7ZB3UJWz417PcBV
KEGHYC6H7sdse/wC60deta4ZoftiwPHSxim7nta6uuUuVWkXlZxy/21ypknDo1NY
v/Ek6LhvzzhvYDwotjuQPVXOo4DulFE7AoNo5Xqd4VuzkifBZ7U6zxHRz17sNcOY
sLJW/6uEASbH9jarEv0PN9qcou5WtWEOiSoNdRs5SPWuq+SrFNRMmQOlz9m/9RNd
2zaNpeectLO7mMBq5VC+SIPUGNy+wq2oDg/NKt9WNHMfActciv/q0gJDDUHLYaMz
XponS47ngYP6SRS47g0IiufcT0KJsD8b8aMqyBL2zBslLoSXPThzYrzmfKMe2NKl
GVNDYn7LQkspZkL9Y4PcHcc6fiVHjqZxL1Rsx+H+yaXYz5PlTsL+g1jFeySsHPrT
NtQ7U0dbgSgG6yXMyYEl2QqHE1RFWjhDz7PWORJD0xCDTGBT5tp2crNeZ8sHYWqA
ipz52GgWszBTTYmMBYbke3MCj3/lJP1imcrNhIBILSLVHc0mkZl2srmLbCsvNq6U
Yxrdr4Q1l0julNiil7IFdZHo41LJpfCYSaCRvLXSyps
-> ssh-ed25519 Dfencg FnGT29dYaYptFPwz8wyMmTFIzgDDRQUYrzVPO/Y2dQ8
k38BfgRhf5J5ZT1JsRcI/VS5iPCnQtXucZDEnVjYoA4
--- 4fsRMPbGZKgCJyas/nepabKgC+8HGEZgjvBbVvGAfrA
àAœGgY^cCzç^‡ØP<C398>§ã·J_Ù…g±3•v8`g.˜…ªÕz<C395>^.Bö˜%œ;Å ¢ U²òߘˆï„Ђ·<50>>"‡û3Õ!m=‡<>c+<2B>¼ú¦ÿs

View file

@ -0,0 +1,31 @@
{ pkgs, ... }:
let
host = "mail.srx.digital";
in
{
services.roundcube = {
enable = true;
hostName = "${host}";
dicts = with pkgs.aspellDicts; [ en fr es de ];
extraConfig = ''
$config['imap_host'] = "ssl://${host}";
$config['smtp_host'] = "ssl://${host}";
$config['smtp_user'] = "%u";
$config['smtp_pass'] = "%p";
'';
};
services.telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
}

View file

@ -0,0 +1,103 @@
{ lib, pkgs, config, ... }:
let
host = "s3.srx.digital";
admin = "admin.s3.srx.digital";
in
{
age.secrets = {
minioRootCredentials.file = ./user_admin.age;
minioPrometheusCredentials.file = ./user_prometheus.age;
};
environment.shellAliases.minio-client = "${pkgs.minio-client}/bin/mc";
services = {
minio = {
enable = true;
region = "eu-central-1";
listenAddress = ":9900";
consoleAddress = ":9901";
rootCredentialsFile = config.age.secrets.minioRootCredentials.path;
};
nginx.virtualHosts = {
"${host}" = {
forceSSL = true;
enableACME = true;
extraConfig = ''
ignore_invalid_headers off;
client_max_body_size 0;
proxy_buffering off;
'';
locations."/" = {
proxyPass = "http://localhost${config.services.minio.listenAddress}";
extraConfig = ''
proxy_set_header Connection "";
chunked_transfer_encoding off;
'';
};
};
"${admin}" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost${config.services.minio.consoleAddress}";
proxyWebsockets = true;
};
};
};
prometheus.exporters.minio = {
enable = true;
minioBucketStats = true;
minioAddress = "https://${host}/";
};
};
systemd.services = {
minio.serviceConfig.Environment = [
"MINIO_SERVER_URL=https://${host}/"
"MINIO_BROWSER_REDIRECT=false"
];
prometheus-minio-exporter.serviceConfig = {
EnvironmentFile = config.age.secrets.minioPrometheusCredentials.path;
ExecStart = lib.mkForce ''
${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
-web.listen-address ${config.services.prometheus.exporters.minio.listenAddress}:${toString config.services.prometheus.exporters.minio.port} \
-minio.server ${config.services.prometheus.exporters.minio.minioAddress} \
-minio.access-key $MINIO_PROMETHEUS_USER \
-minio.access-secret $MINIO_PROMETHEUS_PASSWORD \
${lib.optionalString config.services.prometheus.exporters.minio.minioBucketStats "-minio.bucket-stats"}
'';
};
};
services.telegraf.extraConfig.inputs = {
x509_cert = [
{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}
{
sources = [ "https://${admin}:443" ];
tags.host = admin;
interval = "10m";
}
];
http_response = [
{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}
{
urls = [ "https://${admin}" ];
tags.host = admin;
interval = "10m";
}
];
};
}

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,14 @@
{ pkgs, ... }:
{
services = {
mysql = {
enable = true;
package = pkgs.mariadb;
};
mysqlBackup = {
enable = true;
calendar = "05:00:00";
};
};
}

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw gXLNgjhGBM10SZ1U2F269Hi0snqwdMBR9cxDA5vABDs
BVTnuH5HbgS2SQoZEFl7GsXvcYO7Eq1lH2zGhflfUR0
-> ssh-ed25519 JzjriQ 3c/vRp79jgta5Ow2TfO3VOKABVTSSpshM43eU7o/uRo
LQt1o3Fm5T2ot6Dly9pNbl1YOt+0SQxqnSxMiapzWJs
-> ssh-rsa 6hPx7A
VzCl3ya8O0fD+nzqrTfZvH/W+JSQ1k6K30hKprc0FqhIMWWpmG7k9MDt28zlKqbI
V2Im+QWT3UNohQ7uKo+RsBTkGTRtL/kyGCUqqjfQEyDMgXSdjb05kaE0/jjFR+Tp
MrhuXn390S7Bb/vWW3pv+BqcyHMwHIp9raQeOULu0qBhCVkDaqZbKHqMXOyYyrrn
ooAbofosADSIQkKDkFoBdhIraPejKp7US1ytEG5SaG5uSZXUlgE4iwm8BFos6xKt
CJ8OX/RaNwylA2mkf704wZmY8DPt+0AGuzhyXvfQwB+InQLvNHLEnUl7ErCbxENH
lNWG5+EI7oYQycYg7UN1ofnBSbuvchERPVmDPRx7GSwqrIXj0uWtyys/NERYl1gV
GRdnzC90U+HBhN5l46hnHpyYuO6h434cAPeVa7pE6BQ2ZXvE07vPdyG0uMobq05x
7jLQoX6IAMzBLfzu8g7JpBRyRULhXm9ejPgnH3qCEvXWrRnwc16+1moMADXLyq62
WtKxFlYALyZ3DnAUW2alZYMdSFTvGQ30GhX/7lSj2ndQ/3XppsX8BtGGVMkSX3g6
Zc6Re4xqiXKDcqmtn1MJUNSm2kFC+Y/FXYmf92rkmnE0DfoEROwKs45PFX+Thz7P
NjRNPb0rjVJu+YipuOl4Bz1AC96U2BUTBcmwFP8ux8Y
-> ssh-ed25519 Dfencg H6B2Vjd4e6PJwMfsPBaYCYNCDehCryWXD/yvQgKPixk
1GbC05c7ruTO+Ca9KO6fpfMugigGKNhWR0Xw6qB5D0Y
--- ylP9OpryQ/nl1nCS46mGrOsS+8wR4YclZRQRK7hl3To
*Ðe¨^+u¹”K_‰ùȬŠz?ùE© œËI€Å€½Ál]Þ¾²¶ëáºFAˆÅ¹<C385>¥ðYÅ„Ì­uˆ ê×­ÒÏÈrõ3c<7F>|¿

View file

@ -0,0 +1,153 @@
{ config, pkgs, ... }:
{
age.secrets = {
nextcloudAdminPass = {
file = ./adminpass.age;
owner = "nextcloud";
};
nextcloudSecrets = {
file = ./secrets.age;
owner = "nextcloud";
};
};
environment.systemPackages = with pkgs; [
ffmpeg
ocrmypdf
tesseract
];
services = {
nextcloud = {
enable = true;
hostName = "cloud.srx.digital";
package = pkgs.nextcloud29;
extraAppsEnable = true;
configureRedis = true;
webfinger = true;
https = true;
maxUploadSize = "1024M";
extraApps = {
inherit (pkgs.nextcloud29Packages.apps) mail;
inherit (pkgs.nextcloud29Packages.apps) contacts;
inherit (pkgs.nextcloud29Packages.apps) calendar;
inherit (pkgs.nextcloud29Packages.apps) deck;
inherit (pkgs.nextcloud29Packages.apps) forms;
inherit (pkgs.nextcloud29Packages.apps) polls;
oidc_login = pkgs.fetchNextcloudApp {
url = "https://github.com/pulsejet/nextcloud-oidc-login/releases/download/v3.1.1/oidc_login.tar.gz";
sha256 = "sha256-EVHDDFtz92lZviuTqr+St7agfBWok83HpfuL6DFCoTE=";
license = "gpl3";
};
};
phpOptions = {
post_max_size = "1024M";
upload_max_filesize = "1024M";
expose_php = "Off";
short_open_tag = "Off";
catch_workers_output = "yes";
display_errors = "stderr";
error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
"opcache.enable_cli" = "1";
"opcache.fast_shutdown" = "1";
"opcache.interned_strings_buffer" = "16";
"opcache.max_accelerated_files" = "10000";
"opcache.memory_consumption" = "1024";
"opcache.revalidate_freq" = "1";
"openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt";
};
poolSettings = {
"pm" = "dynamic";
"pm.max_children" = "512";
"pm.max_requests" = "4096";
"pm.max_spare_servers" = "512";
"pm.min_spare_servers" = "32";
"pm.start_servers" = "64";
};
config = {
dbtype = "pgsql";
dbhost = "/run/postgresql";
adminuser = "service";
adminpassFile = config.age.secrets.nextcloudAdminPass.path;
};
secretFile = config.age.secrets.nextcloudSecrets.path;
settings = {
overwriteProtocol = "https";
default_phone_region = "+49";
oidc_login_client_id = "cloud";
oidc_login_provider_url = "https://id.srx.digital/realms/srx";
oidc_login_logout_url = "https://${config.services.nextcloud.hostName}/apps/oidc.login/oidc";
oidc_login_scope = "openid email profile roles";
oidc_login_attributes = {
id = "preferred_username";
mail = "email";
};
oidc_create_groups = true;
oidc_login_auto_redirect = true;
oidc_login_button_text = "Log in with OpenID";
oidc_login_disable_registration = false;
oidc_login_end_session_redirect = true;
oidc_login_hide_password_form = true;
oidc_login_redir_fallback = true;
oidc_login_update_avatar = false;
allow_user_to_change_display_name = false;
lost_password_link = "disabled";
overwritehost = config.services.nextcloud.hostName;
"htaccess.IgnoreFrontController" = true;
};
};
postgresql = {
ensureDatabases = [ config.services.nextcloud.config.dbname ];
ensureUsers = [{
name = config.services.nextcloud.config.dbuser;
ensureDBOwnership = true;
}];
};
postgresqlBackup.databases = [ "nextcloud" ];
nginx.virtualHosts = {
"${config.services.nextcloud.hostName}" = {
forceSSL = true;
enableACME = true;
};
"srx.digital".locations = {
"= /.well-known/carddav".return = "301 https://${config.services.nextcloud.hostName}/remote.php/dav";
"= /.well-known/caldav".return = "301 https://${config.services.nextcloud.hostName}/remote.php/dav";
"= /.well-known/webdav".return = "301 https://${config.services.nextcloud.hostName}/remote.php/dav";
};
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${config.services.nextcloud.hostName}:443" ];
tags.host = config.services.nextcloud.hostName;
interval = "10m";
}];
http_response = [{
urls = [ "https://${config.services.nextcloud.hostName}" ];
tags.host = config.services.nextcloud.hostName;
interval = "10m";
}];
};
};
systemd.services.nextcloud-setup = {
wantedBy = [ "multi-user.target" ];
requires = [ "postgresql.service" ];
after = [ "network.target" "postgresql.service" ];
};
}

View file

@ -0,0 +1,22 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw gt4LnY8JEyRm05USTTnnmoGb+yhY2ivHF7AYyDRS2zs
rLxQQOl158KA7sFDoSA1Dq9eo4hvKqyFQKuMiJYIKPw
-> ssh-ed25519 JzjriQ jLuV616OJ/W3mUU47Srd5zMrCK8R7mYM6s5ol/0Pl2s
MjUPz0mq7v+Ye4ZRR7yTXL8To+o7KZFrXjiHf3WQA7E
-> ssh-rsa 6hPx7A
3iihKSQ2x0fzSzXM+PRWTSrW4xqE0Gx73uZHWRnX/FlmipfC6tl5Dha9jPOKY8q1
spUKmpvjs/N/MELP1z5NJMhnLXKO5S4FA81R2VJ/iR9XqTK+wx0reuytBsE291du
5PyKcMzFT+Nr6frPaHji9UC8J1GDb5zUXApRE2aAZjSeA0wA5bYXM0/bI8nt72hj
rK0Jn31I3oHHxU6jMFRMRv2yDHinSM88fF9c5qIDr7CHGyVZVi6aKUYi5WRXl0gV
JFQJ64Te9jPdpYk0SmvBtcTDQfBzYjEQDut4j2ssPkM+QqT96yCaRBdQ38J0oxEc
z/aeBJ3CQ2Sgq7D7wp+1Tj0gL2dgRtf6BriWSc+gJlwHk74xiruCj1V7PS0cGYqj
0f4p1zpdwnst0z/guzbpZfwXaMvHCYe44cpdY0WvuV6oRIifHC0ValbNUlnAdoyw
A6+/wuyBzcTDda5muhh6C4d6OzibcX0s5yuZ9NtqiLzPFgeCUNKcdAGlcQvq39Mh
89tKLWrSFVfbL3E/xQqiLX9x3W2iXPp4wETGnF8tcTeJpqYRL63g+/VpqYg0ZTki
jadSj01JWsr3uis/yAhV07rAIf+r6laPzpt50UuFmyadL5ODEVFyeh3SkndC/Cq0
f4kq3FBrTjB+i+qw/a3Db2at6xT0ZATMzcvK2CK+Nig
-> ssh-ed25519 Dfencg 21+CtN2dIcGrA2/9M9gwiHfvundfX2IDWm3joukktQM
245qkkTOClX/swsR5gI4pYmmtCkGwKQC55E7RilEcGw
--- LsD1y9ZZvjrUAkeIg6lHAwh0CwbtBdT6f5HWNXLOX5k
|³þ•2²ÄÁÔ>Èz£•„Uuƒô®¥L¹U'©OHAÑ<41> ðJ‡•¶ËÔ¡uößg
˜1—ä+ãž ¯6žîš<C3AE>k:ÚwaQa¤œáú­­Öš$¡6Dê?à©Ãí-Hª

View file

@ -0,0 +1,33 @@
{ pkgs, ... }:
let
host = "checkip.srx.digital";
in
{
services = {
nginx = {
additionalModules = [ pkgs.nginxModules.echo ];
virtualHosts."${host}" = {
enableACME = true;
forceSSL = true;
locations."/".extraConfig = ''
default_type text/plain;
echo $remote_addr;
'';
};
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,10 @@
{ self, ... }:
{
imports = [
self.nixosModules.services-web-nginx
./checkip.nix
./nix-hamburg.nix
./srx.digital.nix
./srx81.de.nix
];
}

View file

@ -0,0 +1,27 @@
{ pkgs, ... }:
let
host = "nix-hamburg.de";
in
{
services = {
nginx.virtualHosts."${host}" = {
enableACME = true;
forceSSL = true;
locations."/".root = pkgs.nix-hamburg;
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,26 @@
{ pkgs, ... }:
let
host = "srx.digital";
in
{
services = {
nginx.virtualHosts."${host}" = {
enableACME = true;
forceSSL = true;
locations."/".root = pkgs.srx-digital;
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,26 @@
let
host = "srx81.de";
in
{
services = {
nginx.virtualHosts."${host}" = {
enableACME = true;
forceSSL = true;
locations."/" = { };
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,80 @@
{ config, ... }:
let
domain = "srx.digital";
host = "auth.${domain}";
oidc = "https://id.${domain}/realms/srx";
in
{
age.secrets.oauth2-proxy.file = ./secrets.age;
services = {
oauth2-proxy = {
enable = true;
keyFile = config.age.secrets.oauth2-proxy.path;
nginx.domain = host;
cookie.domain = ".${domain}";
email.domains = [ domain ];
reverseProxy = true;
passBasicAuth = true;
setXauthrequest = true;
provider = "keycloak-oidc";
clientID = "nginx";
loginURL = oidc;
scope = "openid profile email";
extraConfig = {
metrics-address = "127.0.0.1:5673";
oidc-issuer-url = oidc;
code-challenge-method = "S256";
session-store-type = "redis";
redis-connection-url = "redis://${config.services.redis.servers.oauth2-proxy.bind}:${toString config.services.redis.servers.oauth2-proxy.port}/0";
};
};
redis.servers.oauth2-proxy = {
enable = true;
port = 6386;
};
nginx.virtualHosts."${host}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = config.services.oauth2-proxy.httpAddress;
};
prometheus.scrapeConfigs = [{
job_name = "oauth2-proxy";
static_configs = [{ targets = [ config.services.oauth2-proxy.extraConfig.metrics-address ]; }];
}];
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
systemd.services.oauth2-proxy = {
requires = [
"nginx.service"
"keycloak.service"
"redis-oauth2-proxy.service"
];
after = [
"nginx.service"
"keycloak.service"
"redis-oauth2-proxy.service"
];
};
}

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw W2dbMcjU4bR5zQ0k2Z/6hR7hwzZ9PkAEv61suzJs/E4
NiLEBUVQVEbTN6heg+JDCvt7b5hFDc3zOItKwJLttbU
-> ssh-ed25519 JzjriQ gBWt7o4AKcR9ByJCnLe6S6mh8v1Fq2mqk/BPK4Y2z24
Ginz/IkisV4BRmjcj4ZmwCUawibIo4KYO5NeASxS5ns
-> ssh-rsa 6hPx7A
UWmmiSoCzvY0Jk+Uf76MgiHS5ha/Qze8arOTWctfge+DM6ur8eGxJUq5U7FoPj2s
Grv4hS/NxcXg006S+ID8/c1jWBtU8mCZG8lck5iNMpfnQZ+cRGPdv/Wef19ORtTp
LDCPjzWNJg6G985HPQu6G6+RbYJGJnN1Kf6c9mrmXNIJmiJoRIBjxbLicfHzjYKl
dyIQ20RpOwrhrl6QWhZl1A+KCqh2oMExm4qaocBD2+IKp8GZRWPFX4YBkqWzphnx
R8u+1bxcN7ZCkB09FaLoVCgQabh01hGpDSUcvQbHfvn1YJtXVegqUUrf6atU7NbT
z51z2EEIZ1lHYE1qGDTBHAl81y5gSSmW2ccHKDQ9CsuIByXgjOGLo0/FLB+fr3oA
bHaCvKQvRfz3wXS19X78xLz1MZHBFDWpVmRqEkNijQJGu1Ihii8ATNeu/IVS3erF
WIxYjs65xYr0+zY6+90n07E1+Gt+qW4ZBstslz/eP0f9iVa2uOCsvnQU8MKrurBM
43WqRlIz7HjeHXUlBBtzelGfjL1WRTM786haIeNlzkl2XDZ2Av1qUdhPBGA7rSl7
jNVF4Rj9YurRtB78IF3XFGDmyNt5/TNw0qOF32jKpetO26GiYV/Ia+QKwLklnUuz
hT+7JzO5uQx9u4EqElQvBwSnk3NQ3+EBuBt8lZP56Ns
-> ssh-ed25519 Dfencg G/63XTx+5UrL9Kd6frbq/q7EG5b8RJ4y57+mfVSEMlY
MNTaYp9jlvoBMI2PxdiccpqEYBY8mARp3tqPZuSo4bs
--- fzhYndI2/14JjCXprVjEvTDo2pSvalSTpxAVXqFA8g0
áÅùj•µ~ëuŠÀïåÍÉg»y¥0ÖÊëñ¦O<d_Þ4r9ÝjÏ[íš„½- <1.Û û-[“}ÆLødú”aPÃFÓhÄ "tÐÒ½F·%)å«xdŠñ0gDùš!‰ÓQ·|Ð!"5r˜\[ØO¿tP,µ1Éè¯"²T@kí²{

View file

@ -0,0 +1,133 @@
{ pkgs, config, ... }:
let
domain = "srx.digital";
host = "ldap.${domain}";
adminUser = "service";
testUser = "hans";
# domainSet = lib.splitString "." domain;
# domainTLD = lib.lists.last domainSet;
# domainSLD = lib.lists.last (lib.lists.reverseList domainSet);
# distinguishedName = "dc=${domainSLD},dc=${domainTLD}";
distinguishedName = "dc=srx,dc=digital";
bindDnAdmin = "cn=${adminUser},${distinguishedName}";
in
{
age.secrets = {
ldapConfigSecret = {
file = ./ldap-config-secret.age;
owner = "openldap";
};
ldapBindSecret = {
file = ./ldap-bind-secret.age;
owner = "openldap";
};
};
services.openldap = {
enable = true;
urlList = [
"ldaps://"
"ldapi://"
];
configDir = "/var/lib/openldap/slapd.d";
settings = {
attrs = {
olcReferral = "ldap://${host}";
olcLogLevel = [ "stats" ];
olcSecurity = "ssf=128";
olcTLSProtocolMin = "3.3";
olcTLSCipherSuite = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:!RC4:HIGH:!MD5:!EDH:!EXP:!SSLV2:!eNULL";
olcTLSCACertificateFile = "${config.security.acme.certs.${host}.directory}/fullchain.pem";
olcTLSCertificateKeyFile = "${config.security.acme.certs.${host}.directory}/key.pem";
olcTLSCertificateFile = "${config.security.acme.certs.${host}.directory}/chain.pem";
};
children = {
"cn=schema".includes = [
"${pkgs.openldap}/etc/schema/core.ldif"
"${pkgs.openldap}/etc/schema/cosine.ldif"
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
"${pkgs.openldap}/etc/schema/dyngroup.ldif"
"${pkgs.openldap}/etc/schema/namedobject.ldif"
"${pkgs.openldap}/etc/schema/nis.ldif"
];
"olcDatabase={0}config" = {
attrs = {
objectClass = "olcDatabaseConfig";
olcDatabase = "{0}config";
olcAccess = [ "{0}to * by * none break" ];
};
};
"olcDatabase={-1}frontend" = {
attrs = {
objectClass = "olcDatabaseConfig";
olcDatabase = "{-1}frontend";
olcAccess = [
"{0}to * by dn.exact=uidNumber=0+gidNumber=0,cn=peercred,cn=external,cn=auth manage stop by * none stop"
];
};
};
"olcDatabase={2}monitor" = {
attrs = {
objectClass = "olcMonitorConfig";
olcDatabase = "{2}Monitor";
};
};
"olcDatabase={1}mdb" = {
attrs = {
objectClass = [
"olcDatabaseConfig"
"olcMdbConfig"
];
olcDatabase = "{1}mdb";
olcDbDirectory = "${config.services.openldap.configDir}";
olcDbIndex = [
"objectClass eq"
"cn pres,eq"
"uid pres,eq"
"sn pres,eq,subany"
];
olcSuffix = distinguishedName;
olcRootDN = bindDnAdmin;
olcRootPW.path = "${config.age.secrets.ldapBindSecret.path}";
};
};
};
};
declarativeContents = {
${distinguishedName} = ''
dn: ${distinguishedName}
objectClass: top
objectClass: dcObject
objectClass: organization
o: ${domain}
dn: ou=posix,${distinguishedName}
objectClass: top
objectClass: organizationalUnit
dn: ou=accounts,ou=posix,${distinguishedName}
objectClass: top
objectClass: organizationalUnit
dn: uid=${testUser},ou=accounts,ou=posix,${distinguishedName}
objectClass: person
objectClass: posixAccount
userPassword: somePasswordHash
homeDirectory: /home/${testUser}
uidNumber: 1234
gidNumber: 1234
cn: ""
sn: ""
'';
};
};
security.acme.certs."${host}" = {
domain = "${host}";
inherit (config.services.openldap) group;
reloadServices = [ config.services.openldap.group ];
};
security.dhparams.enable = true;
}

Binary file not shown.

View file

@ -0,0 +1,22 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw tQJi3a3tKiJl5ZfNkro+cOEGDAbUh+vbGf+L4MTCu0k
UioTuNzRsHau5sCRfw7L1y2z52EKlQKscNAv6aCEKl8
-> ssh-ed25519 JzjriQ UxmUpbg8QKiYsKjkHk00uDc2LcKRye+uwPGiwMD0wmA
/pBpwVPDdiH8TMNksH2V8FtysncRGvtO3sdoSeU2GG4
-> ssh-rsa 6hPx7A
iJBX2tRnuY0ZMquFrij61pUpuJsgKIy7cgaISaW0vSFtiplmorYVKY2DUmHJ1qbO
4cCb7P72XDs+5utQyTvO80WTKEFMUxAYpVb8VJ3MuP9mRNvlptcI1lCB47IhcKzK
YZFXpbRAMp4u9kTxZiaYakiQsPoaDK6FjQ9XEG1JxjOn1u4XB/WHxG9blW+AIpn9
1h+Fs7DJF1tU20MlwaLLF5XooxLbDKr/MKeiR1k0y3H6eDtEiQH+Ay42/e1yPwAG
BOO08YxEzAl8sLg8rzlyCu7O1uTvP2wlvqBMV324iu1ug6Le7Gh4ilQyGSLsYOxK
m6rggC5kUEhfBT+60ACReFJx/T/8A/KTYLiQ03F/lmimaCPqnNwkydjSCmIOOiYc
u+hFS6/9TyNM97svMhmq0ttwOhf+zIXVB0zHJ2w9Pj4U77KPrd/C6Ctvn2Q6XK1a
y3EAhTErk4R5fXUiQIcN/5V4lkGPAnwrlqRu4fZAhX/os16M8YZkRoKPNp+aI9Ji
RXBIenbhBNv/9N6/ID1aTZ8Epq420mcT/VEXN/9KkiReWt9ju+lf1NQcCzW4ETZT
WGWay1k0/MFkXBXBW/r1D1p595U8jNB1kfyz/AKTbY/MP2G5FTdM+GsBQ+E6lE9+
B5xY/iwbrOj6HIuWqGXtk2/c9mOuBTFwCtx06KScayU
-> ssh-ed25519 Dfencg 1sbJD67nv/ArocTaWkLnM9YclfGlRCa696NkshUhaAk
eFbUDRH4GGJVNHv89qwxMdlpjpl+4o3jxPYT9bm+Du8
--- KVW/9rQkJ+rdNKHcKAlIXpI4yNcdXzmx0NaePNSuyns
5B5ØgÇ Ã“d¥[Wú{áY&zMÏÍàóûP:9ƒ:•.2
ßz ª$mÐ#âz Ã{ õlŠ¦é7ÎçĆì*Œó

View file

@ -0,0 +1,53 @@
{ config, ... }:
let
name = "paperless";
host = "paper.srx.digital";
in
{
age.secrets.paperlessPassword = {
file = ./password.age;
owner = "paperless";
};
services = {
paperless = {
enable = true;
passwordFile = config.age.secrets.paperlessPassword.path;
settings = {
PAPERLESS_OCR_LANGUAGE = "deu+eng";
PAPERLESS_ADMIN_USER = "service";
PAPERLESS_DBHOST = "/run/postgresql";
};
};
postgresql = {
ensureDatabases = [ name ];
ensureUsers = [{
inherit name;
ensureDBOwnership = true;
}];
};
postgresqlBackup.databases = [ name ];
nginx.virtualHosts."${host}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${config.services.paperless.address}:${toString config.services.paperless.port}";
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw ImlVc99pq750TSzRKTcl+MiuH48A0a8i/CzWr3hMTCA
6HmkKtwIhBoLBYUSsr73gSZwZ2nOJPGhtVF/oZxjzAM
-> ssh-ed25519 JzjriQ d8yMMWhdt6idbv8bxxHRvSRDXsfuOUm90YTfrXoD1EE
yQtX8LadpLxetD1zMxPge0wjIjV/9TDcjCmseGdrYuQ
-> ssh-rsa 6hPx7A
qKpB3nXTGN3gGW8K9XE3bfRTJpukEuIpb3Km/4hppWaA72gWXZOeSICTMZEf7u6/
oRYJpOX9DR1dbX4dXYTuyidyUWUQ6jr5xwzpVNO2EGfZj8bqfRjB4CBzf1ixYZx6
je6rsFPc10BU8rwYxb3F6ZdIJGpwmVnqNDHBcgl0uprUyGhEAAytKuS+QJO6h1/T
SvB49ToPr2gOLbYicuIL85Zo2PsHlQLQzt+2+jLWPShFUb9PPyyqbnp70TSu2vkD
UZMyxoe861E7ocx28zrm968TzR/UsRo8vLixvcAKlee02OpNe3eWnRlduDuoeR28
aUSXKnqPs3BPpce4/pcpnYXpqCUtrWILV6OQQ0qq3dZSif82CyHx9e8ftXyYMUZ2
1HBVLO7qMgx5oN1U70Oxy3ZOwv3UIqHhOe42BiF0cNpIEEtdWVl2RjVyN6pL/3ci
SZO2r2aS29oEOtxGGwQXlJJn1T7z94isotFFOst0/s4Qwp+bUv6G85CdVI3OJaJ2
5ACu4j/mtP7v6MkY/yTVU62Ne57BxI3Y4TvV8cJqIneeVQlsZwyE0YM1C6Lvp0J6
UUQjQRItfrX3bXuZZ6N6joPLN6wFARmxgIXAJtevaDC9IkSgpPWDSrs/Vfhj5IBD
zkuwSije7KzjKLQxQs2Qv5FabKN13xqu/VOPnAWBvCw
-> ssh-ed25519 Dfencg tglNj/t+eye5AZIEtxRjAg/TQtYWUxJO8wCKPM7ITHQ
3lZEIz8qPuoiyiNM8i8gFzX4spMmbNut51DQ9AOMjHc
--- rD33wYC+pDGERf8tYF96oGcUHZapBLHnVxPfi3PDu54
³õÍi‰Ú>¬t©D¡$ UŽ_I}ZÕ@{¶ «ßÂßáÜh1s²0`ž9î<>ñ[S.ooH´ Q<>

View file

@ -0,0 +1,48 @@
{ config, ... }:
let
host = "analytics.srx.digital";
in
{
age.secrets = {
plausibleAminPassword.file = ./password.age;
plausibleSecretKey.file = ./secret.age;
plausibleMailPassword.file = ./mail.age;
};
services = {
plausible = {
enable = true;
adminUser = {
activate = true;
email = "hostmaster@srx.digital";
passwordFile = config.age.secrets.plausibleAminPassword.path;
};
server = {
baseUrl = "https://${host}";
disableRegistration = true;
secretKeybaseFile = config.age.secrets.plausibleSecretKey.path;
};
mail.email = "no-reply@srx.digital";
};
nginx.virtualHosts."${host}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://localhost:${toString config.services.plausible.server.port}";
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

Binary file not shown.

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw BHpRYwBogzVgKIZ4UGrIhHs7KPGi5X1LHj3ePqbP4hY
9q1B89pSlI5ZD/42TAHGHL+yHa0a6hQR5Glezc6F/yg
-> ssh-ed25519 JzjriQ a8pElvBQ5eblGFHamtwGR4DnsvbBeiks1apLIdPs2AA
lhufHExUpSpVfAbUsLVOc3cR2oOMek2aiBEWTmxM468
-> ssh-rsa 6hPx7A
FFtNqcWwHH4Jr51oLzZnyGRuAwLQ+RFEHhXnjrt2xtSOV8b6LmDprr7swahjz7Cl
fjda2A0rBElySjfzc4QIAzNtqjTGIzcmeGxuQrEWhbQyGoARWgp/EQxSln7ydD1x
ancyiPWTgnd2Wld0mAxoMRhp/0EbNdRlA3JIxlIeb5qxQyxSy72Yg9S3qxjwi9SM
q0WbjW4Tg4IRh2J/okP0H7KLfABi0qXB9pfWCtBtuUxNMZ2ekEzouS8SBumd6oUR
AYpCGt/eAn/AwOXCdAD73onsB8odB0MVpH1RYZRU9wOuHPIuTZjH+h3AI4779AnM
jLDvMEZVe0KMXuYrb/94djkyfnIeECEzOtc7W5i8vS283zoAJdjqwIRY5P1JOB3K
XbutCM1QnWTmx3GZ99UwyAYEfRHWMHdsBOxygJXYgXFSWQJa0monKayX2s0GPHEz
s1/CwqJ95TPsucPfC59q7UVkIinDTk1osR42hjqoKbOPWvPR0O9yajhijvMiNJJp
fkEieUAzIT/YzoAkbRbC4Myzitq97VPsHIfFy2jPG1tf5gVN2yxZj3EFIKxM5tEN
kvx+WLhY/1HPP4c31hXs0+U6Gw5sML/j9ntJnD0cmPsyoeWbjUNhuX8ulM4zUv/X
k2d3k61F9nLMU6yEPw+V8mzPqf0UQaL7VW2UapTPkRQ
-> ssh-ed25519 Dfencg TLzld5xDEsoRFtEsMsahiRYg9A51L4ub8j0+vSRP/T0
9x8xqil/0DQUgWFPAp7yc9iWnJ9uX9MosTmwP+fjEKY
--- 0H2hrCIaBG759rpwwPW6x456cJTgayK838A178Vy12A
ï­lgÛ ‡*?2rq€‡”ð½°v~d <0C>«Æ5à+(4¤o¼[‰*¦Ö±YûýÚý§J

View file

@ -0,0 +1,21 @@
age-encryption.org/v1
-> ssh-ed25519 jDpDqw oIY2chGlJnQwRKGYUPMAmueSm/hRcj4qyEGRP4v99DA
iNW2k9EEprnMGRA2BHitSztk49lbbaQt/QJR40BC2K4
-> ssh-ed25519 JzjriQ mq5SPS7+UlBdCS6ZTtHy7MGZyIGudllK0QKTCzAbfR4
mGaMwonri+MgWovKK6iNrM6N4eTBZrHX5FrPij+lLz0
-> ssh-rsa 6hPx7A
KQmnB9QgfxBMTAUnO3l7YjDqQAskGQV3Hve7IRg4b4sG7HFW3aI9SIKXpECzVvT4
5kHqrSJNeQvo8fUdxdqvfXDPaxaVz+sWUmfbTzcZagAg6jkxEqlxeUzDw6mMgUu+
QjJsSFIwOIyNTlAjdcbH9ZdEKZKGsmbaPvMJlqrDs+PPKpXdkemHDcO2QrWdKqlX
N227ljuOf05Qgbn+JJw3m91tMscxDERFCfCt28jLX2IaPdriexLiJv6LDSLIyf3y
uhwtbVX24PmKhCPeL/PKYjFKyCXrWyhNSlOE7bQSgWSdfxinvSsjc8DWwfC7vhvo
aouEhAzXGd1AnQTBuUz8N68fQckSw5RVgYt4fbemN0Y22GFK51ZstjYmP+33xMTb
CKrs9/wB+nEmIzQ3N7yj+xbCoYmpJ7Ce2Q/qdp4MYUn3wVgoU4SiwWAZUe4aXgAd
LmGo1UF9MgPETDEhf4TDNQoQWL4mxNxHDTEUSfjsDd/yL1DVt0hfzoVsGgSXDHD2
GeTv0/nCu/NhWW8Lxo/HEcSg3JzFk0kd53LzvXTqqx8vnXW1lyzZcsZZp05gJxqo
iiKIzzUBbDhXR+pkhab9J6DOxKxIZabjjejsb8cKS8clOTwsyqExsbi/dYoQaFmd
Sr9B6FlpANDdhSTcnmOXJ9GHtNzjMNiiYil7TyMfl5M
-> ssh-ed25519 Dfencg /fWY2Ahqeu99jIPhbcbzlPZDz1Pev45LNSis0aCx0n0
IblFmuMc9XhDTrpG/iy0jeGpn8vmYytSdCeXpKeeJX0
--- Xezwhc5YCpsxxJWuvyNwYozTh1OXst7iT6xCuepciLI
pz¬sß©ÇĽÏÐfóÂTdÑD%w¼x<C2BC> /^*vÜ€•'ME Z±ÊðópoôzzõΌ´HçÓÌî.Å×ÖÞì[·6†äÊìÔ<C3AC>IJ1@ 8”8ãïäõ§<C3B5>¼¨¡jĤí€z

View file

@ -0,0 +1,14 @@
{ lib, pkgs, ... }:
{
services = {
postgresql.package = lib.mkForce pkgs.postgresql_14;
postgresqlBackup = {
enable = true;
startAt = "*-*-* 05:00:00";
compression = "none";
};
prometheus.exporters.postgres.enable = true;
};
}

Binary file not shown.

View file

@ -0,0 +1,107 @@
{ config, ... }:
let
host = "alerts.srx.digital";
in
{
age.secrets.prometheusAlertmanagerEnv.file = ./alertmanager-env.age;
services = {
prometheus = {
scrapeConfigs = [{
job_name = "alertmanager";
static_configs = [{
targets = [ "${config.services.prometheus.alertmanager.listenAddress}:${toString config.services.prometheus.alertmanager.port}" ];
}];
}];
alertmanager = {
enable = true;
webExternalUrl = "https://${host}";
listenAddress = "localhost";
environmentFile = config.age.secrets.prometheusAlertmanagerEnv.path;
configuration = {
global = {
smtp_smarthost = "mail.srx.digital:587";
smtp_auth_username = "$SMTP_USERNAME";
smtp_auth_password = "$SMTP_PASSWORD";
smtp_from = "$SMTP_USERNAME";
};
route = {
receiver = "default";
routes = [
{
receiver = "mail";
group_by = [ "host" ];
group_wait = "30s";
group_interval = "2m";
repeat_interval = "2h";
}
{
receiver = "telegram";
group_by = [ "host" ];
group_wait = "30s";
group_interval = "2m";
repeat_interval = "2h";
}
];
};
receivers = [
{ name = "default"; }
{
name = "mail";
email_configs = [{
to = "$SMTP_RECEIVER";
send_resolved = true;
}];
}
{
name = "telegram";
telegram_configs = [{
bot_token = "$TELEGRAM_BOT_TOKEN";
chat_id = -419206986;
parse_mode = "HTML";
}];
}
];
};
};
alertmanagers = [{
scheme = "http";
path_prefix = "/";
static_configs = [{
targets = [
"${config.services.prometheus.alertmanager.listenAddress}:${toString config.services.prometheus.alertmanager.port}"
];
}];
}];
};
nginx.virtualHosts."${host}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${config.services.prometheus.alertmanager.listenAddress}:${toString config.services.prometheus.alertmanager.port}";
};
oauth2-proxy.nginx.virtualHosts."${host}" = {
allowed_email_domains = [ "srx.digital" ];
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
};
}

View file

@ -0,0 +1,7 @@
{ config, ... }:
{
services.prometheus.scrapeConfigs = [{
job_name = "apcupsd";
static_configs = [{ targets = [ "srxnas00.vpn.srx.dev:${toString config.services.prometheus.exporters.apcupsd.port}" ]; }];
}];
}

View file

@ -0,0 +1,8 @@
{
imports = [
./prometheus.nix
./alertmanager.nix
./rules.nix
./check/apcupsd.nix
];
}

View file

@ -0,0 +1,93 @@
{ config, ... }:
let
host = "status.srx.digital";
hosts = [
"srxgp00.vpn.srx.dev"
"srxgp01.vpn.srx.dev"
"srxgp02.vpn.srx.dev"
"srxk8s00.vpn.srx.dev"
"srxnas00.vpn.srx.dev"
"srxnas01.vpn.srx.dev"
"copepod.vpn.srx.dev"
"diatome.vpn.srx.dev"
"opd00.vpn.srx.dev"
];
openwrt = [
"srxap01.op.hq.hh.srx.digital"
"srxfw01.op.hq.hh.srx.digital"
];
in
{
services = {
prometheus = {
enable = true;
extraFlags = [ "--storage.tsdb.retention.time=30d" ];
scrapeConfigs = [
{
job_name = "prometheus";
static_configs = [{
targets = [
"srxgp00.vpn.srx.dev:${toString config.services.prometheus.port}"
];
}];
}
{
job_name = "node";
static_configs = [
{
targets = builtins.concatMap
(name: [
"${name}:${toString config.services.prometheus.exporters.node.port}"
])
hosts;
}
];
}
{
job_name = "telegraf";
static_configs = [{
targets = builtins.concatMap (name: [ "${name}:9273" ]) hosts;
}];
}
{
job_name = "openwrt";
static_configs = [{
targets = builtins.concatMap (name: [ "${name}:9273" ]) openwrt;
}];
}
];
};
nginx.virtualHosts."${host}" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://${config.services.prometheus.listenAddress}:${toString config.services.prometheus.port}";
};
oauth2-proxy.nginx.virtualHosts."${host}" = {
allowed_email_domains = [ "srx.digital" ];
};
telegraf.extraConfig.inputs = {
x509_cert = [{
sources = [ "https://${host}:443" ];
tags.host = host;
interval = "10m";
}];
http_response = [{
urls = [ "https://${host}" ];
tags.host = host;
interval = "10m";
}];
};
grafana.provision.datasources.settings.datasources = [{
name = "Prometheus";
type = "prometheus";
access = "proxy";
url = "http://${config.services.prometheus.listenAddress}:${toString config.services.prometheus.port}";
isDefault = true;
}];
};
}

View file

@ -0,0 +1,37 @@
{ lib, pkgs, ... }:
let
getRuleFiles = lib.mapAttrs'
(
f: _: lib.nameValuePair (lib.removeSuffix ".nix" f) (import (./rules + "/${f}"))
)
(builtins.readDir ./rules);
formatRules =
rules:
(lib.mapAttrsToList (
name: opts: {
alert = name;
expr = opts.condition;
for = opts.time or "1m";
labels = opts.labels or { };
annotations.description = opts.description;
}
))
rules;
writeRuleFile =
name: value:
(pkgs.writeText "prometheus-rules-${name}.yaml" (
builtins.toJSON {
groups = [
{
inherit name;
rules = formatRules value;
}
];
}
));
in
{
services.prometheus.ruleFiles = lib.attrsets.mapAttrsToList writeRuleFile getRuleFiles;
}

View file

@ -0,0 +1,41 @@
{
apcupsd_line_volts = {
condition = "apcupsd_line_volts < 200";
description = "The UPS line voltage on {{$labels.instance}} dropped below 200V, indicating a potential blackout: {{$value}} V!";
};
apcupsd_battery_volts = {
condition = "apcupsd_battery_volts < 20";
description = "The UPS battery voltage on {{$labels.instance}} dropped below 20V: {{$value}} V!";
};
apcupsd_battery_volts_nominal = {
condition = "apcupsd_battery_nominal_volts < 20";
description = "The nominal UPS battery voltage on {{$labels.instance}} dropped below 20V: {{$value}} V!";
};
apcupsd_temperature = {
condition = "apcupsd_internal_temperature_celsius > 40";
description = "The internal UPS temperature on {{$labels.instance}} exceeds 40°C: {{$value}}°C!";
};
apcupsd_ups_load = {
condition = "apcupsd_ups_load_percent > 80";
description = "The UPS load on {{$labels.instance}} is higher than 80%: {{$value}}%!";
};
apcupsd_battery_time_left = {
condition = "apcupsd_battery_time_left_seconds < 600";
description = "Less than 10 minutes of UPS battery charge remaining on {{$labels.instance}}: {{$value}} seconds!";
};
apcupsd_battery_charge = {
condition = "apcupsd_battery_charge_percent < 80";
description = "Less than 80% battery charge remaining on {{$labels.instance}}: {{$value}}%!";
};
apcupsd_status_not_online = {
condition = ''apcupsd_info{status != "ONLINE"}'';
description = "The UPS status on {{$labels.instance}} is not online: {{$labels.status}}!";
};
}

View file

@ -0,0 +1,6 @@
{
dendrite_up = {
condition = "dendrite_up != 1";
description = "Dendrite on {{$labels.instance}} seems to be down!";
};
}

View file

@ -0,0 +1,16 @@
{
dns_query = {
condition = "dns_query_result_code != 0";
description = "{{$labels.domain}} : could retrieve A record {{$labels.instance}} from server {{$labels.server}}: {{$labels.result}}!";
};
dns_secure_query = {
condition = "secure_dns_state != 0";
description = "{{$labels.domain}} : could retrieve A record {{$labels.instance}} from server {{$labels.server}}: {{$labels.result}} for protocol {{$labels.protocol}}!";
};
dnssec_zone_days_left = {
condition = "dnssec_zone_record_resolves == 1 and dnssec_zone_record_days_left <= 10";
description = "{{$labels.domain}} : could retrieve A record {{$labels.instance}} from server {{$labels.server}}: {{$labels.result}} for protocol {{$labels.protocol}}!";
};
}

View file

@ -0,0 +1,8 @@
{
# cert_expiry = {
# # condition = "x509_cert_expiry < 7*24*3600";
# # description = "{{$labels.instance}}: The TLS certificate from {{$labels.source}} will expire in less than 7 days: {{$value}}s";
# condition = ''x509_cert_expiry{issuer_common_name="R3"} < ${toString (60 * 60 * 24 * 5)}'';
# description = "{{ $labels.san }} does expire in less than 5 days";
# };
}

View file

@ -0,0 +1,6 @@
{
# forgejo_http_500 = {
# condition = ''rate(promhttp_metric_handler_requests_total{job="forgejo", code="500"}[5m]) > 3'';
# description = "{{$labels.instance}}: forgejo instances error rate went up: {{$value}} errors in 5 minutes";
# };
}

View file

@ -0,0 +1,26 @@
{
# smart_errors = {
# condition = ''smart_device_health_ok{enabled!="Disabled"} != 1'';
# description = "{{$labels.instance}}: S.M.A.R.T reports: {{$labels.device}} ({{$labels.model}}) has errors.";
# };
mdraid_not_active = {
condition = ''node_md_state{state="active"} != 1'';
description = "{{$labels.instance}} with device {{$labels.device}} is in {{$labels.state}}.";
};
mdraid_is_recovering = {
condition = ''node_md_state{state="recovering"} == 1'';
description = "{{$labels.instance}} with device {{$labels.device}} is in {{$labels.state}}.";
};
mdraid_disks_down = {
condition = "mdstat_DisksDown != 0";
description = "{{$labels.instance}} with device {{$labels.device}} is in {{$labels.state}}.";
};
mdraid_disks_faild = {
condition = "mdstat_DisksFailed != 0";
description = "{{$labels.instance}} with device {{$labels.device}} is in {{$labels.state}}.";
};
}

View file

@ -0,0 +1,16 @@
{
http = {
condition = "http_response_result_code != 0";
description = "{{$labels.server}} : http request failed from {{$labels.instance}}: {{$labels.result}}!";
};
# http_not_ok = {
# condition = "0 * (http_response_http_response_code != 200) + 1";
# description = "{{ $labels.exported_server }} does not return Ok for more than 5 minutes";
# };
# http_match_failed = {
# condition = "http_response_response_string_match == 0";
# description = "{{$labels.server}} : http body not as expected; status code: {{$labels.status_code}}!";
# };
}

View file

@ -0,0 +1,17 @@
{
instance_down = {
condition = "up == 0";
time = "5m";
description = "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes.";
};
instance_reboot = {
condition = "system_uptime < 300";
description = "{{$labels.host}} just rebooted.";
};
instance_uptime = {
condition = "system_uptime > 2592000";
description = "{{$labels.host}} has been up for more than 30 days.";
};
}

View file

@ -0,0 +1,11 @@
{
postfix_queue_length = {
condition = "avg_over_time(postfix_queue_length[1h]) > 10";
description = "{{$labels.instance}}: postfix mail queue has undelivered {{$value}} items";
};
# https://doc.dovecot.org/configuration_manual/stats/old_statistics/
# dovecot_up
# dovecot_user_auth_failures
# dovecot_user_auth_db_tempfails
}

View file

@ -0,0 +1,18 @@
{
memory_under_pressure = {
condition = "rate(node_vmstat_pgmajfault[1m]) > 1000";
description = "{{$labels.instance}}: The node is under heavy memory pressure. High rate of major page faults: {{$value}}";
};
ram_using_90percent = {
condition = "mem_buffered + mem_free + mem_cached < mem_total * 0.1";
time = "1h";
description = "{{$labels.host}} is using at least 90% of its RAM for at least 1 hour.";
};
swap_using_30percent = {
condition = "mem_swap_total - (mem_swap_cached + mem_swap_free) > mem_swap_total * 0.3";
time = "30m";
description = "{{$labels.host}} is using 30% of its swap space for at least 30 minutes.";
};
}

View file

@ -0,0 +1,17 @@
{
## ssh: connect to host srxgp00.srx.digital port 22: Connection refused
# connection_failed = {
# condition = "net_response_result_code != 0";
# description = "{{$labels.server}}: connection to {{$labels.port}}({{$labels.protocol}}) failed from {{$labels.instance}}";
# };
# ping = {
# condition = "ping_result_code != 0";
# description = "{{$labels.url}}: ping from {{$labels.instance}} has failed!";
# };
# ping_high_latency = {
# condition = "ping_average_response_ms > 5000";
# description = "{{$labels.instance}}: ping probe from {{$labels.source}} is encountering high latency!";
# };
}

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