Compare commits
10 Commits
c3a11470b9
...
13e407847f
| Author | SHA1 | Date | |
|---|---|---|---|
| 13e407847f | |||
| f212c874b5 | |||
| 64b87222bd | |||
| 91a36ec0c6 | |||
| 4a21cb9a9d | |||
| 6ea769417b | |||
| 6a3ff12527 | |||
| 03548606e4 | |||
| 7c82eb5dc5 | |||
| e4abb866e9 |
@@ -0,0 +1 @@
|
|||||||
|
result
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./home-manager.nix
|
|
||||||
./hardware-configuration.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
|
||||||
|
|
||||||
boot.loader = {
|
|
||||||
limine = {
|
|
||||||
enable = true;
|
|
||||||
};
|
|
||||||
efi.canTouchEfiVariables = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.hostName = "benq-serv";
|
|
||||||
networking.networkmanager.enable = true;
|
|
||||||
|
|
||||||
time.timeZone = "Europe/Moscow";
|
|
||||||
|
|
||||||
i18n = {
|
|
||||||
defaultLocale = "en_US.UTF-8";
|
|
||||||
extraLocaleSettings = {
|
|
||||||
LC_TIME = "en_GB.UTF-8";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.xserver.xkb = {
|
|
||||||
layout = "us,ru";
|
|
||||||
options = "eurosign:e,caps:escape";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.pipewire = {
|
|
||||||
enable = true;
|
|
||||||
pulse.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
git
|
|
||||||
vim
|
|
||||||
];
|
|
||||||
|
|
||||||
services.openssh.enable = true;
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 22 80 443 ];
|
|
||||||
networking.firewall.allowedUDPPorts = [];
|
|
||||||
|
|
||||||
system.stateVersion = "25.05";
|
|
||||||
}
|
|
||||||
|
|
||||||
Generated
+112
-4
@@ -1,12 +1,82 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"disko": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777713215,
|
||||||
|
"narHash": "sha256-8GzXDOXckDWwST8TY5DbwYFjdvQLlP7K9CLSVx6iTTo=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "disko",
|
||||||
|
"rev": "63b4e7e6cf75307c1d26ac3762b886b5b0247267",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "disko",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dotfiles": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1778445660,
|
||||||
|
"narHash": "sha256-eiGc//3MdHJJSlIXcdtjT+Fdgg+xrK8eqYpRBALzFyw=",
|
||||||
|
"owner": "aloslider",
|
||||||
|
"repo": "dotfiles",
|
||||||
|
"rev": "5cf0727f8aede69b350e46284cf7147d6185ef56",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "aloslider",
|
||||||
|
"repo": "dotfiles",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"home-manager": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1778365864,
|
||||||
|
"narHash": "sha256-ImoT/wqmgMImf2dAC+E0MverAdA4QXsedOeES9B7Ezw=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"rev": "2f419037039a152448c5f4ae9494154753d1b399",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772542754,
|
"lastModified": 1773628058,
|
||||||
"narHash": "sha256-WGV2hy+VIeQsYXpsLjdr4GvHv5eECMISX1zKLTedhdg=",
|
"narHash": "sha256-hpXH0z3K9xv0fHaje136KY872VT2T5uwxtezlAskQgY=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "f8573b9c935cfaa162dd62cc9e75ae2db86f85df",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777954456,
|
||||||
|
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8c809a146a140c5c8806f13399592dbcb1bb5dc4",
|
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -16,9 +86,47 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775888245,
|
||||||
|
"narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "13043924aaa7375ce482ebe2494338e058282925",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs"
|
"disko": "disko",
|
||||||
|
"dotfiles": "dotfiles",
|
||||||
|
"home-manager": "home-manager",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"sops-nix": "sops-nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sops-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777944972,
|
||||||
|
"narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=",
|
||||||
|
"owner": "mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"rev": "c591bf665727040c6cc5cb409079acb22dcce33c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,16 +1,46 @@
|
|||||||
{
|
{
|
||||||
description = "Homelab server flake";
|
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
|
disko.url = "github:nix-community/disko";
|
||||||
|
dotfiles = {
|
||||||
|
url = "github:aloslider/dotfiles";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
home-manager = {
|
||||||
|
url = "github:nix-community/home-manager";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
sops-nix.url = "github:mic92/sops-nix";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs }@inputs: {
|
outputs =
|
||||||
nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
|
{ self, nixpkgs, ... }@inputs:
|
||||||
specialArgs = { inherit inputs; };
|
let
|
||||||
modules = [
|
system = "x86_64-linux";
|
||||||
./configuration.nix
|
pkgs = nixpkgs;
|
||||||
];
|
customLib = pkgs.lib.extend (
|
||||||
};
|
self: super: {
|
||||||
};
|
custom = (
|
||||||
|
import ./lib {
|
||||||
|
inherit (pkgs) lib;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixosConfigurations = {
|
||||||
|
homelab = pkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
specialArgs = {
|
||||||
|
inherit inputs;
|
||||||
|
inherit (inputs) disko;
|
||||||
|
lib = customLib;
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
./hosts/homelab/configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
|
||||||
# and may be overwritten by future invocations. Please make changes
|
|
||||||
# to /etc/nixos/configuration.nix instead.
|
|
||||||
{ config, lib, pkgs, modulesPath, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports =
|
|
||||||
[ (modulesPath + "/profiles/qemu-guest.nix")
|
|
||||||
];
|
|
||||||
|
|
||||||
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "sr_mod" "virtio_blk" ];
|
|
||||||
boot.initrd.kernelModules = [ ];
|
|
||||||
boot.kernelModules = [ "kvm-amd" ];
|
|
||||||
boot.extraModulePackages = [ ];
|
|
||||||
|
|
||||||
fileSystems."/" =
|
|
||||||
{ device = "/dev/disk/by-uuid/5454b429-e268-4399-9eaf-48e16951d1b4";
|
|
||||||
fsType = "btrfs";
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/boot" =
|
|
||||||
{ device = "/dev/disk/by-uuid/F6C1-695C";
|
|
||||||
fsType = "vfat";
|
|
||||||
options = [ "fmask=0077" "dmask=0077" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
swapDevices = [ ];
|
|
||||||
|
|
||||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
|
||||||
let
|
|
||||||
home-manager = builtins.fetchTarball {
|
|
||||||
url = "https://github.com/nix-community/home-manager/archive/release-25.05.tar.gz";
|
|
||||||
sha256 = "07pk5m6mxi666dclaxdwf7xrinifv01vvgxn49bjr8rsbh31syaq";
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
(import "${home-manager}/nixos")
|
|
||||||
];
|
|
||||||
|
|
||||||
users.users.benq.isNormalUser = true;
|
|
||||||
|
|
||||||
home-manager = {
|
|
||||||
useGlobalPkgs = true;
|
|
||||||
users.benq = { pkgs, ... }: {
|
|
||||||
home.packages = with pkgs; [
|
|
||||||
lazygit
|
|
||||||
tmux
|
|
||||||
];
|
|
||||||
home.stateVersion = "25.05";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
inputs.home-manager.nixosModules.home-manager
|
||||||
|
];
|
||||||
|
|
||||||
|
home-manager = {
|
||||||
|
extraSpecialArgs = {
|
||||||
|
inherit (inputs) disko dotfiles sops-nix;
|
||||||
|
};
|
||||||
|
useGlobalPkgs = true;
|
||||||
|
useUserPackages = true;
|
||||||
|
users.benq =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
imports = lib.flatten [
|
||||||
|
(map lib.custom.relativeToRoot (
|
||||||
|
[
|
||||||
|
"home/common/core"
|
||||||
|
]
|
||||||
|
++ (map (f: "home/common/optional/${f}") [
|
||||||
|
|
||||||
|
])
|
||||||
|
))
|
||||||
|
];
|
||||||
|
|
||||||
|
home.stateVersion = "25.05";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./git.nix
|
||||||
|
./packages.nix
|
||||||
|
# ./sops.nix
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.programs.git.options;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.programs.git.options = {
|
||||||
|
username = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "aloslider";
|
||||||
|
};
|
||||||
|
email = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "53711835+aloslider@users.noreply.github.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
programs.git = {
|
||||||
|
settings = {
|
||||||
|
user = {
|
||||||
|
name = cfg.username;
|
||||||
|
email = cfg.email;
|
||||||
|
};
|
||||||
|
init.defaultBranch = "master";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
chezmoi
|
||||||
|
fzf
|
||||||
|
lazygit
|
||||||
|
lazydocker
|
||||||
|
sops
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{ config, dotfiles, sops-nix, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
sops-nix.homeManagerModules.sops
|
||||||
|
];
|
||||||
|
|
||||||
|
sops = {
|
||||||
|
defaultSopsFile = "${dotfiles}/dot_config/sops/secrets.yaml";
|
||||||
|
age.keyFile = "${config.xdg.configHome}/sops/age/keys.txt";
|
||||||
|
};
|
||||||
|
|
||||||
|
home.file.".sops.yaml".source = "${dotfiles}/dot_sops.yaml";
|
||||||
|
|
||||||
|
xdg.configFile."sops" = {
|
||||||
|
source = "${dotfiles}/dot_config/sops";
|
||||||
|
recursive = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./disko.nix
|
||||||
|
./locale.nix
|
||||||
|
./nix.nix
|
||||||
|
./packages.nix
|
||||||
|
./sops.nix
|
||||||
|
./zsh.nix
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
disko,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.disko.cfg;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
disko.nixosModules.disko
|
||||||
|
];
|
||||||
|
|
||||||
|
options.disko.cfg = {
|
||||||
|
mainDevice = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Main disk name";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
disko.devices.disk.ssd = {
|
||||||
|
device = cfg.mainDevice;
|
||||||
|
type = "disk";
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
ESP = {
|
||||||
|
name = "ESP";
|
||||||
|
size = "1G";
|
||||||
|
type = "EF00";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "vfat";
|
||||||
|
mountpoint = "/boot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
root = {
|
||||||
|
name = "root";
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "btrfs";
|
||||||
|
extraArgs = [
|
||||||
|
"-f"
|
||||||
|
"-L"
|
||||||
|
"root"
|
||||||
|
];
|
||||||
|
mountpoint = "/";
|
||||||
|
mountOptions = [
|
||||||
|
"compress=zstd"
|
||||||
|
"noatime"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
time.timeZone = "Europe/Moscow";
|
||||||
|
|
||||||
|
i18n = {
|
||||||
|
defaultLocale = "en_US.UTF-8";
|
||||||
|
extraLocaleSettings = {
|
||||||
|
LC_TIME = "en_GB.UTF-8";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.xserver.xkb = {
|
||||||
|
layout = "us,ru";
|
||||||
|
options = "eurosign:e,caps:escape";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
nix = {
|
||||||
|
gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "daily";
|
||||||
|
options = "--delete-older-than 7d";
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
experimental-features = [
|
||||||
|
"nix-command"
|
||||||
|
"flakes"
|
||||||
|
];
|
||||||
|
auto-optimise-store = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{ congif, pkgs, ... }:
|
||||||
|
{
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
age
|
||||||
|
bat
|
||||||
|
bind
|
||||||
|
btop
|
||||||
|
git
|
||||||
|
jq
|
||||||
|
openssl
|
||||||
|
ripgrep
|
||||||
|
tmux
|
||||||
|
tree
|
||||||
|
vim
|
||||||
|
wget
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{ config, inputs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
inputs.sops-nix.nixosModules.sops
|
||||||
|
];
|
||||||
|
|
||||||
|
sops = {
|
||||||
|
defaultSopsFile = "/etc/sops/secrets.yaml";
|
||||||
|
age.keyFile = "/etc/age/key.txt";
|
||||||
|
validateSopsFiles = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
# programs.zsh.enable = true;
|
||||||
|
programs.nix-ld.enable = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
services.pipewire = {
|
||||||
|
enable = true;
|
||||||
|
pulse.enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
boot.kernelModules = [ "tcp_bbr" ];
|
||||||
|
boot.kernel.sysctl = {
|
||||||
|
"net.ipv4.tcp_congestion_control" = "bbr";
|
||||||
|
"net.core.deafult_qdisc" = "fq";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
services.endlessh = {
|
||||||
|
enable = true;
|
||||||
|
port = 22;
|
||||||
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.endlessh = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network.target" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
services.fail2ban = {
|
||||||
|
enable = true;
|
||||||
|
maxretry = 3;
|
||||||
|
bantime = "48h";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
fonts.fontDir.enable = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
ports = [ 6969 ];
|
||||||
|
settings = {
|
||||||
|
PasswordAuthentication = false;
|
||||||
|
PermitEmptyPasswords = false;
|
||||||
|
KbdInteractiveAuthentication = false;
|
||||||
|
PermitRootLogin = "no";
|
||||||
|
PubkeyAuthentication = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./gitea
|
||||||
|
./homarr
|
||||||
|
./immich
|
||||||
|
./jellyfin
|
||||||
|
./netbird
|
||||||
|
./pocket-id
|
||||||
|
./seafile
|
||||||
|
./traefik
|
||||||
|
./vaultwarden
|
||||||
|
./zapret2
|
||||||
|
./zerobyte
|
||||||
|
];
|
||||||
|
|
||||||
|
options.selfhosted.common = {
|
||||||
|
backend = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
rootTarget = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
network = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
address = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
mask = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
gateway = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
user = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
uid = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
group = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
gid = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
selfhosted = {
|
||||||
|
common = {
|
||||||
|
backend = "docker";
|
||||||
|
rootTarget.name = "sh-root";
|
||||||
|
network = {
|
||||||
|
name = "selfhosted";
|
||||||
|
address = "172.30.0.0";
|
||||||
|
mask = "24";
|
||||||
|
gateway = "172.30.0.1";
|
||||||
|
};
|
||||||
|
dataDir = "/mnt/data";
|
||||||
|
user = {
|
||||||
|
name = config.users.users.benq.name;
|
||||||
|
uid = "1000";
|
||||||
|
};
|
||||||
|
group = {
|
||||||
|
name = "users";
|
||||||
|
gid = "1000";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
jellyfin = {
|
||||||
|
name = "jellyfin";
|
||||||
|
subdomain = "media";
|
||||||
|
hostHttpPort = 5040;
|
||||||
|
hostUdpPort = 5041;
|
||||||
|
};
|
||||||
|
gitea = {
|
||||||
|
targetName = "gitea";
|
||||||
|
gitea = {
|
||||||
|
name = "gitea";
|
||||||
|
subdomain = "gitea";
|
||||||
|
hostWebPort = 5060;
|
||||||
|
hostSshPort = 5061;
|
||||||
|
};
|
||||||
|
postgresql = {
|
||||||
|
name = "gitea-db";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
immich = {
|
||||||
|
targetName = "immich";
|
||||||
|
version = "v2";
|
||||||
|
immich = {
|
||||||
|
name = "immich";
|
||||||
|
subdomain = "photos";
|
||||||
|
hostPort = 5090;
|
||||||
|
};
|
||||||
|
ml = {
|
||||||
|
name = "immich-ml";
|
||||||
|
};
|
||||||
|
redis = {
|
||||||
|
name = "immich-cache";
|
||||||
|
};
|
||||||
|
postgresql = {
|
||||||
|
name = "immich-db";
|
||||||
|
db = "immich";
|
||||||
|
user = "immich";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
netbird = {
|
||||||
|
targetName = "netbird";
|
||||||
|
subdomain = "nb";
|
||||||
|
dashboard = {
|
||||||
|
name = "nb-dashboard";
|
||||||
|
};
|
||||||
|
proxy = {
|
||||||
|
name = "nb-proxy";
|
||||||
|
};
|
||||||
|
server = {
|
||||||
|
name = "nb-server";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
homarr = {
|
||||||
|
name = "homarr";
|
||||||
|
subdomain = "dash";
|
||||||
|
hostPort = 5030;
|
||||||
|
};
|
||||||
|
pocket-id = {
|
||||||
|
name = "pocket-id";
|
||||||
|
subdomain = "id";
|
||||||
|
hostPort = 5020;
|
||||||
|
};
|
||||||
|
seafile = {
|
||||||
|
targetName = "seafile";
|
||||||
|
server = {
|
||||||
|
name = "sf-server";
|
||||||
|
subdomain = "cloud";
|
||||||
|
hostHttpPort = 5050;
|
||||||
|
};
|
||||||
|
mariadb = {
|
||||||
|
name = "sf-db";
|
||||||
|
};
|
||||||
|
redis = {
|
||||||
|
name = "sf-cache";
|
||||||
|
};
|
||||||
|
seadoc = {
|
||||||
|
name = "sf-seadoc";
|
||||||
|
hostHttpPort = 5051;
|
||||||
|
};
|
||||||
|
notification = {
|
||||||
|
name = "sf-notification";
|
||||||
|
hostHttpPort = 5052;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
traefik = {
|
||||||
|
name = "traefik";
|
||||||
|
ip = "172.30.0.10";
|
||||||
|
};
|
||||||
|
vaultwarden = {
|
||||||
|
name = "vaultwarden";
|
||||||
|
subdomain = "vault";
|
||||||
|
hostPort = 5010;
|
||||||
|
};
|
||||||
|
zapret2 = {
|
||||||
|
name = "zapret2";
|
||||||
|
socksPort = 6000;
|
||||||
|
ssPort = 6001;
|
||||||
|
};
|
||||||
|
zerobyte = {
|
||||||
|
name = "zerobyte";
|
||||||
|
subdomain = "backup";
|
||||||
|
hostPort = 5070;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation = lib.mkMerge [
|
||||||
|
{ oci-containers.backend = config.selfhosted.common.backend; }
|
||||||
|
(
|
||||||
|
if config.selfhosted.common.backend == "docker" then
|
||||||
|
{
|
||||||
|
docker = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if config.selfhosted.common.backend == "podman" then
|
||||||
|
{
|
||||||
|
podman = {
|
||||||
|
enable = true;
|
||||||
|
dockerCompat = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
abort "virtualisation.backend is not set"
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
services.netbird.clients = {
|
||||||
|
homelab = {
|
||||||
|
port = 51820;
|
||||||
|
openFirewall = true;
|
||||||
|
openInternalFirewall = true;
|
||||||
|
login = {
|
||||||
|
enable = true;
|
||||||
|
setupKeyFile = config.sops.secrets."netbird/setup_keys/homelab".path;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets = {
|
||||||
|
"netbird/setup_keys/homelab" = { };
|
||||||
|
"netbird/setup_keys/site" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."netbird-homelab.env".content = ''
|
||||||
|
NB_MANAGEMENT_URL=https://${cfg.netbird.subdomain}.${config.sops.placeholder."domains/serv"}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Override to pass env file
|
||||||
|
systemd.services = {
|
||||||
|
"netbird-homelab" = {
|
||||||
|
serviceConfig = {
|
||||||
|
EnvironmentFile = [ config.sops.templates."netbird-homelab.env".path ];
|
||||||
|
};
|
||||||
|
after = [ "sops-nix.service" ];
|
||||||
|
wants = [ "sops-nix.service" ];
|
||||||
|
};
|
||||||
|
"netbird-homelab-login" = {
|
||||||
|
after = [ "sops-nix.service" ];
|
||||||
|
wants = [ "sops-nix.service" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.targets."${common.backend}-${common.rootTarget.name}" = {
|
||||||
|
unitConfig = {
|
||||||
|
Description = "Root target for homelab stack";
|
||||||
|
};
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-network-${common.network.name}" = {
|
||||||
|
path = [ pkgs.${common.backend} ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStop = "${common.backend} network rm -f ${common.network.name}";
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
${common.backend} network inspect ${common.network.name} \
|
||||||
|
|| ${common.backend} network create ${common.network.name} --driver=bridge --subnet=${common.network.address}/${common.network.mask} --gateway=${common.network.gateway}
|
||||||
|
'';
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.interfaces = lib.mkMerge [
|
||||||
|
(lib.mkIf (config.selfhosted.common.backend == "podman") (
|
||||||
|
let
|
||||||
|
matchAll = if !config.networking.nftables.enable then "podman+" else "podman*";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"${matchAll}" = {
|
||||||
|
allowedUDPPorts = [ 53 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
))
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.gitea;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./gitea
|
||||||
|
./postgresql
|
||||||
|
];
|
||||||
|
|
||||||
|
options.selfhosted.gitea = {
|
||||||
|
targetName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
sops.secrets = {
|
||||||
|
"gitea/db_password" = { };
|
||||||
|
"gitea/client_id" = { };
|
||||||
|
"gitea/client_secret" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.targets."${common.backend}-${cfg.targetName}" = {
|
||||||
|
unitConfig = {
|
||||||
|
Description = "Root target for ${cfg.targetName}";
|
||||||
|
};
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.gitea;
|
||||||
|
giteaDomain = "${cfg.gitea.subdomain}.${config.sops.placeholder."domains/serv"}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.templates."gitea.env".content = ''
|
||||||
|
USER_UID=${common.user.uid}
|
||||||
|
USER_GID=${common.group.gid}
|
||||||
|
|
||||||
|
GITEA__database__DB_TYPE=postgres
|
||||||
|
GITEA__database__HOST=${cfg.postgresql.name}:5432
|
||||||
|
GITEA__database__NAME=gitea
|
||||||
|
GITEA__database__USER=gitea
|
||||||
|
GITEA__database__PASSWD=${config.sops.placeholder."gitea/db_password"}
|
||||||
|
|
||||||
|
TZ=Europe/Moscow
|
||||||
|
GITEA__time__DEFAULT_UI_LOCATION=Europe/Moscow
|
||||||
|
|
||||||
|
GITEA__server__DOMAIN=${giteaDomain}
|
||||||
|
GITEA__server__ROOT_URL=https://${giteaDomain}/
|
||||||
|
GITEA__server__HTTP_PORT=3000
|
||||||
|
GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION=false
|
||||||
|
GITEA__service__DISABLE_REGISTRATION=true
|
||||||
|
GITEA__service__SHOW_REGISTRATION_BUTTON=false
|
||||||
|
GITEA__service__ENABLE_BASIC_AUTHENTICATION=false
|
||||||
|
|
||||||
|
GITEA__service__ENABLE_PASSKEY_AUTHENTICATION=false
|
||||||
|
GITEA__server__START_SSH_SERVER=true
|
||||||
|
GITEA__server__SSH_DOMAIN=git.${config.sops.placeholder."domains/serv"}
|
||||||
|
GITEA__server__SSH_LISTEN_HOST=0.0.0.0
|
||||||
|
GITEA__server__SSH_LISTEN_PORT=2222
|
||||||
|
GITEA__server__SSH_PORT=2222
|
||||||
|
|
||||||
|
GITEA__security__INSTALL_LOCK=true
|
||||||
|
|
||||||
|
GITEA__repository__MAX_CREATION_LIMIT=50
|
||||||
|
|
||||||
|
GITEA__server__ENABLE_PASSWORD_SIGNIN_FORM=false
|
||||||
|
GITEA__server__ENABLE_BASIC_AUTHENTICATION=false
|
||||||
|
GITEA__openid__ENABLE_OPENID_SIGNIN=false
|
||||||
|
GITEA__openid__ENABLE_OPENID_SIGNUP=false
|
||||||
|
|
||||||
|
GITEA__oauth2_client__ENABLE_AUTO_REGISTRATION=true
|
||||||
|
GITEA__oauth2_client__ACCOUNT_LINKING=auto
|
||||||
|
GITEA__oauth2_client__USERNAME=preferred_username
|
||||||
|
|
||||||
|
GITEA__session__COOKIE_SECURE=true
|
||||||
|
GITEA__log__LEVEL=Info
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.gitea;
|
||||||
|
hostDataPath = "${common.dataDir}/gitea";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.gitea.gitea = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostWebPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
hostSshPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.gitea.name}" = {
|
||||||
|
image = "gitea/gitea:latest";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.gitea.hostWebPort}:3000"
|
||||||
|
"0.0.0.0:${toString cfg.gitea.hostSshPort}:2222"
|
||||||
|
];
|
||||||
|
environmentFiles = [ config.sops.templates."gitea.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/data:/data"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.gitea.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-${cfg.postgresql.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-${cfg.postgresql.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/data
|
||||||
|
chown -R ${common.user.uid}:${common.group.gid} ${hostDataPath}/data
|
||||||
|
chmod -R 0775 ${hostDataPath}/data
|
||||||
|
'';
|
||||||
|
postStart =
|
||||||
|
let
|
||||||
|
engine =
|
||||||
|
if common.backend == "podman" then
|
||||||
|
pkgs.podman
|
||||||
|
else if common.backend == "docker" then
|
||||||
|
pkgs.docker
|
||||||
|
else
|
||||||
|
abort "Unknown backend ${common.backend}";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
set -euo pipefail
|
||||||
|
DOMAIN=$(cat ${config.sops.secrets."domains/serv".path})
|
||||||
|
CLIENT_ID=$(cat ${config.sops.secrets."gitea/client_id".path})
|
||||||
|
CLIENT_SECRET=$(cat ${config.sops.secrets."gitea/client_secret".path})
|
||||||
|
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if ${pkgs.curl}/bin/curl -sf http://127.0.0.1:${toString cfg.gitea.hostWebPort}/api/v1/version >/dev/null 2>&1; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "Waiting for Gitea to be ready... ($i/20)"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
if ${engine}/bin/${common.backend} exec --user ${common.user.uid} ${cfg.gitea.name} \
|
||||||
|
gitea admin auth list 2>/dev/null | grep -q 'PocketID'; then
|
||||||
|
echo "PocketID auth source already registered"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
${engine}/bin/${common.backend} exec --user ${common.user.uid} ${cfg.gitea.name} \
|
||||||
|
gitea admin auth add-oauth \
|
||||||
|
--name "PocketID" \
|
||||||
|
--provider "openidConnect" \
|
||||||
|
--key "$CLIENT_ID" \
|
||||||
|
--secret "$CLIENT_SECRET" \
|
||||||
|
--auto-discover-url "https://${config.selfhosted.pocket-id.subdomain}.$DOMAIN/.well-known/openid-configuration" \
|
||||||
|
--scopes "openid email profile" \
|
||||||
|
--skip-local-2fa
|
||||||
|
|
||||||
|
echo "PocketID auth source registered successfully"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.gitea;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.templates."gitea-db.env".content = ''
|
||||||
|
POSTGRES_DB=gitea
|
||||||
|
POSTGRES_USER=gitea
|
||||||
|
POSTGRES_PASSWORD=${config.sops.placeholder."gitea/db_password"}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.gitea;
|
||||||
|
hostDataPath = "${common.dataDir}/gitea";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.gitea.postgresql = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.postgresql.name}" = {
|
||||||
|
image = "postgres:16";
|
||||||
|
environmentFiles = [ config.sops.templates."gitea-db.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/dbData:/var/lib/postgresql/data"
|
||||||
|
];
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
"--network-alias=${cfg.postgresql.name}"
|
||||||
|
"--health-cmd=pg_isready -U gitea"
|
||||||
|
"--health-interval=10s"
|
||||||
|
"--health-timeout=5s"
|
||||||
|
"--health-retries=5"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.postgresql.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/dbData
|
||||||
|
chown 70:70 ${hostDataPath}/dbData
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.homarr;
|
||||||
|
poCfg = config.selfhosted.pocket-id;
|
||||||
|
oidpDomain = "https://${poCfg.subdomain}.${config.sops.placeholder."domains/serv"}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"homarr/enc_key" = { };
|
||||||
|
"homarr/client_id" = { };
|
||||||
|
"homarr/client_secret" = { };
|
||||||
|
"homarr/nextauth_secret" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."homarr.env" = {
|
||||||
|
content = ''
|
||||||
|
SECRET_ENCRYPTION_KEY=${config.sops.placeholder."homarr/enc_key"}
|
||||||
|
NEXTAUTH_SECRET=${config.sops.placeholder."homarr/nextauth_secret"}
|
||||||
|
AUTH_PROVIDERS=oidc
|
||||||
|
AUTH_OIDC_CLIENT_ID=${config.sops.placeholder."homarr/client_id"}
|
||||||
|
AUTH_OIDC_CLIENT_SECRET=${config.sops.placeholder."homarr/client_secret"}
|
||||||
|
AUTH_OIDC_ISSUER=${oidpDomain}
|
||||||
|
AUTH_OIDC_CLIENT_NAME="Pocket ID"
|
||||||
|
AUTH_OIDC_SCOPE_OVERWRITE=openid email profile groups
|
||||||
|
AUTH_OIDC_GROUPS_ATTRIBUTE=groups
|
||||||
|
AUTH_LOGOUT_REDIRECT_URL=${oidpDomain}
|
||||||
|
AUTH_OIDC_AUTO_LOGIN=true
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.homarr;
|
||||||
|
hostDataPath = "${common.dataDir}/homarr";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.homarr = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.name}" = {
|
||||||
|
image = "ghcr.io/homarr-labs/homarr:latest";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.hostPort}:7575"
|
||||||
|
];
|
||||||
|
environmentFiles = [ config.sops.templates."homarr.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/data:/appdata"
|
||||||
|
"/var/run/docker.sock:/var/run/docker.sock"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/data
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.immich;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./immich
|
||||||
|
./ml
|
||||||
|
./postgresql
|
||||||
|
./redis
|
||||||
|
];
|
||||||
|
|
||||||
|
options.selfhosted.immich = {
|
||||||
|
targetName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
version = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
sops.secrets = {
|
||||||
|
"immich/db_password" = { };
|
||||||
|
"immich/client_id" = { };
|
||||||
|
"immich/client_secret" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.targets."${common.backend}-${cfg.targetName}" = {
|
||||||
|
unitConfig = {
|
||||||
|
Description = "Root target for ${cfg.targetName}";
|
||||||
|
};
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
sops.templates."immich.env".content = ''
|
||||||
|
DB_PASSWORD=${config.sops.placeholder."immich/db_password"}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.immich;
|
||||||
|
cfgPath = "/config/immich.json";
|
||||||
|
hostDataPath = "${common.dataDir}/immich";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.immich.immich = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.immich.name}" = {
|
||||||
|
image = "ghcr.io/immich-app/immich-server:${cfg.version}";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.immich.hostPort}:2283"
|
||||||
|
];
|
||||||
|
environmentFiles = [ config.sops.templates."immich.env".path ];
|
||||||
|
environment = {
|
||||||
|
IMMICH_VERSION = cfg.version;
|
||||||
|
DB_HOSTNAME = cfg.postgresql.name;
|
||||||
|
DB_DATABASE_NAME = cfg.postgresql.db;
|
||||||
|
DB_USERNAME = cfg.postgresql.user;
|
||||||
|
REDIS_HOSTNAME = cfg.redis.name;
|
||||||
|
};
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/data:/data"
|
||||||
|
"/etc/localtime:/etc/localtime:ro"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.immich.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${cfg.postgresql.name}.service"
|
||||||
|
"${common.backend}-${cfg.redis.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${cfg.postgresql.name}.service"
|
||||||
|
"${common.backend}-${cfg.redis.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/data
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
sops.templates."immich-db.env".content = ''
|
||||||
|
POSTGRES_PASSWORD=${config.sops.placeholder."immich/db_password"}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.immich;
|
||||||
|
hostDataPath = "${common.dataDir}/immich";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfhosted.immich.ml = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.ml.name}" = {
|
||||||
|
image = "ghcr.io/immich-app/immich-machine-learning:${cfg.version}";
|
||||||
|
environment = {
|
||||||
|
IMMICH_VERSION = cfg.version;
|
||||||
|
};
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/cache:/cache"
|
||||||
|
];
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.ml.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/cache
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
sops.templates."immich-db.env".content = ''
|
||||||
|
POSTGRES_PASSWORD=${config.sops.placeholder."immich/db_password"}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.immich;
|
||||||
|
hostDataPath = "${common.dataDir}/immich";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.immich.postgresql = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
db = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.postgresql.name}" = {
|
||||||
|
image = "ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23";
|
||||||
|
environmentFiles = [ config.sops.templates."immich-db.env".path ];
|
||||||
|
environment = {
|
||||||
|
POSTGRES_DB = cfg.postgresql.db;
|
||||||
|
POSTGRES_USER = cfg.postgresql.user;
|
||||||
|
POSTGRES_INITDB_ARGS = "--data-checksums";
|
||||||
|
};
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/db:/var/lib/postgresql/data"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
"--shm-size=128m"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.postgresql.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/db
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.immich;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfhosted.immich.redis = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.redis.name}" = {
|
||||||
|
image = "docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9";
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
"--health-cmd=redis-cli ping || exit 1"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.redis.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.jellyfin;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."jellyfin.env".content = ''
|
||||||
|
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.jellyfin;
|
||||||
|
hostDataPath = "${common.dataDir}/jellyfin";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.jellyfin = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostHttpPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
hostUdpPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.name} = {
|
||||||
|
image = "docker.io/jellyfin/jellyfin:10.11.11";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.hostHttpPort}:8096"
|
||||||
|
"0.0.0.0:${toString cfg.hostUdpPort}:7359"
|
||||||
|
];
|
||||||
|
user = "${common.user.uid}:${common.group.gid}";
|
||||||
|
environmentFiles = [ config.sops.templates."jellyfin.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/config:/config"
|
||||||
|
"${hostDataPath}/cache:/cache"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/{config,cache}
|
||||||
|
chown -R ${common.user.uid}:${common.group.gid} ${hostDataPath}
|
||||||
|
chmod -R 0775 ${hostDataPath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
fullDomain = "${config.selfhosted.netbird.subdomain}.${config.sops.placeholder."domains/serv"}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.templates."nb-dashboard.env".content = ''
|
||||||
|
NETBIRD_MGMT_API_ENDPOINT=https://${fullDomain}
|
||||||
|
NETBIRD_MGMT_GRPC_API_ENDPOINT=https://${fullDomain}
|
||||||
|
|
||||||
|
# Initial NB setup: custom OIDCP is added manually in panel later
|
||||||
|
AUTH_AUTHORITY=https://${fullDomain}/oauth2
|
||||||
|
AUTH_AUDIENCE=netbird-dashboard
|
||||||
|
AUTH_CLIENT_ID=netbird-dashboard
|
||||||
|
AUTH_CLIENT_SECRET=
|
||||||
|
USE_AUTH0=false
|
||||||
|
AUTH_SUPPORTED_SCOPES=openid profile email groups
|
||||||
|
AUTH_REDIRECT_URI=/nb-auth
|
||||||
|
AUTH_SILENT_REDIRECT_URI=/nb-silent-auth
|
||||||
|
|
||||||
|
LETSENCRYPT_DOMAIN=none
|
||||||
|
LETSENCRYPT_EMAIL=${config.sops.placeholder."letsEncrypt/email"}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.netbird;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.netbird.dashboard = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.dashboard.name} = {
|
||||||
|
image = "netbirdio/dashboard:latest";
|
||||||
|
environmentFiles = [ config.sops.templates."nb-dashboard.env".path ];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network-alias=${cfg.dashboard.name}"
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.dashboard.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-${cfg.server.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-${cfg.server.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.netbird;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./dashboard
|
||||||
|
./proxy
|
||||||
|
./server
|
||||||
|
];
|
||||||
|
|
||||||
|
options.selfhosted.netbird = {
|
||||||
|
targetName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
sops.secrets = {
|
||||||
|
"netbird/proxy/token" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.targets."${common.backend}-${cfg.targetName}" = {
|
||||||
|
unitConfig = {
|
||||||
|
Description = "Root target for ${cfg.targetName}";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.netbird;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.templates."nb-proxy.env".content = ''
|
||||||
|
NB_PROXY_DEBUG_LOGS=false
|
||||||
|
NB_PROXY_PROXY_PROTOCOL=true
|
||||||
|
NB_PROXY_MANAGEMENT_ADDRESS=http://${cfg.server.name}:80
|
||||||
|
NB_PROXY_ALLOW_INSECURE=true
|
||||||
|
NB_PROXY_DOMAIN=${cfg.subdomain}.${config.sops.placeholder."domains/serv"}
|
||||||
|
NB_PROXY_ADDRESS=:8443
|
||||||
|
NB_PROXY_TOKEN=${config.sops.placeholder."netbird/proxy/token"}
|
||||||
|
NB_PROXY_FORWARDED_PROTO=https
|
||||||
|
NB_PROXY_CERTIFICATE_DIRECTORY=/certs
|
||||||
|
NB_PROXY_ACME_CERTIFICATES=true
|
||||||
|
NB_PROXY_ACME_CHALLENGE_TYPE=tls-alpn-01
|
||||||
|
NB_PROXY_TRUSTED_PROXIES=${config.selfhosted.traefik.ip}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.netbird;
|
||||||
|
hostDataPath = "${common.dataDir}/netbird/proxy";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.netbird.proxy = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.proxy.name} = {
|
||||||
|
image = "netbirdio/reverse-proxy:latest";
|
||||||
|
environmentFiles = [ config.sops.templates."nb-proxy.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/certs:/certs:rw"
|
||||||
|
];
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:2222:2222/tcp" # gitea
|
||||||
|
"0.0.0.0:7359:7359/udp" # jelly
|
||||||
|
"0.0.0.0:51820:51820/udp"
|
||||||
|
];
|
||||||
|
dependsOn = [ cfg.server.name ];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network-alias=${cfg.proxy.name}"
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.proxy.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${cfg.server.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${cfg.server.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/certs
|
||||||
|
chown -R ${common.user.uid}:${common.group.gid} ${hostDataPath}
|
||||||
|
chmod -R 0755 ${hostDataPath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.netbird;
|
||||||
|
fullDomain = "${cfg.subdomain}.${config.sops.placeholder."domains/serv"}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"netbird/server/auth_secret" = { };
|
||||||
|
"netbird/server/enc_key" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."nb-server.yaml".content = ''
|
||||||
|
server:
|
||||||
|
listenAddress: ":80"
|
||||||
|
exposedAddress: "https://${fullDomain}:443"
|
||||||
|
stunPorts:
|
||||||
|
- 3478
|
||||||
|
metricsPort: 9090
|
||||||
|
healthcheckAddress: ":9000"
|
||||||
|
logLevel: "info"
|
||||||
|
logFile: "console"
|
||||||
|
|
||||||
|
authSecret: "${config.sops.placeholder."netbird/server/auth_secret"}"
|
||||||
|
|
||||||
|
auth:
|
||||||
|
issuer: "https://${fullDomain}/oauth2"
|
||||||
|
dashboardRedirectURIs:
|
||||||
|
- "https://${fullDomain}/nb-auth"
|
||||||
|
- "https://${fullDomain}/nb-silent-auth"
|
||||||
|
cliRedirectURIs:
|
||||||
|
- "http://localhost:53000/"
|
||||||
|
localAuthDisabled: false
|
||||||
|
|
||||||
|
store:
|
||||||
|
engine: "sqlite"
|
||||||
|
encryptionKey: "${config.sops.placeholder."netbird/server/enc_key"}"
|
||||||
|
|
||||||
|
reverseProxy:
|
||||||
|
trustedHTTPProxies:
|
||||||
|
- "${config.selfhosted.traefik.ip}/32"
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.netbird;
|
||||||
|
hostDataPath = "${common.dataDir}/netbird/server";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.netbird.server = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.server.name} = {
|
||||||
|
image = "netbirdio/netbird-server:latest";
|
||||||
|
volumes = [
|
||||||
|
"${config.sops.templates."nb-server.yaml".path}:/etc/netbird/config.yaml:rw"
|
||||||
|
"${hostDataPath}/data:/var/lib/netbird:rw"
|
||||||
|
];
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:3478:3478/udp"
|
||||||
|
];
|
||||||
|
cmd = [
|
||||||
|
"--config"
|
||||||
|
"/etc/netbird/config.yaml"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network-alias=${cfg.server.name}"
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.server.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/data
|
||||||
|
chown -R ${common.user.uid}:${common.group.gid} ${hostDataPath}
|
||||||
|
chmod -R 0755 ${hostDataPath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.pocket-id;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"pocket_id/enc_key" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."pocket-id.env" = {
|
||||||
|
content = ''
|
||||||
|
APP_URL=https://${cfg.subdomain}.${config.sops.placeholder."domains/serv"}
|
||||||
|
ENCRYPTION_KEY=${config.sops.placeholder."pocket_id/enc_key"}
|
||||||
|
TRUST_PROXY=true
|
||||||
|
ANALYTICS_DISABLED=true
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.pocket-id;
|
||||||
|
hostDataPath = "${common.dataDir}/pocket-id";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.pocket-id = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.name} = {
|
||||||
|
image = "ghcr.io/pocket-id/pocket-id:latest";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.hostPort}:1411"
|
||||||
|
];
|
||||||
|
user = "${common.user.uid}:${common.group.gid}";
|
||||||
|
environmentFiles = [ config.sops.templates."pocket-id.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/data:/app/data"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/letsencrypt
|
||||||
|
chown -R ${common.user.uid}:${common.group.gid} ${hostDataPath}
|
||||||
|
chmod -R 0775 ${hostDataPath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./mariadb
|
||||||
|
./notification
|
||||||
|
./redis
|
||||||
|
./seadoc
|
||||||
|
./server
|
||||||
|
];
|
||||||
|
|
||||||
|
options.selfhosted.seafile = {
|
||||||
|
targetName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
systemd.targets."${common.backend}-${cfg.targetName}" = {
|
||||||
|
unitConfig = {
|
||||||
|
Description = "Root target for ${cfg.targetName}";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"seafile/mysql_password" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."seafile-db.env".content = ''
|
||||||
|
MYSQL_ROOT_PASSWORD=${config.sops.placeholder."seafile/mysql_password"}
|
||||||
|
MYSQL_LOG_CONSOLE=true
|
||||||
|
MARIADB_AUTO_UPGRADE=1
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
hostDataPath = "${common.dataDir}/seafile";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.seafile.mariadb = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.mariadb.name} = {
|
||||||
|
image = "docker.io/mariadb:10.11";
|
||||||
|
environmentFiles = [ config.sops.templates."seafile-db.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/mysql:/var/lib/mysql"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
"--health-cmd=/usr/local/bin/healthcheck.sh --connect --mariadbupgrade --innodb_initialized"
|
||||||
|
"--health-interval=20s"
|
||||||
|
"--health-start-period=30s"
|
||||||
|
"--health-timeout=5s"
|
||||||
|
"--health-retries=10"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.mariadb.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/mysql
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.templates."seafile-notification.env".content = ''
|
||||||
|
SEAFILE_MYSQL_DB_HOST=${cfg.mariadb.name}
|
||||||
|
SEAFILE_MYSQL_DB_PORT=3306
|
||||||
|
SEAFILE_MYSQL_DB_USER=seafile
|
||||||
|
SEAFILE_MYSQL_DB_PASSWORD=${config.sops.placeholder."seafile/mysql_password"}
|
||||||
|
SEAFILE_MYSQL_DB_CCNET_DB_NAME=ccnet_db
|
||||||
|
SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=seafile_db
|
||||||
|
JWT_PRIVATE_KEY=${config.sops.placeholder."seafile/jwt_private_key"}
|
||||||
|
SEAFILE_LOG_TO_STDOUT=false
|
||||||
|
NOTIFICATION_SERVER_LOG_LEVEL=info
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
hostDataPath = "${common.dataDir}/seafile";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.seafile.notification = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostHttpPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.notification.name} = {
|
||||||
|
image = "docker.io/seafileltd/notification-server:13.0-latest";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.notification.hostHttpPort}:8083"
|
||||||
|
];
|
||||||
|
environmentFiles = [ config.sops.templates."seafile-notification.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/notification/logs:/shared/seafile/logs"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.notification.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
"${common.backend}-${cfg.mariadb.name}.service"
|
||||||
|
"${common.backend}-${cfg.server.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
"${common.backend}-${cfg.mariadb.name}.service"
|
||||||
|
"${common.backend}-${cfg.server.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
path = with pkgs; [ curl gawk ];
|
||||||
|
preStart = ''
|
||||||
|
echo "Waiting for Seafile server to be ready..."
|
||||||
|
for i in {1..60}; do
|
||||||
|
status=$(curl -sI --max-time 5 "http://localhost:${toString cfg.server.hostHttpPort}" | head -1 | awk '{print $2}')
|
||||||
|
|
||||||
|
if [[ "$status" =~ ^(200|301|302)$ ]]; then
|
||||||
|
echo "Seafile server is up (HTTP $status, attempt $i)"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Seafile not ready yet (status: $status) ... ($i/60)"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p ${hostDataPath}/notification
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"seafile/redis_password" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."seafile-redis.env".content = ''
|
||||||
|
REDIS_PASSWORD=${config.sops.placeholder."seafile/redis_password"}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.seafile.redis = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.redis.name} = {
|
||||||
|
image = "docker.io/redis:7";
|
||||||
|
environmentFiles = [ config.sops.templates."seafile-redis.env".path ];
|
||||||
|
cmd = [ "/bin/sh" "-c" "exec redis-server --requirepass \"$REDIS_PASSWORD\"" ];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.redis.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.templates."seadoc.env".content = ''
|
||||||
|
DB_HOST=${cfg.mariadb.name}
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USER=seafile
|
||||||
|
DB_PASSWORD=${config.sops.placeholder."seafile/mysql_password"}
|
||||||
|
DB_NAME=seahub_db
|
||||||
|
TIME_ZONE=${config.time.timeZone}
|
||||||
|
JWT_PRIVATE_KEY=${config.sops.placeholder."seafile/jwt_private_key"}
|
||||||
|
NON_ROOT=false
|
||||||
|
SEAHUB_SERVICE_URL=http://${cfg.server.name}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
hostDataPath = "${common.dataDir}/seafile";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.seafile.seadoc = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostHttpPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.seadoc.name} = {
|
||||||
|
image = "docker.io/seafileltd/sdoc-server:2.0-latest";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.seadoc.hostHttpPort}:80"
|
||||||
|
];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/seadoc/shared:/shared"
|
||||||
|
];
|
||||||
|
environmentFiles = [ config.sops.templates."seadoc.env".path ];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.seadoc.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
"${common.backend}-${cfg.mariadb.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
"${common.backend}-${cfg.mariadb.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/seadoc/shared
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
poCfg = config.selfhosted.pocket-id;
|
||||||
|
domain = config.sops.placeholder."domains/serv";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"seafile/admin_email" = { };
|
||||||
|
"seafile/admin_password" = { };
|
||||||
|
"seafile/client_id" = { };
|
||||||
|
"seafile/client_secret" = { };
|
||||||
|
"seafile/jwt_private_key" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."seafile.env".content = ''
|
||||||
|
SEAFILE_MYSQL_DB_HOST=${cfg.mariadb.name}
|
||||||
|
SEAFILE_MYSQL_DB_PORT=3306
|
||||||
|
SEAFILE_MYSQL_DB_USER=seafile
|
||||||
|
SEAFILE_MYSQL_DB_PASSWORD=${config.sops.placeholder."seafile/mysql_password"}
|
||||||
|
SEAFILE_MYSQL_DB_CCNET_DB_NAME=ccnet_db
|
||||||
|
SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=seafile_db
|
||||||
|
SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=seahub_db
|
||||||
|
TIME_ZONE=${config.time.timeZone}
|
||||||
|
INIT_SEAFILE_MYSQL_ROOT_PASSWORD=${config.sops.placeholder."seafile/mysql_password"}
|
||||||
|
INIT_SEAFILE_ADMIN_EMAIL=${config.sops.placeholder."seafile/admin_email"}
|
||||||
|
INIT_SEAFILE_ADMIN_PASSWORD=${config.sops.placeholder."seafile/admin_password"}
|
||||||
|
SEAFILE_SERVER_HOSTNAME=${cfg.server.subdomain}.${domain}
|
||||||
|
SEAFILE_SERVER_PROTOCOL=https
|
||||||
|
SEAFILE_SERVER_LETSENCRYPT=false
|
||||||
|
SITE_ROOT=/
|
||||||
|
NON_ROOT=false
|
||||||
|
JWT_PRIVATE_KEY=${config.sops.placeholder."seafile/jwt_private_key"}
|
||||||
|
SEAFILE_LOG_TO_STDOUT=false
|
||||||
|
ENABLE_GO_FILESERVER=true
|
||||||
|
ENABLE_SEADOC=true
|
||||||
|
SEADOC_SERVER_URL=https://${cfg.server.subdomain}.${domain}/sdoc-server
|
||||||
|
CACHE_PROVIDER=redis
|
||||||
|
REDIS_HOST=${cfg.redis.name}
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=${config.sops.placeholder."seafile/redis_password"}
|
||||||
|
ENABLE_NOTIFICATION_SERVER=true
|
||||||
|
INNER_NOTIFICATION_SERVER_URL=http://${cfg.notification.name}:8083
|
||||||
|
NOTIFICATION_SERVER_URL=https://${cfg.server.subdomain}.${domain}/notification
|
||||||
|
ENABLE_SEAFILE_AI=false
|
||||||
|
MD_FILE_COUNT_LIMIT=100000
|
||||||
|
'';
|
||||||
|
|
||||||
|
sops.templates."seahub_settings.py".content = ''
|
||||||
|
ENABLE_VIDEO_THUMBNAIL = True
|
||||||
|
|
||||||
|
ENABLE_OAUTH = True
|
||||||
|
OAUTH_CREATE_UNKNOWN_USER = True
|
||||||
|
OAUTH_ACTIVATE_USER_AFTER_CREATION = True
|
||||||
|
OAUTH_ENABLE_INSECURE_TRANSPORT = False
|
||||||
|
OAUTH_CLIENT_ID = "${config.sops.placeholder."seafile/client_id"}"
|
||||||
|
OAUTH_CLIENT_SECRET = "${config.sops.placeholder."seafile/client_secret"}"
|
||||||
|
OAUTH_REDIRECT_URL = "https://${cfg.server.subdomain}.${domain}/oauth/callback"
|
||||||
|
OAUTH_PROVIDER = "pocket-id"
|
||||||
|
OAUTH_PROVIDER_DOMAIN = "pocket-id"
|
||||||
|
OAUTH_AUTHORIZATION_URL = "https://${poCfg.subdomain}.${domain}/authorize"
|
||||||
|
OAUTH_TOKEN_URL = "https://${poCfg.subdomain}.${domain}/api/oidc/token"
|
||||||
|
OAUTH_USER_INFO_URL = "https://${poCfg.subdomain}.${domain}/api/oidc/userinfo"
|
||||||
|
OAUTH_SCOPE = [
|
||||||
|
"openid",
|
||||||
|
"profile",
|
||||||
|
"email"
|
||||||
|
]
|
||||||
|
OAUTH_ATTRIBUTE_MAP = {
|
||||||
|
"sub": (True, "uid"),
|
||||||
|
"name": (False, "name"),
|
||||||
|
"email": (False, "contact_email"),
|
||||||
|
}
|
||||||
|
CLIENT_SSO_VIA_LOCAL_BROWSER = True
|
||||||
|
|
||||||
|
ENABLE_WEBDAV_SECRET = True
|
||||||
|
WEBDAV_SECRET_MIN_LENGTH = 8
|
||||||
|
SERVICE_URL = "https://${cfg.server.subdomain}.${domain}"
|
||||||
|
USE_X_FORWARDED_HOST = True
|
||||||
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
ALLOWED_HOSTS = [".${domain}"]
|
||||||
|
CSRF_COOKIE_SECURE = True
|
||||||
|
CSRF_COOKIE_SAMESITE = "Lax"
|
||||||
|
CSRF_TRUSTED_ORIGINS = [
|
||||||
|
"https://${cfg.server.subdomain}.${domain}"
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
|
||||||
|
sops.templates."seafdav.conf".content = ''
|
||||||
|
[WEBDAV]
|
||||||
|
enabled = true
|
||||||
|
port = 8080
|
||||||
|
debug = true
|
||||||
|
share_name = /seafdav
|
||||||
|
workers = 5
|
||||||
|
timeout = 1200
|
||||||
|
'';
|
||||||
|
|
||||||
|
sops.templates."seafile.conf".content = ''
|
||||||
|
[quota]
|
||||||
|
default = 100
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.seafile;
|
||||||
|
hostDataPath = "${common.dataDir}/seafile";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.seafile.server = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostHttpPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.server.name} = {
|
||||||
|
image = "docker.io/seafileltd/seafile-mc:13.0-latest";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.server.hostHttpPort}:80"
|
||||||
|
];
|
||||||
|
environmentFiles = [ config.sops.templates."seafile.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/server/shared:/shared"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.server.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.seafile.mariadb.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.seafile.redis.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
"${common.backend}-${cfg.mariadb.name}.service"
|
||||||
|
"${common.backend}-${cfg.redis.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${cfg.targetName}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/server/shared/seafile/conf
|
||||||
|
|
||||||
|
cp -f ${config.sops.templates."seahub_settings.py".path} \
|
||||||
|
${hostDataPath}/server/shared/seafile/conf/seahub_settings.py
|
||||||
|
cp -f ${config.sops.templates."seafdav.conf".path} \
|
||||||
|
${hostDataPath}/server/shared/seafile/conf/seafdav.conf
|
||||||
|
cp -f ${config.sops.templates."seafile.conf".path} \
|
||||||
|
${hostDataPath}/server/shared/seafile/conf/seafile.conf
|
||||||
|
chmod 644 ${hostDataPath}/server/shared/seafile/conf/{seafile.conf,seafdav.conf,seahub_settings.py}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.netbird;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.templates."traefik.env".content = ''
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_EMAIL=${config.sops.placeholder."letsEncrypt/email"}
|
||||||
|
'';
|
||||||
|
|
||||||
|
sops.templates."traefik-static-config.yaml".content = ''
|
||||||
|
entryPoints:
|
||||||
|
web:
|
||||||
|
address: ":80"
|
||||||
|
http:
|
||||||
|
redirections:
|
||||||
|
entrypoint:
|
||||||
|
to: websecure
|
||||||
|
scheme: https
|
||||||
|
websecure:
|
||||||
|
address: ":443"
|
||||||
|
allowACMEByPass: true
|
||||||
|
transport:
|
||||||
|
respondingTimeouts:
|
||||||
|
readTimeout: 0
|
||||||
|
writeTimeout: 0
|
||||||
|
idleTimeout: 0
|
||||||
|
proxyProtocol:
|
||||||
|
trustedIPs:
|
||||||
|
- "${common.network.address}/${common.network.mask}"
|
||||||
|
|
||||||
|
serversTransport:
|
||||||
|
forwardingTimeouts:
|
||||||
|
responseHeaderTimeout: 0
|
||||||
|
idleConnTimeout: 0
|
||||||
|
|
||||||
|
providers:
|
||||||
|
docker:
|
||||||
|
exposedByDefault: false
|
||||||
|
network: "${common.network.name}"
|
||||||
|
file:
|
||||||
|
filename: /etc/traefik/dynamic.yaml
|
||||||
|
watch: true
|
||||||
|
|
||||||
|
certificatesResolvers:
|
||||||
|
letsencrypt:
|
||||||
|
acme:
|
||||||
|
storage: /letsencrypt/acme.json
|
||||||
|
tlsChallenge: true
|
||||||
|
'';
|
||||||
|
|
||||||
|
sops.templates."traefik-dynamic-config.yaml".content = ''
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
netbird-dashboard:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
rule: "Host(`${cfg.subdomain}.${config.sops.placeholder."domains/serv"}`)"
|
||||||
|
service: dashboard
|
||||||
|
priority: 1
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
netbird-backend:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
rule: "Host(`${cfg.subdomain}.${config.sops.placeholder."domains/serv"}`) && (PathPrefix(`/relay`) || PathPrefix(`/ws-proxy/`) || PathPrefix(`/api`) || PathPrefix(`/oauth2`))"
|
||||||
|
service: netbird-server
|
||||||
|
priority: 100
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
netbird-grpc:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
rule: "Host(`${cfg.subdomain}.${config.sops.placeholder."domains/serv"}`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`))"
|
||||||
|
service: netbird-server-h2c
|
||||||
|
priority: 100
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
services:
|
||||||
|
dashboard:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://${cfg.dashboard.name}:80"
|
||||||
|
netbird-server:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://${cfg.server.name}:80"
|
||||||
|
netbird-server-h2c:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "h2c://${cfg.server.name}:80"
|
||||||
|
tcp:
|
||||||
|
serversTransports:
|
||||||
|
pp-v2:
|
||||||
|
proxyProtocol:
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
routers:
|
||||||
|
proxy-passthrough:
|
||||||
|
entrypoints:
|
||||||
|
- websecure
|
||||||
|
rule: HostSNI(`*`)
|
||||||
|
service: proxy-tls
|
||||||
|
priority: 1
|
||||||
|
tls:
|
||||||
|
passthrough: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
proxy-tls:
|
||||||
|
loadBalancer:
|
||||||
|
serversTransport: pp-v2@file
|
||||||
|
servers:
|
||||||
|
- address: "${cfg.proxy.name}:8443"
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.traefik;
|
||||||
|
hostDataPath = "${common.dataDir}/traefik";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.traefik = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
ip = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
sops.secrets = {
|
||||||
|
"domains/serv" = { };
|
||||||
|
"letsEncrypt/email" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.oci-containers.containers.${cfg.name} = {
|
||||||
|
image = "traefik:latest";
|
||||||
|
environmentFiles = [ config.sops.templates."traefik.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
|
"${config.sops.templates."traefik-static-config.yaml".path}:/etc/traefik/traefik.yaml:ro"
|
||||||
|
"${config.sops.templates."traefik-dynamic-config.yaml".path}:/etc/traefik/dynamic.yaml:ro"
|
||||||
|
"${hostDataPath}/letsencrypt:/letsencrypt:rw"
|
||||||
|
];
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:80:80"
|
||||||
|
"0.0.0.0:443:443"
|
||||||
|
];
|
||||||
|
cmd = [
|
||||||
|
"--log.level=INFO"
|
||||||
|
"--accesslog=true"
|
||||||
|
"--configfile=/etc/traefik/traefik.yaml"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--ip=172.30.0.10"
|
||||||
|
"--network-alias=${cfg.name}"
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/letsencrypt
|
||||||
|
chown -R ${common.user.uid}:${common.group.gid} ${hostDataPath}
|
||||||
|
chmod -R 0600 ${hostDataPath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted;
|
||||||
|
domain = "${config.sops.placeholder."domains/serv"}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"vaultwarden/admin_token" = { };
|
||||||
|
"vaultwarden/client_id" = { };
|
||||||
|
"vaultwarden/client_secret" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."vaultwarden.env".content = ''
|
||||||
|
DOMAIN=https://${cfg.vaultwarden.subdomain}.${domain}
|
||||||
|
SIGNUPS_ALLOWED=false
|
||||||
|
ADMIN_TOKEN=${config.sops.placeholder."vaultwarden/admin_token"}
|
||||||
|
|
||||||
|
# SSO
|
||||||
|
SSO_ENABLED=true
|
||||||
|
SSO_ONLY=false
|
||||||
|
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION=true
|
||||||
|
SSO_AUTHORITY=https://${cfg.pocket-id.subdomain}.${domain}
|
||||||
|
SSO_CLIENT_ID=${config.sops.placeholder."vaultwarden/client_id"}
|
||||||
|
SSO_CLIENT_SECRET=${config.sops.placeholder."vaultwarden/client_secret"}
|
||||||
|
SSO_AUTH_ONLY_NOT_SESSION=true
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.vaultwarden;
|
||||||
|
hostDataPath = "${common.dataDir}/vaultwarden";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.vaultwarden = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers."${cfg.name}" = {
|
||||||
|
image = "vaultwarden/server:latest";
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.hostPort}:80"
|
||||||
|
];
|
||||||
|
user = "${common.user.uid}:${common.group.gid}";
|
||||||
|
environmentFiles = [ config.sops.templates."vaultwarden.env".path ];
|
||||||
|
volumes = [
|
||||||
|
"${hostDataPath}/data:/data"
|
||||||
|
];
|
||||||
|
log-driver = "journald";
|
||||||
|
extraOptions = [
|
||||||
|
"--network=${common.network.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/data
|
||||||
|
chown -R ${common.user.uid}:${common.group.gid} ${hostDataPath}
|
||||||
|
chmod -R 0775 ${hostDataPath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{ pkgs }:
|
||||||
|
let
|
||||||
|
configFiles = {
|
||||||
|
config = ''
|
||||||
|
FWTYPE=iptables
|
||||||
|
|
||||||
|
SET_MAXELEM=522288
|
||||||
|
|
||||||
|
IPSET_OPT="hashsize 262144 maxelem $SET_MAXELEM"
|
||||||
|
|
||||||
|
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
|
||||||
|
IP2NET_OPT6="--prefix-length=56-64 --v6-threshold=5"
|
||||||
|
|
||||||
|
AUTOHOSTLIST_INCOMING_MAXSEQ=4096
|
||||||
|
AUTOHOSTLIST_RETRANS_MAXSEQ=32768
|
||||||
|
AUTOHOSTLIST_RETRANS_RESET=1
|
||||||
|
AUTOHOSTLIST_RETRANS_THRESHOLD=3
|
||||||
|
AUTOHOSTLIST_FAIL_THRESHOLD=3
|
||||||
|
AUTOHOSTLIST_FAIL_TIME=60
|
||||||
|
AUTOHOSTLIST_UDP_IN=1
|
||||||
|
AUTOHOSTLIST_UDP_OUT=4
|
||||||
|
AUTOHOSTLIST_DEBUGLOG=0
|
||||||
|
|
||||||
|
MDIG_THREADS=30
|
||||||
|
|
||||||
|
GZIP_LISTS=1
|
||||||
|
|
||||||
|
DESYNC_MARK=0x40000000
|
||||||
|
DESYNC_MARK_POSTNAT=0x20000000
|
||||||
|
|
||||||
|
NFQWS2_ENABLE=1
|
||||||
|
NFQWS2_PORTS_TCP=80,443
|
||||||
|
NFQWS2_PORTS_UDP=443
|
||||||
|
NFQWS2_TCP_PKT_OUT=20
|
||||||
|
NFQWS2_TCP_PKT_IN=10
|
||||||
|
NFQWS2_UDP_PKT_OUT=5
|
||||||
|
NFQWS2_UDP_PKT_IN=3
|
||||||
|
NFQWS2_OPT="
|
||||||
|
--filter-tcp=80 --filter-l7=http --payload http_req --lua-desync=tcpseg:pos=0,midsld:ip_id=rnd:repeats=1 --new
|
||||||
|
--filter-tcp=443 --filter-l7=tls --payload=tls_client_hello --hostlist-domains=x.com --lua-desync=hostfakesplit:nofake1:midhost=midsld:ip_ttl=6:repeats=1 --new
|
||||||
|
--filter-tcp=443 --filter-l7=tls --payload=tls_client_hello --lua-desync=tcpseg:pos=0,midsld:ip_id=rnd:repeats=1 --new
|
||||||
|
--filter-udp=443 --filter-l7=quic --payload=quic_initial --lua-desync=tcpseg:pos=0,midsld:ip_id=rnd:repeats=1
|
||||||
|
"
|
||||||
|
|
||||||
|
MODE_FILTER=none
|
||||||
|
|
||||||
|
FLOWOFFLOAD=donttouch
|
||||||
|
|
||||||
|
INIT_APPLY_FW=1
|
||||||
|
|
||||||
|
DISABLE_IPV6=1
|
||||||
|
|
||||||
|
FILTER_TTL_EXPIRED_ICMP=1
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
pkgs.lib.mapAttrs (name: content: pkgs.writeText name content) configFiles
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.zapret2;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfhosted.zapret2 = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
socksPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
ssPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
socksPort = "${toString cfg.socksPort}";
|
||||||
|
ssPort = "${toString cfg.ssPort}";
|
||||||
|
configFiles = import ./config.nix { inherit pkgs; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
virtualisation.oci-containers.containers.${cfg.name} = {
|
||||||
|
image = "vernette/ss-zapret2:latest";
|
||||||
|
volumes = [
|
||||||
|
"${configFiles.config}:/opt/zapret2/config"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
SOCKS_PORT = socksPort;
|
||||||
|
SS_PORT = ssPort;
|
||||||
|
SS_PASSWORD = "test";
|
||||||
|
SS_ENCRYPT_METHOD = "chacha20-ietf-poly1305";
|
||||||
|
SS_TIMEOUT = "300";
|
||||||
|
};
|
||||||
|
ports = [
|
||||||
|
"127.0.0.1:${socksPort}:${socksPort}"
|
||||||
|
"127.0.0.1:${ssPort}:${ssPort}"
|
||||||
|
];
|
||||||
|
extraOptions = [
|
||||||
|
"--cap-add=NET_ADMIN"
|
||||||
|
"--network=host"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
postStart = ''
|
||||||
|
mv /opt/zapret2/lua.dist /opt/zapret2/lua || true
|
||||||
|
'';
|
||||||
|
after = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
requires = [ "${common.backend}-network-${common.network.name}.service" ];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.selfhosted;
|
||||||
|
domain = "${config.sops.placeholder."domains/serv"}";
|
||||||
|
zerobyteDomain = "${cfg.zerobyte.subdomain}.${domain}";
|
||||||
|
trustedOrigins = [
|
||||||
|
"https://${zerobyteDomain}"
|
||||||
|
"https://${cfg.pocket-id.subdomain}.${domain}"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
"zerobyte/app_secret" = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.templates."zerobyte.env".content = ''
|
||||||
|
TZ=${config.time.timeZone}
|
||||||
|
BASE_URL=https://${zerobyteDomain}
|
||||||
|
APP_SECRET=${config.sops.placeholder."zerobyte/app_secret"}
|
||||||
|
TRUST_PROXY=true
|
||||||
|
TRUSTED_ORIGINS=${(concatStringsSep "," trustedOrigins)}
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
common = config.selfhosted.common;
|
||||||
|
cfg = config.selfhosted.zerobyte;
|
||||||
|
hostDataPath = "${common.dataDir}/zerobyte";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./config.nix ];
|
||||||
|
|
||||||
|
options.selfhosted.zerobyte = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
hostPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers.${cfg.name} = {
|
||||||
|
image = "ghcr.io/nicotsx/zerobyte:latest";
|
||||||
|
environmentFiles = [ config.sops.templates."zerobyte.env".path ];
|
||||||
|
ports = [
|
||||||
|
"0.0.0.0:${toString cfg.hostPort}:4096"
|
||||||
|
];
|
||||||
|
volumes = [
|
||||||
|
"/etc/localtime:/etc/localtime:ro"
|
||||||
|
"${hostDataPath}/data:/var/lib/zerobyte"
|
||||||
|
"${hostDataPath}/backups:/mydata"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."${common.backend}-${cfg.name}" = {
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = lib.mkOverride 90 "always";
|
||||||
|
};
|
||||||
|
after = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"${common.backend}-network-${common.network.name}.service"
|
||||||
|
"${common.backend}-${config.selfhosted.traefik.name}.service"
|
||||||
|
];
|
||||||
|
partOf = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
wantedBy = [ "${common.backend}-${common.rootTarget.name}.target" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${hostDataPath}/{data,backups}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
{
|
||||||
|
sops.secrets = {
|
||||||
|
benq-password = {
|
||||||
|
neededForUsers = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users = {
|
||||||
|
mutableUsers = false;
|
||||||
|
users.benq = {
|
||||||
|
shell = pkgs.zsh;
|
||||||
|
isNormalUser = true;
|
||||||
|
ignoreShellProgramCheck = true;
|
||||||
|
hashedPasswordFile = config.sops.secrets.benq-password.path;
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeIuJzR68xA4ugJjtWbwvaWEU852Hg9FAAhXNw8ou43 benq"
|
||||||
|
];
|
||||||
|
linger = true;
|
||||||
|
subUidRanges = [
|
||||||
|
{
|
||||||
|
startUid = 100000;
|
||||||
|
count = 65536;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
subGidRanges = [
|
||||||
|
{
|
||||||
|
startGid = 100000;
|
||||||
|
count = 65536;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
extraGroups =
|
||||||
|
let
|
||||||
|
ifTheyExist = groups: filter (group: hasAttr group config.users.groups) groups;
|
||||||
|
in
|
||||||
|
flatten [
|
||||||
|
"wheel"
|
||||||
|
(ifTheyExist [
|
||||||
|
"docker"
|
||||||
|
"git"
|
||||||
|
"networkmanager"
|
||||||
|
"video"
|
||||||
|
])
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
boot.loader = {
|
||||||
|
systemd-boot = {
|
||||||
|
enable = true;
|
||||||
|
configurationLimit = 5;
|
||||||
|
};
|
||||||
|
efi.canTouchEfiVariables = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
imports = lib.flatten [
|
||||||
|
./boot.nix
|
||||||
|
./network.nix
|
||||||
|
./hardware-configuration.nix
|
||||||
|
(map lib.custom.relativeToRoot (
|
||||||
|
[
|
||||||
|
"hosts/common/core"
|
||||||
|
"hosts/common/users/benq.nix"
|
||||||
|
"home/benq/homelab.nix"
|
||||||
|
]
|
||||||
|
++ (map (f: "hosts/common/optional/${f}") [
|
||||||
|
"bbr.nix"
|
||||||
|
"endlessh.nix"
|
||||||
|
"fail2ban.nix"
|
||||||
|
"fonts.nix"
|
||||||
|
"openssh.nix"
|
||||||
|
"selfhosted"
|
||||||
|
])
|
||||||
|
))
|
||||||
|
];
|
||||||
|
|
||||||
|
disko.cfg.mainDevice = "/dev/disk/by-id/ata-AMD_R5M120G8_07092225C0040";
|
||||||
|
environment.shells = [ pkgs.zsh ];
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
modulesPath,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/installer/scan/not-detected.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [
|
||||||
|
"ahci"
|
||||||
|
"xhci_pci"
|
||||||
|
"nvme"
|
||||||
|
"usb_storage"
|
||||||
|
"usbhid"
|
||||||
|
"sd_mod"
|
||||||
|
];
|
||||||
|
boot.initrd.kernelModules = [ ];
|
||||||
|
boot.kernelModules = [ "kvm-amd" ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
# hdd
|
||||||
|
fileSystems."/mnt/data" = {
|
||||||
|
device = "/dev/disk/by-id/ata-WDC_WD20EFPX-68C4TN0_WD-WX32DA423Z41";
|
||||||
|
fsType = "ext4";
|
||||||
|
options = [
|
||||||
|
"noatime"
|
||||||
|
"nofail"
|
||||||
|
"x-systemd.device-timeout=5s"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
networking = {
|
||||||
|
hostName = "homelab";
|
||||||
|
firewall = {
|
||||||
|
allowedTCPPorts = [ 22 80 443 2222 6969 ];
|
||||||
|
allowedUDPPorts = [ 3478 7359 ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
boot.kernel.sysctl = {
|
||||||
|
"net.ipv4.ip_unprivileged_port_start" = 80;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
relativeToRoot = lib.path.append ../.;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user