From acc0ad68e00395d14f4d48b9a5e827abcc10f2ce Mon Sep 17 00:00:00 2001 From: Swarsel Date: Fri, 22 Dec 2023 01:15:04 +0100 Subject: [PATCH] Add several NixOS hosts on Proxmox and Oracle Cloud --- .sops.yaml | 49 + Emacs.org | 64 +- Nix.org | 1904 +++++++++++++++-- flake.lock | 39 + flake.nix | 98 + profiles/common/home.nix | 70 +- profiles/onett/nixos.nix | 6 + .../oracle/sync/hardware-configuration.nix | 36 + profiles/remote/oracle/sync/nixos.nix | 160 ++ profiles/server1/TEMPLATE/nixos.nix | 4 +- .../calibre/hardware-configuration.nix | 29 + profiles/server1/calibre/nixos.nix | 101 + .../jellyfin/hardware-configuration.nix | 34 + profiles/server1/jellyfin/nixos.nix | 77 + .../server1/matrix/hardware-configuration.nix | 29 + profiles/server1/matrix/nixos.nix | 296 +++ .../server1/nginx/hardware-configuration.nix | 29 + profiles/server1/nginx/nixos.nix | 243 +++ .../server1/sound/hardware-configuration.nix | 35 + profiles/server1/sound/nixos.nix | 132 ++ .../spotifyd/hardware-configuration.nix | 29 + profiles/server1/spotifyd/nixos.nix | 85 + .../transmission/hardware-configuration.nix | 29 + profiles/server1/transmission/nixos.nix | 358 ++++ programs/emacs/custom.el | 3 +- programs/emacs/init.el | 43 +- scripts/server1/doublepuppet.yaml | 21 + scripts/server1/iptables.sh | 47 + scripts/server1/routing.sh | 14 + scripts/server1/update-resolv-conf | 45 + secrets/calibre/secrets.yaml | 55 + secrets/certs/ca.rsa.2048.crt | 33 + secrets/certs/crl.rsa.2048.pem | 15 + secrets/general/secrets.yaml | 4 +- secrets/keys/calibre.pub | 1 + secrets/keys/nginx.pub | 1 + secrets/matrix/secrets.yaml | 56 + secrets/nginx/secrets.yaml | 54 + secrets/sound/secrets.yaml | 52 + secrets/spotifyd/secrets.yaml | 53 + secrets/sync/secrets.yaml | 53 + secrets/transmission/secrets.yaml | 57 + wallpaper/navidrome.png | Bin 0 -> 79385 bytes 43 files changed, 4356 insertions(+), 187 deletions(-) create mode 100644 profiles/remote/oracle/sync/hardware-configuration.nix create mode 100644 profiles/remote/oracle/sync/nixos.nix create mode 100644 profiles/server1/calibre/hardware-configuration.nix create mode 100644 profiles/server1/calibre/nixos.nix create mode 100644 profiles/server1/jellyfin/hardware-configuration.nix create mode 100644 profiles/server1/jellyfin/nixos.nix create mode 100644 profiles/server1/matrix/hardware-configuration.nix create mode 100644 profiles/server1/matrix/nixos.nix create mode 100644 profiles/server1/nginx/hardware-configuration.nix create mode 100644 profiles/server1/nginx/nixos.nix create mode 100644 profiles/server1/sound/hardware-configuration.nix create mode 100644 profiles/server1/sound/nixos.nix create mode 100644 profiles/server1/spotifyd/hardware-configuration.nix create mode 100644 profiles/server1/spotifyd/nixos.nix create mode 100644 profiles/server1/transmission/hardware-configuration.nix create mode 100644 profiles/server1/transmission/nixos.nix create mode 100644 scripts/server1/doublepuppet.yaml create mode 100644 scripts/server1/iptables.sh create mode 100644 scripts/server1/routing.sh create mode 100644 scripts/server1/update-resolv-conf create mode 100644 secrets/calibre/secrets.yaml create mode 100644 secrets/certs/ca.rsa.2048.crt create mode 100644 secrets/certs/crl.rsa.2048.pem create mode 100644 secrets/keys/calibre.pub create mode 100644 secrets/keys/nginx.pub create mode 100644 secrets/matrix/secrets.yaml create mode 100644 secrets/nginx/secrets.yaml create mode 100644 secrets/sound/secrets.yaml create mode 100644 secrets/spotifyd/secrets.yaml create mode 100644 secrets/sync/secrets.yaml create mode 100644 secrets/transmission/secrets.yaml create mode 100644 wallpaper/navidrome.png diff --git a/.sops.yaml b/.sops.yaml index f2f2d57..7504f48 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -7,6 +7,13 @@ keys: - &server_nixos age1zdjm8qa5t25mca0xxhhkpuh85mgg4l267mqjj2pdttksq7zg4unqdmqyp4 - &server_surface age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg - &server_stand age1hkajkcje5xvg8jd4zj2e0s9tndpv36hwhn7p38x9lyq2z8g7v45q2nhlej + - &server_nginx age1zyts3egct4he229klgrfkd9r442xw9r3qg3hyydh44pvk3wjhd3s2zjqvt + - &server_calibre age1q2k4j9m6ge6dgygehulzd8vqjcdgv5s7s4zrferaq29qlu94a4uqpv76s5 + - &server_transmiss age1wevwwytv5q8wx8yttc85gly678hn4k3qe4csgnq2frf3wxes63jqlt8kqs + - &server_matrix age1t2uj8arq8nnmd5s3h32p7z7masj2gqe5ec49dtr8ex2nlgef3yfqtgcnj6 + - &server_spotifyd age16d6wulu4vzuawvsnqv0cqjhxdz9e20qm3xdnzq2lp7787srl8shqsqlfps + - &server_sound age1w7tfe7k0r0hm6mzz0kmz8302kfn0rlh96w7g6zwqd4muqg7u9anqv07745 + - &server_sync age1glge4e97vgqzh332mqs5990vteezu2m8k4wq3z35jk0q8czw3gks2d7a3h creation_rules: - path_regex: secrets/general/[^/]+\.(yaml|json|env|ini)$ key_groups: @@ -22,3 +29,45 @@ creation_rules: - *admin_swarsel age: - *server_surface + - path_regex: secrets/nginx/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: + - *admin_swarsel + age: + - *server_nginx + - path_regex: secrets/calibre/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: + - *admin_swarsel + age: + - *server_calibre + - path_regex: secrets/transmission/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: + - *admin_swarsel + age: + - *server_transmiss + - path_regex: secrets/matrix/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: + - *admin_swarsel + age: + - *server_matrix + - path_regex: secrets/spotifyd/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: + - *admin_swarsel + age: + - *server_spotifyd + - path_regex: secrets/sound/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: + - *admin_swarsel + age: + - *server_sound + - path_regex: secrets/sync/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: + - *admin_swarsel + age: + - *server_sync diff --git a/Emacs.org b/Emacs.org index 52704e9..94fb388 100644 --- a/Emacs.org +++ b/Emacs.org @@ -412,6 +412,7 @@ Base emacs undo logic is very useful, but not easy to understand. I prefer undo- "C-c d" 'duplicate-line ; duplicate line on CURSOR "C-M-j" 'consult-buffer "C-s" 'consult-line + "C-" 'my-python-shell-run ) #+end_src @@ -1068,18 +1069,19 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+begin_src emacs-lisp - (use-package auctex) - (setq TeX-auto-save t) - (setq TeX-save-query nil) - (setq TeX-parse-self t) - (setq-default TeX-master nil) + ;; (use-package auctex + ;; :ensure nil) + (setq TeX-auto-save t) + (setq TeX-save-query nil) + (setq TeX-parse-self t) + (setq-default TeX-master nil) - ;; (add-hook 'LaTeX-mode-hook 'visual-line-mode) - ;; (add-hook 'LaTeX-mode-hook 'flyspell-mode) - ;; (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode) + ;; (add-hook 'LaTeX-mode-hook 'visual-line-mode) + ;; (add-hook 'LaTeX-mode-hook 'flyspell-mode) + ;; (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode) - ;; (add-hook 'LaTeX-mode-hook 'turn-on-reftex) - ;; (setq reftex-plug-into-AUCTeX t) + ;; (add-hook 'LaTeX-mode-hook 'turn-on-reftex) + ;; (setq reftex-plug-into-AUCTeX t) #+end_src @@ -1503,12 +1505,7 @@ NOTE: Make sure to configure a GitHub token before using this package! (use-package git-timemachine :hook (git-time-machine-mode . evil-normalize-keymaps) :init (setq git-timemachine-show-minibuffer-details t) - :general - (general-nmap "SPC g t" 'git-timemachine-toggle) - (git-timemachine-mode-map - "C-k" 'git-timemachine-show-previous-revision - "C-j" 'git-timemachine-show-next-revision - "q" 'git-timemachine-quit)) + ) #+end_src @@ -1733,12 +1730,35 @@ Currently unused #+begin_src emacs-lisp - ;; run the python inferior shell immediately upon entering a python buffer - (add-hook 'python-mode-hook 'swarsel/run-python) + ;; run the python inferior shell immediately upon entering a python buffer + ;; (add-hook 'python-mode-hook 'swarsel/run-python) + + ;; (defun swarsel/run-python () + ;; (save-selected-window + ;; (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command)))))) + + ;; reload python shell automatically + (defun my-python-shell-run () + (interactive) + (when (get-buffer-process "*Python*") + (set-process-query-on-exit-flag (get-buffer-process "*Python*") nil) + (kill-process (get-buffer-process "*Python*")) + ;; Uncomment If you want to clean the buffer too. + ;;(kill-buffer "*Python*") + ;; Not so fast! + (sleep-for 0.5)) + (run-python (python-shell-parse-command) nil nil) + (python-shell-send-buffer) + ;; Pop new window only if shell isnt visible + ;; in any frame. + (unless (get-buffer-window "*Python*" t) + (python-shell-switch-to-shell))) + + (defun my-python-shell-run-region () + (interactive) + (python-shell-send-region (region-beginning) (region-end)) + (python-shell-switch-to-shell)) -(defun swarsel/run-python () - (save-selected-window - (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command)))))) #+end_src *** CUDA @@ -2337,7 +2357,7 @@ The following block is 100% stolen from Dominik :P :config ;; This is set to 't' to avoid mail syncing issues when using mbsync - (setq send-mail-function 'smtpmail-send-it) + (setq send-mail-function 'sendmail-send-it) (setq mu4e-change-filenames-when-moving t) (setq mu4e-mu-binary (executable-find "mu")) diff --git a/Nix.org b/Nix.org index 974da1a..caa3a11 100644 --- a/Nix.org +++ b/Nix.org @@ -149,6 +149,51 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src +*** Virtual hosts init + +#+begin_src nix :noweb-ref vminitbare + + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + nix.settings.experimental-features = ["nix-command" "flakes"]; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.useDHCP = true; + networking.enableIPv6 = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + listenAddresses = [{ + port = 22; + addr = "0.0.0.0"; + }]; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + +#+end_src + +#+begin_src nix :noweb yes :noweb-ref vminit + + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + <> + +#+end_src + ** flake.nix *** Inputs & Inputs@Outputs @@ -164,6 +209,8 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann stylix, sops-nix, lanzaboote, + pia, + nixpkgs-mautrix-signal, #+end_src @@ -213,6 +260,17 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann inputs.nixpkgs.follows = "nixpkgs"; }; + pia = { + url = "git+https://git.sr.ht/~rprospero/nixos-pia?ref=development"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # provides expressions for mautrix-signal + nixpkgs-mautrix-signal ={ + url = github:niklaskorz/nixpkgs/nixos-23.11-mautrix-signal; + }; + + #+end_src *** let @@ -223,9 +281,24 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann overlays = [ emacs-overlay.overlay nur.overlay nixgl.overlay + # (self: super: { + # airsonic = super.airsonic.overrideAttrs (_: rec { + # version = "11.0.2-kagemomiji"; + # name = "airsonic-advanced-${version}"; + # src = super.fetchurl { + # url = "https://github.com/kagemomiji/airsonic-advanced/releases/download/11.0.2/airsonic.war"; + # sha256 = "PgErtEizHraZgoWHs5jYJJ5NsliDd9VulQfS64ackFo="; + # }; + # }); + # }) ]; config.allowUnfree = true; }; + + pkgsmautrix = import nixpkgs-mautrix-signal { inherit system; + config.allowUnfree = true; + }; + # NixOS modules that can only be used on NixOS systems nixModules = [ stylix.nixosModules.stylix ./profiles/common/nixos.nix @@ -244,58 +317,128 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+begin_src nix :noweb-ref flakenixosconf - onett = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs pkgs; }; - modules = nixModules ++ [ - ./profiles/onett/nixos.nix - home-manager.nixosModules.home-manager - { - home-manager.users.swarsel.imports = mixedModules ++ [ - ./profiles/onett/home.nix - ]; - } - ]; - }; + onett = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = nixModules ++ [ + ./profiles/onett/nixos.nix + home-manager.nixosModules.home-manager + { + home-manager.users.swarsel.imports = mixedModules ++ [ + ./profiles/onett/home.nix + ]; + } + ]; + }; - twoson = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs pkgs; }; - modules = nixModules ++ [ - ./profiles/twoson/nixos.nix - home-manager.nixosModules.home-manager - { - home-manager.users.swarsel.imports = mixedModules ++ [ - ./profiles/twoson/home.nix - ]; - } - ]; - }; + twoson = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = nixModules ++ [ + ./profiles/twoson/nixos.nix + home-manager.nixosModules.home-manager + { + home-manager.users.swarsel.imports = mixedModules ++ [ + ./profiles/twoson/home.nix + ]; + } + ]; + }; - stand = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs pkgs; }; - modules = nixModules ++ [ - ./profiles/stand/nixos.nix - home-manager.nixosModules.home-manager - { - home-manager.users.homelen.imports = mixedModules ++ [ - ./profiles/stand/home.nix - ]; - } - ]; - }; + stand = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = nixModules ++ [ + ./profiles/stand/nixos.nix + home-manager.nixosModules.home-manager + { + home-manager.users.homelen.imports = mixedModules ++ [ + ./profiles/stand/home.nix + ]; + } + ]; + }; - threed = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs pkgs; }; - modules = nixModules ++ [ - lanzaboote.nixosModules.lanzaboote - ./profiles/threed/nixos.nix - home-manager.nixosModules.home-manager - { - home-manager.users.swarsel.imports = mixedModules ++ [ - ./profiles/threed/home.nix - ]; - } - ]; - }; + threed = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = nixModules ++ [ + lanzaboote.nixosModules.lanzaboote + ./profiles/threed/nixos.nix + home-manager.nixosModules.home-manager + { + home-manager.users.swarsel.imports = mixedModules ++ [ + ./profiles/threed/home.nix + ]; + } + ]; + }; + + nginx = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/nginx/nixos.nix + ]; + }; + + calibre = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/calibre/nixos.nix + ]; + }; + + jellyfin = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + # sops-nix.nixosModules.sops + ./profiles/server1/jellyfin/nixos.nix + ]; + }; + + transmission = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + pia.nixosModule + ./profiles/server1/transmission/nixos.nix + ]; + }; + + matrix = nixpkgs.lib.nixosSystem { + # specialArgs = {inherit pkgsmautrix; }; + pkgs = pkgsmautrix; + # this is to import a service module that is not on nixpkgs + # this way avoids infinite recursion errors + specialArgs.unstable = nixpkgs-mautrix-signal; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/matrix/nixos.nix + ]; + }; + + sound = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/sound/nixos.nix + ]; + }; + + spotifyd = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/spotifyd/nixos.nix + ]; + }; + + #ovm + sync = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/remote/oracle/sync/nixos.nix + ]; + }; #+end_src *** homeConfigurations @@ -337,58 +480,6 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src -* flake.nix - -This tangles the flake.nix file; This block only needs to be touched when updating the general structure of the flake. For everything else, see the respective noweb-ref block. - -#+begin_src nix :noweb yes :tangle flake.nix - - { - description = "SwarseFlake - Nix Flake for all SwarselSystems"; - - inputs = { - <> - }; - - outputs = inputs@{ - self, - <> - ... - }: let - <> - in { - - # NixOS setups - run home-manager as a NixOS module for better compatibility - # another benefit - full rebuild on nixos-rebuild switch - # run rebuild using `nswitch` - - # NEW HOSTS: For a new host, decide whether a NixOS (nixosConfigurations) or non-NixOS (homeConfigurations) is used. - # Make sure to move hardware-configuration to the appropriate location, by default it is found in /etc/nixos/. - - nixosConfigurations = { - <> - }; - - # pure Home Manager setups - for non-NixOS machines - # run rebuild using `hmswitch` - - homeConfigurations = { - <> - }; - - nixOnDroidConfigurations = { - <> - }; - - packages.x86_64-linux = { - <> - }; - - }; - } - -#+end_src - * TODO System specific configuration This section mainly exists house different `configuration.nix` files for system level configurations of NixOS systems as well as `home.nix` for user level configurations on all systems. @@ -722,6 +813,7 @@ My laptop, sadly soon to be replaced by a new one, since most basic functions ar xserver.videoDrivers = ["nvidia"]; }; + hardware = { nvidia = { modesetting.enable = true; @@ -749,6 +841,7 @@ My laptop, sadly soon to be replaced by a new one, since most basic functions ar }; networking.hostName = "onett"; # Define your hostname. + networking.enableIPv6 = false; users.users.swarsel = { isNormalUser = true; @@ -759,6 +852,10 @@ My laptop, sadly soon to be replaced by a new one, since most basic functions ar system.stateVersion = "23.05"; # Did you read the comment? + environment.systemPackages = with pkgs; [ + ]; + + } #+end_src @@ -1268,6 +1365,11 @@ My home PC, the most powerful machine. Sadly Sway cannot make good use out of it #+end_src ** Virtual hosts + +My server setup is built on Proxmox VE; back when I started, I created all kinds of wild Debian/Ubuntu/etc. KVMs and LXCs on there. However, the root disk has suffered a weird failure where it has become unable to be cloned, but it is still functional for now. I am currently rewriting all machines on there to use NixOS instead; this is a ongoing process. + +In the long run, I am thinking about a transition to kubernetes or using just a server running NixOS and using the built-in container functionality. For now however, I like the network management provided by Proxmox, as I am a bit intimidated by doing that from scratch. + *** TEMPLATE **** NixOS @@ -1291,8 +1393,10 @@ My home PC, the most powerful machine. Sadly Sway cannot make good use out of it xkbVariant = "altgr-intl"; }; + nix.settings.experimental-features = ["nix-command" "flakes"]; + proxmoxLXC.manageNetwork = true; # manage network myself - proxmoxLXC.manageHostName = true; # manage hostname myself + proxmoxLXC.manageHostName = false; # manage hostname myself networking.hostName = "TEMPLATE"; # Define your hostname. networking.useDHCP = true; networking.enableIPv6 = false; @@ -1311,6 +1415,1453 @@ My home PC, the most powerful machine. Sadly Sway cannot make good use out of it #+end_src +*** NGINX +**** NixOS + +#+begin_src nix :tangle profiles/server1/nginx/nixos.nix + + { config, pkgs, modulesPath, ... }: + { + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + lego + nginx + ]; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/nginx/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.dnstokenfull = {owner="acme";}; + sops.templates."certs.secret".content = '' + CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull} + ''; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "nginx"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + # users.users.root.password = "TEMPLATE"; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + security.acme = { + acceptTerms = true; + preliminarySelfsigned = false; + defaults.email = "mrswarsel@gmail.com"; + defaults.dnsProvider = "cloudflare"; + defaults.environmentFile = "${config.sops.templates."certs.secret".path}"; + }; + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + virtualHosts = { + + "stash.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "https://192.168.2.5"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + # "/push/" = { + # proxyPass = "http://192.168.2.5:7867"; + # }; + "/.well-known/carddav" = { + return = "301 $scheme://$host/remote.php/dav"; + }; + "/.well-known/caldav" = { + return = "301 $scheme://$host/remote.php/dav"; + }; + }; + }; + + "matrix2.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "~ ^(/_matrix|/_synapse/client)" = { + proxyPass = "http://192.168.2.23:8008"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + # "sound.swarsel.win" = { + # enableACME = true; + # forceSSL = true; + # acmeRoot = null; + # locations = { + # "/" = { + # proxyPass = "https://192.168.2.13"; + # extraConfig = '' + # client_max_body_size 0; + # ''; + # }; + # }; + # }; + + # "sound.swarsel.win" = { + # enableACME = true; + # forceSSL = true; + # acmeRoot = null; + # locations = { + # "/" = { + # proxyPass = "http://192.168.2.13:4040"; + # recommendedProxySettings = false; + # # proxyWebsockets = true; + # extraConfig = '' + # proxy_set_header Upgrade $http_upgrade; + # proxy_set_header Connection "Upgrade"; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto https; + # proxy_set_header X-Forwarded-Host $host; + # proxy_set_header X-Forwarded-Port $server_port; + # proxy_set_header Host $host; + # proxy_max_temp_file_size 0; + # proxy_redirect http:// https://; + # proxy_buffering off; + # proxy_request_buffering off; + # client_max_body_size 0; + # ''; + # }; + # }; + # }; + + "sound.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://192.168.2.13:4040"; + proxyWebsockets = true; + extraConfig = '' + proxy_redirect http:// https://; + proxy_read_timeout 600s; + proxy_send_timeout 600s; + proxy_buffering off; + proxy_request_buffering off; + client_max_body_size 0; + ''; + }; + }; + }; + + "screen.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://192.168.2.16:8096"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "matrix.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "~ ^(/_matrix|/_synapse/client)" = { + proxyPass = "http://192.168.2.20:8008"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "scroll.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://192.168.2.22:8080"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + # "books.swarsel.win" = { + # enableACME = true; + # forceSSL = true; + # acmeRoot = null; + # locations = { + # "/" = { + # proxyPass = "http://192.168.2.22:8083"; + # extraConfig = '' + # client_max_body_size 0; + # ''; + # }; + # }; + # }; + + "blog.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "https://192.168.2.7"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + }; + }; + + + + + + } + +#+end_src + +*** [Manual steps required] Calibre + +This machine requires manual setup: +1) Set up calibre-web: + - Create metadata.db with 664 permissions, make sure parent directory is writeable + - Login @ books.swarsel.win using initial creds: + - user: admin + - pw: admin123 + - point to metadata.db file, make sure you can upload + - Change pw, create normal user +2) Setup kavita: + - Login @ scrolls.swarsel.win + - Create admin user + - Import Libraries + - Create normal user + +In general, I am not amazed by this setup; Kavita is the reader of choice, calibre-web mostly is there to have a convenient way to fullfill the opinionated folder structure when uploading ebooks (calibre-web does not work on its own since it forces sqlite which does not work nicely with my NFS book store). I hope that in the future Kavita will implement ebook upload, or that calibre-web will ditch the sqlite constraints. + +**** NixOS + +#+begin_src nix :tangle profiles/server1/calibre/nixos.nix + + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + calibre + ]; + + users.groups.lxc_shares = { + gid = 10000; + members = [ + "kavita" + "calibre-web" + "root" + ]; + }; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/calibre/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.kavita = { owner = "kavita";}; + # sops.secrets.smbuser = { }; + # sops.secrets.smbpassword = { }; + # sops.secrets.smbdomain = { }; + # sops.templates."smb.cred".content = '' + # user=${config.sops.placeholder.smbuser} + # password=${config.sops.placeholder.smbpassword} + # domain=${config.sops.placeholder.smbdomain} + # ''; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "calibre"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + + # services.calibre-server = { + # enable = true; + # user = "calibre-server"; + # auth.enable = true; + # auth.userDb = "/srv/calibre/users.sqlite"; + # libraries = [ + # /media/Books/main + # /media/Books/diverse + # /media/Books/language + # /media/Books/science + # /media/Books/sport + # /media/Books/novels + # ]; + # }; + + # services.calibre-web = { + # enable = true; + # user = "calibre-web"; + # group = "calibre-web"; + # listen.port = 8083; + # listen.ip = "0.0.0.0"; + # options = { + # enableBookUploading = true; + # enableKepubify = true; + # enableBookConversion = true; + # }; + # }; + + services.kavita = { + enable = true; + user = "kavita"; + port = 8080; + tokenKeyFile = config.sops.secrets.kavita.path; + }; + + + } + +#+end_src + +*** Jellyfin +**** NixOS + +#+begin_src nix :tangle profiles/server1/jellyfin/nixos.nix + + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + ]; + + users.groups.lxc_shares = { + gid = 10000; + members = [ + "jellyfin" + "root" + ]; + }; + + users.users.jellyfin = { + extraGroups = [ "video" "render" ]; + }; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + # sops.defaultSopsFile = "/.dotfiles/secrets/jellyfin/secrets.yaml"; + # sops.validateSopsFiles = false; + + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "jellyfin"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + nixpkgs.config.packageOverrides = pkgs: { + vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; + }; + hardware.opengl = { + enable = true; + extraPackages = with pkgs; [ + intel-media-driver # LIBVA_DRIVER_NAME=iHD + vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium) + vaapiVdpau + libvdpau-va-gl + ]; + }; + + services.jellyfin = { + enable = true; + user = "jellyfin"; + # openFirewall = true; # this works only for the default ports + }; + + } + +#+end_src + +*** [WIP/Incomplete/Untested] Transmission + +This stuff just does not work, I seem to be unable to create a working VPN Split Tunneling on NixOS. Maybe this is introduced by the wonky Proxmox-NixOS container interaction, I am not sure. For now, this machine does not work at all and I am stuck with my Debian Container that does this for me ... + +**** NixOS + +#+begin_src nix :tangle profiles/server1/transmission/nixos.nix + + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + # ./openvpn.nix #this file holds the vpn login data + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + openvpn + jq + iptables + busybox + wireguard-tools + ]; + + users.groups.lxc_shares = { + gid = 10000; + members = [ + "vpn" + "radarr" + "sonarr" + "lidarr" + "readarr" + "root" + ]; + }; + users.groups.vpn = {}; + + users.users.vpn = { + isNormalUser = true; + group = "vpn"; + home = "/home/vpn"; + }; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/transmission/secrets.yaml"; + sops.validateSopsFiles = false; + + boot.kernelModules = [ "tun" ]; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "transmission"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + + services.radarr = { + enable = true; + }; + + services.readarr = { + enable = true; + }; + services.sonarr = { + enable = true; + }; + services.lidarr = { + enable = true; + }; + services.prowlarr = { + enable = true; + }; + + # networking.interfaces = { + # lo = { + # useDHCP = false; + # ipv4.addresses = [ + # { address = "127.0.0.1"; prefixLength = 8; } + # ]; + # }; + # + # eth0 = { + # useDHCP = true; + # }; + # }; + + # networking.firewall.extraCommands = '' + # sudo iptables -A OUTPUT ! -o lo -m owner --uid-owner vpn -j DROP + # ''; + networking.iproute2 = { + enable = true; + rttablesExtraConfig = '' + 200 vpn + ''; + }; + # boot.kernel.sysctl = { + # "net.ipv4.conf.all.rp_filter" = 2; + # "net.ipv4.conf.default.rp_filter" = 2; + # "net.ipv4.conf.eth0.rp_filter" = 2; + # }; + environment.etc = { + "openvpn/iptables.sh" = + { source = ../../../scripts/server1/iptables.sh; + mode = "0755"; + }; + "openvpn/update-resolv-conf" = + { source = ../../../scripts/server1/update-resolv-conf; + mode = "0755"; + }; + "openvpn/routing.sh" = + { source = ../../../scripts/server1/routing.sh; + mode = "0755"; + }; + "openvpn/ca.rsa.2048.crt" = + { source = ../../../secrets/certs/ca.rsa.2048.crt; + mode = "0644"; + }; + "openvpn/crl.rsa.2048.pem" = + { source = ../../../secrets/certs/crl.rsa.2048.pem; + mode = "0644"; + }; + }; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + listenAddresses = [{ + port = 22; + addr = "0.0.0.0"; + }]; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + # users.users.root.password = "TEMPLATE"; + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + sops.secrets.vpnuser = {}; + sops.secrets.rpcuser = {owner="vpn";}; + sops.secrets.vpnpass = {}; + sops.secrets.rpcpass = {owner="vpn";}; + sops.secrets.vpnprot = {}; + sops.secrets.vpnloc = {}; + # sops.secrets.crlpem = {}; + # sops.secrets.capem = {}; + sops.templates."transmission-rpc".owner = "vpn"; + sops.templates."transmission-rpc".content = builtins.toJSON { + rpc-username = config.sops.placeholder.rpcuser; + rpc-password = config.sops.placeholder.rpcpass; + }; + + sops.templates.pia.content = '' + ${config.sops.placeholder.vpnuser} + ${config.sops.placeholder.vpnpass} + ''; + + sops.templates.vpn.content = '' + client + dev tun + proto ${config.sops.placeholder.vpnprot} + remote ${config.sops.placeholder.vpnloc} + resolv-retry infinite + nobind + persist-key + persist-tun + cipher aes-128-cbc + auth sha1 + tls-client + remote-cert-tls server + + auth-user-pass ${config.sops.templates.pia.path} + compress + verb 1 + reneg-sec 0 + + crl-verify /etc/openvpn/crl.rsa.2048.pem + ca /etc/openvpn/ca.rsa.2048.crt + + disable-occ + dhcp-option DNS 209.222.18.222 + dhcp-option DNS 209.222.18.218 + dhcp-option DNS 8.8.8.8 + route-noexec + ''; + + # services.pia.enable = true; + # services.pia.authUserPass.username = "na"; + # services.pia.authUserPass.password = "na"; + + + # systemd.services.openvpn-vpn = { + # wantedBy = [ "multi-user.target" ]; + # after = [ "network.target" ]; + # description = "OpenVPN connection to pia"; + # serviceConfig = { + # Type = "forking"; + # RuntimeDirectory="openvpn"; + # PrivateTmp=true; + # KillMode="mixed"; + # ExecStart = ''@${pkgs.openvpn}/sbin/openvpn openvpn --daemon ovpn-pia --status /run/openvpn/pia.status 10 --cd /etc/openvpn --script-security 2 --config ${config.sops.templates.vpn.path} --writepid /run/openvpn/pia.pid''; + # PIDFile=''/run/openvpn/pia.pid''; + # ExecReload=''/run/current-system/sw/bin/kill -HUP $MAINPID''; + # WorkingDirectory="/etc/openvpn"; + # Restart="on-failure"; + # RestartSec=30; + # ProtectSystem="yes"; + # DeviceAllow=["/dev/null rw" "/dev/net/tun rw"]; + # }; + # }; + services.openvpn.servers = { + pia = { + autoStart = false; + updateResolvConf = true; + # up = '' + # export INTERFACE="tun0" + # export VPNUSER="vpn" + # export LOCALIP="192.168.1.191" + # export NETIF="eth0" + # export VPNIF="tun0" + # export GATEWAYIP=$(ifconfig $VPNIF | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' | egrep -v '255|(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})' | tail -n1) + # iptables -F -t nat + # iptables -F -t mangle + # iptables -F -t filter + # iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark + # iptables -t mangle -A OUTPUT ! --dest $LOCALIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 + # iptables -t mangle -A OUTPUT --dest $LOCALIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 + # iptables -t mangle -A OUTPUT --dest $LOCALIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 + # iptables -t mangle -A OUTPUT ! --src $LOCALIP -j MARK --set-mark 0x1 + # iptables -t mangle -A OUTPUT -j CONNMARK --save-mark + # iptables -A INPUT -i $INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + # iptables -A INPUT -i $INTERFACE -j REJECT + # iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT + # iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT + # iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE + # iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + # iptables -A OUTPUT ! --src $LOCALIP -o $NETIF -j REJECT + # if [[ `ip rule list | grep -c 0x1` == 0 ]]; then + # ip rule add from all fwmark 0x1 lookup $VPNUSER + # fi + # ip route replace default via $GATEWAYIP table $VPNUSER + # ip route append default via 127.0.0.1 dev lo table $VPNUSER + # ip route flush cache + # ''; + # down = "bash /etc/openvpn/update-resolv-conf"; + # these are outsourced to a local file, I am not sure if it can be done with sops-nix + # authUserPass = { + # username = "TODO:secrets"; + # password = "TODO:secrets"; + # }; + config = "config ${config.sops.templates.vpn.path}"; + }; + }; + + services.transmission = { + enable = true; + credentialsFile = config.sops.templates."transmission-rpc".path; + user = "vpn"; + group = "lxc_shares"; + settings = { + + alt-speed-down= 8000; + alt-speed-enabled= false; + alt-speed-time-begin= 0; + alt-speed-time-day= 127; + alt-speed-time-enabled= true; + alt-speed-time-end= 360; + alt-speed-up= 2000; + bind-address-ipv4= "0.0.0.0"; + bind-address-ipv6= "::"; + blocklist-enabled= false; + blocklist-url= "http://www.example.com/blocklist"; + cache-size-mb= 4; + dht-enabled= false; + download-dir= "/media/Eternor/New"; + download-limit= 100; + download-limit-enabled= 0; + download-queue-enabled= true; + download-queue-size= 5; + encryption= 2; + idle-seeding-limit= 30; + idle-seeding-limit-enabled= false; + incomplete-dir= "/var/lib/transmission-daemon/Downloads"; + incomplete-dir-enabled= false; + lpd-enabled= false; + max-peers-global= 200; + message-level= 1; + peer-congestion-algorithm= ""; + peer-id-ttl-hours= 6; + peer-limit-global= 100; + peer-limit-per-torrent= 40; + peer-port= 22371; + peer-port-random-high= 65535; + peer-port-random-low= 49152; + peer-port-random-on-start= false; + peer-socket-tos= "default"; + pex-enabled= false; + port-forwarding-enabled= false; + preallocation= 1; + prefetch-enabled= true; + queue-stalled-enabled= true; + queue-stalled-minutes= 30; + ratio-limit= 2; + ratio-limit-enabled= false; + rename-partial-files= true; + rpc-authentication-required= true; + rpc-bind-address= "0.0.0.0"; + rpc-enabled= true; + rpc-host-whitelist= ""; + rpc-host-whitelist-enabled= true; + rpc-port= 9091; + rpc-url= "/transmission/"; + rpc-whitelist= "127.0.0.1,192.168.3.2"; + rpc-whitelist-enabled= true; + scrape-paused-torrents-enabled= true; + script-torrent-done-enabled= false; + seed-queue-enabled= false; + seed-queue-size= 10; + speed-limit-down= 6000; + speed-limit-down-enabled= true; + speed-limit-up= 500; + speed-limit-up-enabled= true; + start-added-torrents= true; + trash-original-torrent-files= false; + umask= 2; + upload-limit= 100; + upload-limit-enabled= 0; + upload-slots-per-torrent= 14; + utp-enabled= false; + }; + }; + + # services.nginx = { + # enable = true; + # virtualHosts = { + + # "192.168.1.192" = { + # locations = { + # "/transmission" = { + # proxyPass = "http://127.0.0.1:9091"; + # extraConfig = '' + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # ''; + # }; + # }; + # }; + # }; + # }; + + + } + +#+end_src + +*** [Manual steps needed] Matrix + +1) After the initial setup, run the + - /run/secrets-generated/matrix_user_register.sh +command to register a new admin user. +2) All bridges will fail on first start, copy the registration files using: + - cp /var/lib/mautrix-telegram/telegram-registration.yaml /var/lib/matrix-synapse/ + - chown matrix-synapse:matrix-synapse var/lib/matrix-synapse/telegram-registration.yaml +Make sure to also do this for doublepuppet.yaml +3) Restart postgresql.service, matrix-synapse.service, mautrix-whatsapp.service, mautrix-telegram.service + +**** NixOS + +#+begin_src nix :noweb yes :tangle profiles/server1/matrix/nixos.nix + + { config, pkgs, modulesPath, unstable, sops, ... }: let + matrixDomain = "matrix2.swarsel.win"; + in { + <> + + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + # we import here a service that is not available yet on normal nixpkgs + # this module is hence not in the modules list, we add it ourselves + (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix") + ]; + + networking.hostName = "matrix"; # Define your hostname. + networking.firewall.enable = false; + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + matrix-synapse + lottieconverter + ffmpeg + ]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/matrix/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.matrixsharedsecret = {owner="matrix-synapse";}; + sops.templates."matrix_user_register.sh".content = '' + register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008 + ''; + sops.templates.matrixshared.owner = "matrix-synapse"; + sops.templates.matrixshared.content = '' + registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret} + ''; + sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";}; + sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";}; + sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";}; + sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";}; + sops.templates.mautrixtelegram.owner = "matrix-synapse"; + sops.templates.mautrixtelegram.content = '' + MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as} + MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs} + MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id} + MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash} + ''; + # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";}; + # sops.templates.mautrixwhatsapp.owner = "matrix-synapse"; + # sops.templates.mautrixwhatsapp.content = '' + # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared} + # ''; + + services.postgresql.enable = true; + services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" '' + CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; + CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram'; + CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp'; + CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal'; + CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + ''; + + services.matrix-synapse = { + settings.app_service_config_files = [ + "/var/lib/matrix-synapse/telegram-registration.yaml" + "/var/lib/matrix-synapse/whatsapp-registration.yaml" + "/var/lib/matrix-synapse/signal-registration.yaml" + "/var/lib/matrix-synapse/doublepuppet.yaml" + ]; + enable = true; + settings.server_name = matrixDomain; + settings.public_baseurl = "https://${matrixDomain}"; + extraConfigFiles = [ + config.sops.templates.matrixshared.path + ]; + settings.listeners = [ + { port = 8008; + bind_addresses = [ "0.0.0.0" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = [ "client" "federation" ]; + compress = true; + } + ]; + } + ]; + }; + + services.mautrix-telegram = { + enable = true; + environmentFile = config.sops.templates.mautrixtelegram.path; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = matrixDomain; + }; + appservice = { + address= "http://localhost:29317"; + hostname = "0.0.0.0"; + port = "29317"; + provisioning.enabled = true; + id = "telegram"; + # ephemeral_events = true; # not needed due to double puppeting + public = { + enabled = false; + }; + database = "postgresql:///mautrix-telegram?host=/run/postgresql"; + }; + bridge = { + # login_shared_secret_map = { + # matrixDomain = "as_token:doublepuppet"; + # }; + relaybot.authless_portals = true; + allow_avatar_remove = true; + allow_contact_info = true; + sync_channel_members = true; + startup_sync = true; + sync_create_limit = 0; + sync_direct_chats = true; + telegram_link_preview = true; + permissions = { + "*" = "relaybot"; + "@swarsel:${matrixDomain}" = "admin"; + }; + # Animated stickers conversion requires additional packages in the + # service's path. + # If this isn't a fresh installation, clearing the bridge's uploaded + # file cache might be necessary (make a database backup first!): + # delete from telegram_file where \ + # mime_type in ('application/gzip', 'application/octet-stream') + animated_sticker = { + target = "gif"; + args = { + width = 256; + height = 256; + fps = 30; # only for webm + background = "020202"; # only for gif, transparency not supported + }; + }; + }; + }; + }; + systemd.services.mautrix-telegram.path = with pkgs; [ + lottieconverter # for animated stickers conversion, unfree package + ffmpeg # if converting animated stickers to webm (very slow!) + ]; + + services.mautrix-whatsapp = { + enable = true; + # environmentFile = config.sops.templates.mautrixwhatsapp.path; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = matrixDomain; + }; + appservice = { + address= "http://localhost:29318"; + hostname = "0.0.0.0"; + port = 29318; + database = { + type = "postgres"; + uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql"; + }; + }; + bridge = { + displayname_template = "{{or .FullName .PushName .JID}} (WA)"; + history_sync = { + backfill = true; + max_initial_conversations = -1; + message_count = -1; + request_full_sync = true; + full_sync_config = { + days_limit = 900; + size_mb_limit = 5000; + storage_quota_mb = 5000; + }; + }; + login_shared_secret_map = { + matrixDomain = "as_token:doublepuppet"; + }; + sync_manual_marked_unread = true; + send_presence_on_typing = true; + parallel_member_sync = true; + url_previews = true; + caption_in_message = true; + extev_polls = true; + permissions = { + "*" = "relaybot"; + "@swarsel:${matrixDomain}" = "admin"; + }; + }; + }; + }; + + services.mautrix-signal = { + enable = true; + # environmentFile = config.sops.templates.mautrixwhatsapp.path; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = matrixDomain; + }; + appservice = { + + address= "http://localhost:29328"; + hostname = "0.0.0.0"; + port = 29328; + database = { + type = "postgres"; + uri = "postgresql:///mautrix-signal?host=/run/postgresql"; + }; + }; + bridge = { + displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)"; + login_shared_secret_map = { + matrixDomain = "as_token:doublepuppet"; + }; + caption_in_message = true; + permissions = { + "*" = "relaybot"; + "@swarsel:${matrixDomain}" = "admin"; + }; + }; + }; + }; + + # restart the bridges daily. this is done for the signal bridge mainly which stops carrying + # messages out after a while. + + systemd.timers."restart-bridges" = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "1d"; + OnUnitActiveSec = "1d"; + Unit = "restart-bridges.service"; + }; + }; + + systemd.services."restart-bridges" = { + script = '' + systemctl restart mautrix-whatsapp.service + systemctl restart mautrix-signal.service + systemctl restart mautrix-telegram.service + ''; + serviceConfig = { + Type = "oneshot"; + User = "root"; + }; + }; + + } + +#+end_src + +*** Sound +**** NixOS + +#+begin_src nix :noweb yes :tangle profiles/server1/sound/nixos.nix + + { config, pkgs, modulesPath, ... }: + + { + <> + + proxmoxLXC.privileged = true; # manage hostname myself + + users.groups.lxc_pshares = { + gid = 110000; + members = [ + "navidrome" + "mpd" + "root" + ]; + }; + + users.groups.navidrome = { + gid = 61593; + }; + + users.groups.mpd = {}; + + users.users.navidrome = { + isSystemUser = true; + uid = 61593; + group = "navidrome"; + extraGroups = [ "audio" "utmp" ]; + }; + + users.users.mpd = { + isSystemUser = true; + group = "mpd"; + extraGroups = [ "audio" "utmp" ]; + }; + + sound = { + enable = true; + }; + + hardware.enableAllFirmware = true; + networking.hostName = "sound"; # Define your hostname. + networking.firewall.enable = false; + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + pciutils + alsa-utils + mpv + ]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/sound/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.mpdpass = { owner = "mpd";}; + + services.navidrome = { + enable = true; + settings = { + Address = "0.0.0.0"; + Port = 4040; + MusicFolder = "/media"; + EnableSharing = true; + EnableTranscodingConfig = true; + Scanner.GroupAlbumReleases = true; + ScanSchedule = "@every 1d"; + # Insert these values locally as sops-nix does not work for them + LastFM.ApiKey = TEMPLATE; + LastFM.Secret = TEMPLATE; + Spotify.ID = TEMPLATE; + Spotify.Secret = TEMPLATE; + UILoginBackgroundUrl = "https://i.imgur.com/OMLxi7l.png"; + UIWelcomeMessage = "~SwarselSound~"; + }; + }; + services.mpd = { + enable = true; + musicDirectory = "/media"; + user = "mpd"; + group = "mpd"; + network = { + port = 3254; + listenAddress = "any"; + }; + credentials = [ + { + passwordFile = config.sops.secrets.mpdpass.path; + permissions = [ + "read" + "add" + "control" + "admin" + ]; + } + ]; + }; + } +#+end_src + +*** Spotifyd +**** NixOS + +#+begin_src nix :noweb yes :tangle profiles/server1/spotifyd/nixos.nix + + { config, pkgs, modulesPath, ... }: + + { + <> + + proxmoxLXC.privileged = true; # manage hostname myself + + users.groups.spotifyd = { + gid = 65136; + }; + + users.users.spotifyd = { + isSystemUser = true; + uid = 65136; + group = "spotifyd"; + extraGroups = [ "audio" "utmp" ]; + }; + + sound = { + enable = true; + }; + + hardware.enableAllFirmware = true; + networking.hostName = "spotifyd"; # Define your hostname. + networking.firewall.enable = false; + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + ]; + + # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + # sops.defaultSopsFile = "/.dotfiles/secrets/spotifyd/secrets.yaml"; + # sops.validateSopsFiles = false; + + services.spotifyd = { + enable = true; + settings = { + global = { + dbus_type = "session"; + use_mpris = false; + device = "default:CARD=PCH"; + device_name = "SwarselSpot"; + mixer = "alsa"; + zeroconf_port = 1025; + }; + }; + }; + + } + +#+end_src + +*** Sync +**** NixOS + +#+begin_src nix :tangle profiles/remote/oracle/sync/nixos.nix + + { config, pkgs, modulesPath, ... }: + + { + imports = [ + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + ]; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/root/.dotfiles/secrets/sync/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.swarsel = { owner = "root";}; + sops.secrets.dnstokenfull = {owner="acme";}; + sops.templates."certs.secret".content = '' + CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull} + ''; + + security.acme = { + acceptTerms = true; + preliminarySelfsigned = false; + defaults.email = "mrswarsel@gmail.com"; + defaults.dnsProvider = "cloudflare"; + defaults.environmentFile = "${config.sops.templates."certs.secret".path}"; + }; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + virtualHosts = { + + "synki.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://localhost:27701"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "sync.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://localhost:8384/"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "git.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://localhost:3000"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + }; + }; + + boot.tmp.cleanOnBoot = true; + zramSwap.enable = false; + networking.hostName = "sync"; + networking.enableIPv6 = false; + networking.domain = "subnet03112148.vcn03112148.oraclevcn.com"; + networking.firewall.extraCommands = '' + iptables -I INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 27701 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 8384 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 3000 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 22000 -j ACCEPT + iptables -I INPUT -m state --state NEW -p udp --dport 22000 -j ACCEPT + iptables -I INPUT -m state --state NEW -p udp --dport 21027 -j ACCEPT + ''; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.11"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + boot.loader.grub.device = "nodev"; + + services.anki-sync-server = { + enable = true; + port = 27701; + address = "0.0.0.0"; + openFirewall = true; + users = [ + { + username = "Swarsel"; + passwordFile = config.sops.secrets.swarsel.path; + } + ]; + }; + + services.syncthing = { + enable = true; + guiAddress = "0.0.0.0:8384"; + openDefaultPorts = true; + }; + + services.forgejo = { + enable = true; + settings = { + DEFAULT = { + APP_NAME = "~SwaGit~"; + }; + server = { + PROTOCOL = "http"; + HTTP_PORT = 3000; + HTTP_ADDR = "0.0.0.0"; + DOMAIN = "git.swarsel.win"; + ROOT_URL = "https://git.swarsel.win"; + }; + service = { + DISABLE_REGISTRATION = true; + SHOW_REGISTRATION_BUTTON = false; + }; + }; + }; + + } + +#+end_src + * Common NixOS These are system-level settings specific to NixOS machines. All settings that are required on all machines go here. @@ -1590,6 +3141,12 @@ Also, I define some useful shell scripts here. { home.packages = with pkgs; [ + # audio stuff + spek # spectrum analyzer + losslessaudiochecker + ffmpeg_5-full + flac + # "big" programs filebot gimp @@ -1795,6 +3352,35 @@ Also, I define some useful shell scripts here. #+end_src +** sops + +I use sops-nix to handle secrets that I want to have available on my machines at all times. Procedure to add a new machine: +- `ssh-keygen -t ed25519 -C "NAME sops"` in .ssh directory (or wherever) - name e.g. "sops" +- cat ~/.ssh/sops.pub | ssh-to-age | wl-copy +- add the output to .sops.yaml +- cp ~/.ssh/sops.pub ~/.dotfiles/secrets/keys/NAME.pub +- update entry for sops.age.sshKeyPaths + +#+begin_src nix :tangle profiles/common/home.nix + + sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml"; + sops.validateSopsFiles = false; + + # sops.age.keyFile = "${config.home.homeDirectory}/.ssh/key.txt"; + # This will generate a new key if the key specified above does not exist + # sops.age.generateKey = true; + + # sops.gnupg.home = "/home/swarsel/.dotfiles/secrets/keys"; + # since we are using the home-manager implementation, we need to specify the runtime path for each secret + sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";}; + sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";}; + sops.secrets.leon = {path = "/run/user/1000/secrets/leon";}; + sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";}; + # sops.secrets.leon = { }; + # sops.secrets.nautilus = { }; + # sops.secrets.mrswarsel = { }; +#+end_src + ** SSH Machines It is very convenient to have SSH aliases in place for machines that I use. This is mainly used for some server machines and some university clusters. @@ -1810,6 +3396,11 @@ It is very convenient to have SSH aliases in place for machines that I use. This port = 22; user = "root"; }; + "jellyfin" = { + hostname = "192.168.2.16"; + port = 22; + user = "root"; + }; "pfsense" = { hostname = "192.168.1.1"; port = 22; @@ -1825,6 +3416,11 @@ It is very convenient to have SSH aliases in place for machines that I use. This port = 22; user = "root"; }; + "fetcher" = { + hostname = "192.168.1.192"; + port = 22; + user = "root"; + }; "omv" = { hostname = "192.168.1.3"; port = 22; @@ -1850,6 +3446,11 @@ It is very convenient to have SSH aliases in place for machines that I use. This port = 22; user = "root"; }; + "spotify" = { + hostname = "192.168.1.17"; + port = 22; + user = "root"; + }; "wordpress" = { hostname = "192.168.2.7"; port = 22; @@ -1870,11 +3471,26 @@ It is very convenient to have SSH aliases in place for machines that I use. This port = 22; user = "root"; }; + "matrix2" = { + hostname = "192.168.2.20"; + port = 22; + user = "root"; + }; "database" = { hostname = "192.168.2.21"; port = 22; user = "root"; }; + "minecraft" = { + hostname = "130.61.119.129"; + port = 22; + user = "opc"; + }; + "sync" = { + hostname = "193.122.53.173"; + port = 22; + user = "root"; #this is a oracle vm server but needs root due to nixos-infect + }; "pkv" = { hostname = "46.232.248.161"; port = 22; @@ -1941,35 +3557,6 @@ It is very convenient to have SSH aliases in place for machines that I use. This #+end_src -** sops - -I use sops-nix to handle secrets that I want to have available on my machines at all times. Procedure to add a new machine: -- `ssh-keygen -t ed25519 -C "NAME sops"` in .ssh directory (or wherever) - name e.g. "sops" -- cat ~/.ssh/sops.pub | ssh-to-age | wl-copy -- add the output to .sops.yaml -- cp ~/.ssh/sops.pub ~/.dotfiles/secrets/keys/NAME.pub -- update entry for sops.age.sshKeyPaths - -#+begin_src nix :tangle profiles/common/home.nix - - sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml"; - sops.validateSopsFiles = false; - - # sops.age.keyFile = "${config.home.homeDirectory}/.ssh/key.txt"; - # This will generate a new key if the key specified above does not exist - # sops.age.generateKey = true; - - # sops.gnupg.home = "/home/swarsel/.dotfiles/secrets/keys"; - # since we are using the home-manager implementation, we need to specify the runtime path for each secret - sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";}; - sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";}; - sops.secrets.leon = {path = "/run/user/1000/secrets/leon";}; - sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";}; - # sops.secrets.leon = { }; - # sops.secrets.nautilus = { }; - # sops.secrets.mrswarsel = { }; -#+end_src - ** Fonts + Theme These section allows home-manager to allow theme settings, and handles some other appearance-related settings like cursor styles. Interestingly, system icons (adwaita) still need to be setup on system-level, and will break if defined here. @@ -3429,7 +5016,7 @@ I am currently using SwayFX, which adds some nice effects to sway, like rounded #+end_src -* TODO Manual tasks and Closing Parenthesis (this needs to be the last heading) +* TODO Manual tasks, flake.nix skeleton and Closing Parenthesis (this needs to be the last heading) Here are listed some tasks that I was not able to automate yet, these need to be done automatically for now. Also, this section exists to add an extra closing parenthesis to common.nix so that I do not need to think about this anymore if I ever decide to add more headings towards the end of this file ;) @@ -3439,6 +5026,7 @@ Here are listed some tasks that I was not able to automate yet, these need to be The last blocks exist to close the opening parenthesis of modules/common.nix (home-manager) and profiles/common.nix (NixOS): +** Closing parentheses for common/home.nix and common/nixos.nix #+begin_src nix :tangle profiles/common/home.nix @@ -3451,3 +5039,55 @@ The last blocks exist to close the opening parenthesis of modules/common.nix (ho } #+end_src + +** flake.nix + +This tangles the flake.nix file; This block only needs to be touched when updating the general structure of the flake. For everything else, see the respective noweb-ref block. + +#+begin_src nix :noweb yes :tangle flake.nix + + { + description = "SwarseFlake - Nix Flake for all SwarselSystems"; + + inputs = { + <> + }; + + outputs = inputs@{ + self, + <> + ... + }: let + <> + in { + + # NixOS setups - run home-manager as a NixOS module for better compatibility + # another benefit - full rebuild on nixos-rebuild switch + # run rebuild using `nswitch` + + # NEW HOSTS: For a new host, decide whether a NixOS (nixosConfigurations) or non-NixOS (homeConfigurations) is used. + # Make sure to move hardware-configuration to the appropriate location, by default it is found in /etc/nixos/. + + nixosConfigurations = { + <> + }; + + # pure Home Manager setups - for non-NixOS machines + # run rebuild using `hmswitch` + + homeConfigurations = { + <> + }; + + nixOnDroidConfigurations = { + <> + }; + + packages.x86_64-linux = { + <> + }; + + }; + } + +#+end_src diff --git a/flake.lock b/flake.lock index 5ad0b9d..c2d3241 100644 --- a/flake.lock +++ b/flake.lock @@ -537,6 +537,22 @@ "type": "github" } }, + "nixpkgs-mautrix-signal": { + "locked": { + "lastModified": 1703864075, + "narHash": "sha256-0TtwnLaBydIjpugK1kIIL18dRXZ9KaECfQmkJVBFEa0=", + "owner": "niklaskorz", + "repo": "nixpkgs", + "rev": "d5ba4fc361fbdd71300b190d4fdb82d3c9e46938", + "type": "github" + }, + "original": { + "owner": "niklaskorz", + "ref": "nixos-23.11-mautrix-signal", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-stable": { "locked": { "lastModified": 1701805708, @@ -711,6 +727,27 @@ "type": "github" } }, + "pia": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1687263775, + "narHash": "sha256-sSVNT3o+4RcdnUqdVloPc3UvM3LancTY6htyzAHrC0w=", + "ref": "development", + "rev": "190f8040cc4837fb6b3c43032711e1536ef2270b", + "revCount": 10, + "type": "git", + "url": "https://git.sr.ht/~rprospero/nixos-pia" + }, + "original": { + "ref": "development", + "type": "git", + "url": "https://git.sr.ht/~rprospero/nixos-pia" + } + }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": [ @@ -751,7 +788,9 @@ "nixgl": "nixgl", "nixos-generators": "nixos-generators", "nixpkgs": "nixpkgs_3", + "nixpkgs-mautrix-signal": "nixpkgs-mautrix-signal", "nur": "nur", + "pia": "pia", "sops-nix": "sops-nix", "stylix": "stylix" } diff --git a/flake.nix b/flake.nix index aa80ff6..0fff8ab 100644 --- a/flake.nix +++ b/flake.nix @@ -47,6 +47,17 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + pia = { + url = "git+https://git.sr.ht/~rprospero/nixos-pia?ref=development"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # provides expressions for mautrix-signal + nixpkgs-mautrix-signal ={ + url = github:niklaskorz/nixpkgs/nixos-23.11-mautrix-signal; + }; + + }; outputs = inputs@{ @@ -62,6 +73,8 @@ stylix, sops-nix, lanzaboote, + pia, + nixpkgs-mautrix-signal, ... }: let @@ -71,9 +84,24 @@ overlays = [ emacs-overlay.overlay nur.overlay nixgl.overlay + # (self: super: { + # airsonic = super.airsonic.overrideAttrs (_: rec { + # version = "11.0.2-kagemomiji"; + # name = "airsonic-advanced-${version}"; + # src = super.fetchurl { + # url = "https://github.com/kagemomiji/airsonic-advanced/releases/download/11.0.2/airsonic.war"; + # sha256 = "PgErtEizHraZgoWHs5jYJJ5NsliDd9VulQfS64ackFo="; + # }; + # }); + # }) ]; config.allowUnfree = true; }; + + pkgsmautrix = import nixpkgs-mautrix-signal { inherit system; + config.allowUnfree = true; + }; + # NixOS modules that can only be used on NixOS systems nixModules = [ stylix.nixosModules.stylix ./profiles/common/nixos.nix @@ -151,6 +179,76 @@ ]; }; + nginx = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/nginx/nixos.nix + ]; + }; + + calibre = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/calibre/nixos.nix + ]; + }; + + jellyfin = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + # sops-nix.nixosModules.sops + ./profiles/server1/jellyfin/nixos.nix + ]; + }; + + transmission = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + pia.nixosModule + ./profiles/server1/transmission/nixos.nix + ]; + }; + + matrix = nixpkgs.lib.nixosSystem { + # specialArgs = {inherit pkgsmautrix; }; + pkgs = pkgsmautrix; + # this is to import a service module that is not on nixpkgs + # this way avoids infinite recursion errors + specialArgs.unstable = nixpkgs-mautrix-signal; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/matrix/nixos.nix + ]; + }; + + sound = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/sound/nixos.nix + ]; + }; + + spotifyd = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/server1/spotifyd/nixos.nix + ]; + }; + + #ovm + sync = nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs pkgs; }; + modules = [ + sops-nix.nixosModules.sops + ./profiles/remote/oracle/sync/nixos.nix + ]; + }; + }; # pure Home Manager setups - for non-NixOS machines diff --git a/profiles/common/home.nix b/profiles/common/home.nix index e0dba4d..68148ff 100644 --- a/profiles/common/home.nix +++ b/profiles/common/home.nix @@ -3,6 +3,12 @@ { home.packages = with pkgs; [ + # audio stuff + spek # spectrum analyzer + losslessaudiochecker + ffmpeg_5-full + flac + # "big" programs filebot gimp @@ -205,6 +211,23 @@ # MIGHT NEED TO ENABLE THIS ON SURFACE!! +sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml"; +sops.validateSopsFiles = false; + +# sops.age.keyFile = "${config.home.homeDirectory}/.ssh/key.txt"; +# This will generate a new key if the key specified above does not exist +# sops.age.generateKey = true; + +# sops.gnupg.home = "/home/swarsel/.dotfiles/secrets/keys"; +# since we are using the home-manager implementation, we need to specify the runtime path for each secret +sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";}; +sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";}; +sops.secrets.leon = {path = "/run/user/1000/secrets/leon";}; +sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";}; +# sops.secrets.leon = { }; +# sops.secrets.nautilus = { }; +# sops.secrets.mrswarsel = { }; + programs.ssh= { enable = true; extraConfig = "SetEnv TERM=xterm-256color"; @@ -214,6 +237,11 @@ programs.ssh= { port = 22; user = "root"; }; + "jellyfin" = { + hostname = "192.168.2.16"; + port = 22; + user = "root"; + }; "pfsense" = { hostname = "192.168.1.1"; port = 22; @@ -229,6 +257,11 @@ programs.ssh= { port = 22; user = "root"; }; + "fetcher" = { + hostname = "192.168.1.192"; + port = 22; + user = "root"; + }; "omv" = { hostname = "192.168.1.3"; port = 22; @@ -254,6 +287,11 @@ programs.ssh= { port = 22; user = "root"; }; + "spotify" = { + hostname = "192.168.1.17"; + port = 22; + user = "root"; + }; "wordpress" = { hostname = "192.168.2.7"; port = 22; @@ -274,11 +312,26 @@ programs.ssh= { port = 22; user = "root"; }; + "matrix2" = { + hostname = "192.168.2.20"; + port = 22; + user = "root"; + }; "database" = { hostname = "192.168.2.21"; port = 22; user = "root"; }; + "minecraft" = { + hostname = "130.61.119.129"; + port = 22; + user = "opc"; + }; + "sync" = { + hostname = "193.122.53.173"; + port = 22; + user = "root"; #this is a oracle vm server but needs root due to nixos-infect + }; "pkv" = { hostname = "46.232.248.161"; port = 22; @@ -343,23 +396,6 @@ programs.ssh= { }; }; -sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml"; -sops.validateSopsFiles = false; - -# sops.age.keyFile = "${config.home.homeDirectory}/.ssh/key.txt"; -# This will generate a new key if the key specified above does not exist -# sops.age.generateKey = true; - -# sops.gnupg.home = "/home/swarsel/.dotfiles/secrets/keys"; -# since we are using the home-manager implementation, we need to specify the runtime path for each secret -sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";}; -sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";}; -sops.secrets.leon = {path = "/run/user/1000/secrets/leon";}; -sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";}; -# sops.secrets.leon = { }; -# sops.secrets.nautilus = { }; -# sops.secrets.mrswarsel = { }; - stylix.targets.emacs.enable = false; # fonts.fontconfig.enable = true; diff --git a/profiles/onett/nixos.nix b/profiles/onett/nixos.nix index 53db7a9..0b9c390 100644 --- a/profiles/onett/nixos.nix +++ b/profiles/onett/nixos.nix @@ -14,6 +14,7 @@ xserver.videoDrivers = ["nvidia"]; }; + hardware = { nvidia = { modesetting.enable = true; @@ -89,6 +90,7 @@ }; networking.hostName = "onett"; # Define your hostname. + networking.enableIPv6 = false; users.users.swarsel = { isNormalUser = true; @@ -99,4 +101,8 @@ system.stateVersion = "23.05"; # Did you read the comment? + environment.systemPackages = with pkgs; [ + ]; + + } diff --git a/profiles/remote/oracle/sync/hardware-configuration.nix b/profiles/remote/oracle/sync/hardware-configuration.nix new file mode 100644 index 0000000..9b86511 --- /dev/null +++ b/profiles/remote/oracle/sync/hardware-configuration.nix @@ -0,0 +1,36 @@ +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/4b47378a-02eb-4548-bab8-59cbf379252a"; + fsType = "xfs"; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/2B75-2AD5"; + fsType = "vfat"; + }; + + swapDevices = + [ { device = "/dev/disk/by-uuid/f0126a93-753e-4769-ada8-7499a1efb3a9"; } + ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.ens3.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/profiles/remote/oracle/sync/nixos.nix b/profiles/remote/oracle/sync/nixos.nix new file mode 100644 index 0000000..2034d6d --- /dev/null +++ b/profiles/remote/oracle/sync/nixos.nix @@ -0,0 +1,160 @@ +{ config, pkgs, modulesPath, ... }: + +{ + imports = [ + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + ]; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/root/.dotfiles/secrets/sync/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.swarsel = { owner = "root";}; + sops.secrets.dnstokenfull = {owner="acme";}; + sops.templates."certs.secret".content = '' + CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull} + ''; + + security.acme = { + acceptTerms = true; + preliminarySelfsigned = false; + defaults.email = "mrswarsel@gmail.com"; + defaults.dnsProvider = "cloudflare"; + defaults.environmentFile = "${config.sops.templates."certs.secret".path}"; + }; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + virtualHosts = { + + "synki.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://localhost:27701"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "sync.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://localhost:8384/"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "git.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://localhost:3000"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + }; + }; + + boot.tmp.cleanOnBoot = true; + zramSwap.enable = false; + networking.hostName = "sync"; + networking.enableIPv6 = false; + networking.domain = "subnet03112148.vcn03112148.oraclevcn.com"; + networking.firewall.extraCommands = '' + iptables -I INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 27701 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 8384 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 3000 -j ACCEPT + iptables -I INPUT -m state --state NEW -p tcp --dport 22000 -j ACCEPT + iptables -I INPUT -m state --state NEW -p udp --dport 22000 -j ACCEPT + iptables -I INPUT -m state --state NEW -p udp --dport 21027 -j ACCEPT + ''; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.11"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + boot.loader.grub.device = "nodev"; + + services.anki-sync-server = { + enable = true; + port = 27701; + address = "0.0.0.0"; + openFirewall = true; + users = [ + { + username = "Swarsel"; + passwordFile = config.sops.secrets.swarsel.path; + } + ]; + }; + + services.syncthing = { + enable = true; + guiAddress = "0.0.0.0:8384"; + openDefaultPorts = true; + }; + + services.forgejo = { + enable = true; + settings = { + DEFAULT = { + APP_NAME = "~SwaGit~"; + }; + server = { + PROTOCOL = "http"; + HTTP_PORT = 3000; + HTTP_ADDR = "0.0.0.0"; + DOMAIN = "git.swarsel.win"; + ROOT_URL = "https://git.swarsel.win"; + }; + service = { + DISABLE_REGISTRATION = true; + SHOW_REGISTRATION_BUTTON = false; + }; + }; + }; + +} diff --git a/profiles/server1/TEMPLATE/nixos.nix b/profiles/server1/TEMPLATE/nixos.nix index a0c8baa..8e8c222 100644 --- a/profiles/server1/TEMPLATE/nixos.nix +++ b/profiles/server1/TEMPLATE/nixos.nix @@ -16,8 +16,10 @@ xkbVariant = "altgr-intl"; }; + nix.settings.experimental-features = ["nix-command" "flakes"]; + proxmoxLXC.manageNetwork = true; # manage network myself - proxmoxLXC.manageHostName = true; # manage hostname myself + proxmoxLXC.manageHostName = false; # manage hostname myself networking.hostName = "TEMPLATE"; # Define your hostname. networking.useDHCP = true; networking.enableIPv6 = false; diff --git a/profiles/server1/calibre/hardware-configuration.nix b/profiles/server1/calibre/hardware-configuration.nix new file mode 100644 index 0000000..c572cde --- /dev/null +++ b/profiles/server1/calibre/hardware-configuration.nix @@ -0,0 +1,29 @@ +# 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 = [ ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "vfio_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/mapper/pve-vm--120--disk--0"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eth0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/profiles/server1/calibre/nixos.nix b/profiles/server1/calibre/nixos.nix new file mode 100644 index 0000000..ce6a460 --- /dev/null +++ b/profiles/server1/calibre/nixos.nix @@ -0,0 +1,101 @@ +{ config, pkgs, modulesPath, ... }: + +{ + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + calibre + ]; + + users.groups.lxc_shares = { + gid = 10000; + members = [ + "kavita" + "calibre-web" + "root" + ]; + }; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/calibre/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.kavita = { owner = "kavita";}; + # sops.secrets.smbuser = { }; + # sops.secrets.smbpassword = { }; + # sops.secrets.smbdomain = { }; + # sops.templates."smb.cred".content = '' + # user=${config.sops.placeholder.smbuser} + # password=${config.sops.placeholder.smbpassword} + # domain=${config.sops.placeholder.smbdomain} + # ''; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "calibre"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + + # services.calibre-server = { + # enable = true; + # user = "calibre-server"; + # auth.enable = true; + # auth.userDb = "/srv/calibre/users.sqlite"; + # libraries = [ + # /media/Books/main + # /media/Books/diverse + # /media/Books/language + # /media/Books/science + # /media/Books/sport + # /media/Books/novels + # ]; + # }; + + # services.calibre-web = { + # enable = true; + # user = "calibre-web"; + # group = "calibre-web"; + # listen.port = 8083; + # listen.ip = "0.0.0.0"; + # options = { + # enableBookUploading = true; + # enableKepubify = true; + # enableBookConversion = true; + # }; + # }; + + services.kavita = { + enable = true; + user = "kavita"; + port = 8080; + tokenKeyFile = config.sops.secrets.kavita.path; + }; + + +} diff --git a/profiles/server1/jellyfin/hardware-configuration.nix b/profiles/server1/jellyfin/hardware-configuration.nix new file mode 100644 index 0000000..d0d2466 --- /dev/null +++ b/profiles/server1/jellyfin/hardware-configuration.nix @@ -0,0 +1,34 @@ +# 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 = [ ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "vfio_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/mapper/pve-vm--121--disk--0"; + fsType = "ext4"; + }; + + fileSystems."/media/Videos" = + { device = "//192.168.1.3/Eternor"; + fsType = "cifs"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eth0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/profiles/server1/jellyfin/nixos.nix b/profiles/server1/jellyfin/nixos.nix new file mode 100644 index 0000000..36d209b --- /dev/null +++ b/profiles/server1/jellyfin/nixos.nix @@ -0,0 +1,77 @@ +{ config, pkgs, modulesPath, ... }: + +{ + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + ]; + + users.groups.lxc_shares = { + gid = 10000; + members = [ + "jellyfin" + "root" + ]; + }; + + users.users.jellyfin = { + extraGroups = [ "video" "render" ]; + }; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + # sops.defaultSopsFile = "/.dotfiles/secrets/jellyfin/secrets.yaml"; + # sops.validateSopsFiles = false; + + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "jellyfin"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + nixpkgs.config.packageOverrides = pkgs: { + vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; + }; + hardware.opengl = { + enable = true; + extraPackages = with pkgs; [ + intel-media-driver # LIBVA_DRIVER_NAME=iHD + vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium) + vaapiVdpau + libvdpau-va-gl + ]; + }; + + services.jellyfin = { + enable = true; + user = "jellyfin"; + # openFirewall = true; # this works only for the default ports + }; + +} diff --git a/profiles/server1/matrix/hardware-configuration.nix b/profiles/server1/matrix/hardware-configuration.nix new file mode 100644 index 0000000..0a30877 --- /dev/null +++ b/profiles/server1/matrix/hardware-configuration.nix @@ -0,0 +1,29 @@ +# 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 = [ ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "vfio_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/mapper/pve-vm--102--disk--0"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eth0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/profiles/server1/matrix/nixos.nix b/profiles/server1/matrix/nixos.nix new file mode 100644 index 0000000..a61833b --- /dev/null +++ b/profiles/server1/matrix/nixos.nix @@ -0,0 +1,296 @@ +{ config, pkgs, modulesPath, unstable, sops, ... }: let + matrixDomain = "matrix2.swarsel.win"; +in { + + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + nix.settings.experimental-features = ["nix-command" "flakes"]; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.useDHCP = true; + networking.enableIPv6 = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + listenAddresses = [{ + port = 22; + addr = "0.0.0.0"; + }]; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + # we import here a service that is not available yet on normal nixpkgs + # this module is hence not in the modules list, we add it ourselves + (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix") + ]; + + networking.hostName = "matrix"; # Define your hostname. + networking.firewall.enable = false; + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + matrix-synapse + lottieconverter + ffmpeg + ]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/matrix/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.matrixsharedsecret = {owner="matrix-synapse";}; + sops.templates."matrix_user_register.sh".content = '' + register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008 + ''; + sops.templates.matrixshared.owner = "matrix-synapse"; + sops.templates.matrixshared.content = '' + registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret} + ''; + sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";}; + sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";}; + sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";}; + sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";}; + sops.templates.mautrixtelegram.owner = "matrix-synapse"; + sops.templates.mautrixtelegram.content = '' + MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as} + MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs} + MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id} + MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash} + ''; + # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";}; + # sops.templates.mautrixwhatsapp.owner = "matrix-synapse"; + # sops.templates.mautrixwhatsapp.content = '' + # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared} + # ''; + + services.postgresql.enable = true; + services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" '' + CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; + CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram'; + CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp'; + CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal'; + CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + ''; + + services.matrix-synapse = { + settings.app_service_config_files = [ + "/var/lib/matrix-synapse/telegram-registration.yaml" + "/var/lib/matrix-synapse/whatsapp-registration.yaml" + "/var/lib/matrix-synapse/signal-registration.yaml" + "/var/lib/matrix-synapse/doublepuppet.yaml" + ]; + enable = true; + settings.server_name = matrixDomain; + settings.public_baseurl = "https://${matrixDomain}"; + extraConfigFiles = [ + config.sops.templates.matrixshared.path + ]; + settings.listeners = [ + { port = 8008; + bind_addresses = [ "0.0.0.0" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = [ "client" "federation" ]; + compress = true; + } + ]; + } + ]; + }; + + services.mautrix-telegram = { + enable = true; + environmentFile = config.sops.templates.mautrixtelegram.path; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = matrixDomain; + }; + appservice = { + address= "http://localhost:29317"; + hostname = "0.0.0.0"; + port = "29317"; + provisioning.enabled = true; + id = "telegram"; + # ephemeral_events = true; # not needed due to double puppeting + public = { + enabled = false; + }; + database = "postgresql:///mautrix-telegram?host=/run/postgresql"; + }; + bridge = { + # login_shared_secret_map = { + # matrixDomain = "as_token:doublepuppet"; + # }; + relaybot.authless_portals = true; + allow_avatar_remove = true; + allow_contact_info = true; + sync_channel_members = true; + startup_sync = true; + sync_create_limit = 0; + sync_direct_chats = true; + telegram_link_preview = true; + permissions = { + "*" = "relaybot"; + "@swarsel:${matrixDomain}" = "admin"; + }; + # Animated stickers conversion requires additional packages in the + # service's path. + # If this isn't a fresh installation, clearing the bridge's uploaded + # file cache might be necessary (make a database backup first!): + # delete from telegram_file where \ + # mime_type in ('application/gzip', 'application/octet-stream') + animated_sticker = { + target = "gif"; + args = { + width = 256; + height = 256; + fps = 30; # only for webm + background = "020202"; # only for gif, transparency not supported + }; + }; + }; + }; + }; + systemd.services.mautrix-telegram.path = with pkgs; [ + lottieconverter # for animated stickers conversion, unfree package + ffmpeg # if converting animated stickers to webm (very slow!) + ]; + + services.mautrix-whatsapp = { + enable = true; + # environmentFile = config.sops.templates.mautrixwhatsapp.path; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = matrixDomain; + }; + appservice = { + address= "http://localhost:29318"; + hostname = "0.0.0.0"; + port = 29318; + database = { + type = "postgres"; + uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql"; + }; + }; + bridge = { + displayname_template = "{{or .FullName .PushName .JID}} (WA)"; + history_sync = { + backfill = true; + max_initial_conversations = -1; + message_count = -1; + request_full_sync = true; + full_sync_config = { + days_limit = 900; + size_mb_limit = 5000; + storage_quota_mb = 5000; + }; + }; + login_shared_secret_map = { + matrixDomain = "as_token:doublepuppet"; + }; + sync_manual_marked_unread = true; + send_presence_on_typing = true; + parallel_member_sync = true; + url_previews = true; + caption_in_message = true; + extev_polls = true; + permissions = { + "*" = "relaybot"; + "@swarsel:${matrixDomain}" = "admin"; + }; + }; + }; + }; + + services.mautrix-signal = { + enable = true; + # environmentFile = config.sops.templates.mautrixwhatsapp.path; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = matrixDomain; + }; + appservice = { + + address= "http://localhost:29328"; + hostname = "0.0.0.0"; + port = 29328; + database = { + type = "postgres"; + uri = "postgresql:///mautrix-signal?host=/run/postgresql"; + }; + }; + bridge = { + displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)"; + login_shared_secret_map = { + matrixDomain = "as_token:doublepuppet"; + }; + caption_in_message = true; + permissions = { + "*" = "relaybot"; + "@swarsel:${matrixDomain}" = "admin"; + }; + }; + }; + }; + + # restart the bridges daily. this is done for the signal bridge mainly which stops carrying + # messages out after a while. + + systemd.timers."restart-bridges" = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "1d"; + OnUnitActiveSec = "1d"; + Unit = "restart-bridges.service"; + }; + }; + + systemd.services."restart-bridges" = { + script = '' + systemctl restart mautrix-whatsapp.service + systemctl restart mautrix-signal.service + systemctl restart mautrix-telegram.service + ''; + serviceConfig = { + Type = "oneshot"; + User = "root"; + }; + }; + +} diff --git a/profiles/server1/nginx/hardware-configuration.nix b/profiles/server1/nginx/hardware-configuration.nix new file mode 100644 index 0000000..9d2561f --- /dev/null +++ b/profiles/server1/nginx/hardware-configuration.nix @@ -0,0 +1,29 @@ +# 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 = [ ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "vfio_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/mapper/pve-vm--119--disk--0"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eth0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/profiles/server1/nginx/nixos.nix b/profiles/server1/nginx/nixos.nix new file mode 100644 index 0000000..72ed018 --- /dev/null +++ b/profiles/server1/nginx/nixos.nix @@ -0,0 +1,243 @@ +{ config, pkgs, modulesPath, ... }: +{ + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + lego + nginx + ]; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/nginx/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.dnstokenfull = {owner="acme";}; + sops.templates."certs.secret".content = '' + CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull} + ''; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "nginx"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + # users.users.root.password = "TEMPLATE"; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + security.acme = { + acceptTerms = true; + preliminarySelfsigned = false; + defaults.email = "mrswarsel@gmail.com"; + defaults.dnsProvider = "cloudflare"; + defaults.environmentFile = "${config.sops.templates."certs.secret".path}"; + }; + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + virtualHosts = { + + "stash.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "https://192.168.2.5"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + # "/push/" = { + # proxyPass = "http://192.168.2.5:7867"; + # }; + "/.well-known/carddav" = { + return = "301 $scheme://$host/remote.php/dav"; + }; + "/.well-known/caldav" = { + return = "301 $scheme://$host/remote.php/dav"; + }; + }; + }; + + "matrix2.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "~ ^(/_matrix|/_synapse/client)" = { + proxyPass = "http://192.168.2.23:8008"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + # "sound.swarsel.win" = { + # enableACME = true; + # forceSSL = true; + # acmeRoot = null; + # locations = { + # "/" = { + # proxyPass = "https://192.168.2.13"; + # extraConfig = '' + # client_max_body_size 0; + # ''; + # }; + # }; + # }; + + # "sound.swarsel.win" = { + # enableACME = true; + # forceSSL = true; + # acmeRoot = null; + # locations = { + # "/" = { + # proxyPass = "http://192.168.2.13:4040"; + # recommendedProxySettings = false; + # # proxyWebsockets = true; + # extraConfig = '' + # proxy_set_header Upgrade $http_upgrade; + # proxy_set_header Connection "Upgrade"; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto https; + # proxy_set_header X-Forwarded-Host $host; + # proxy_set_header X-Forwarded-Port $server_port; + # proxy_set_header Host $host; + # proxy_max_temp_file_size 0; + # proxy_redirect http:// https://; + # proxy_buffering off; + # proxy_request_buffering off; + # client_max_body_size 0; + # ''; + # }; + # }; + # }; + + "sound.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://192.168.2.13:4040"; + proxyWebsockets = true; + extraConfig = '' + proxy_redirect http:// https://; + proxy_read_timeout 600s; + proxy_send_timeout 600s; + proxy_buffering off; + proxy_request_buffering off; + client_max_body_size 0; + ''; + }; + }; + }; + + "screen.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://192.168.2.16:8096"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "matrix.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "~ ^(/_matrix|/_synapse/client)" = { + proxyPass = "http://192.168.2.20:8008"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + "scroll.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://192.168.2.22:8080"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + # "books.swarsel.win" = { + # enableACME = true; + # forceSSL = true; + # acmeRoot = null; + # locations = { + # "/" = { + # proxyPass = "http://192.168.2.22:8083"; + # extraConfig = '' + # client_max_body_size 0; + # ''; + # }; + # }; + # }; + + "blog.swarsel.win" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "https://192.168.2.7"; + extraConfig = '' + client_max_body_size 0; + ''; + }; + }; + }; + + }; + }; + + + + + +} diff --git a/profiles/server1/sound/hardware-configuration.nix b/profiles/server1/sound/hardware-configuration.nix new file mode 100644 index 0000000..2e6f899 --- /dev/null +++ b/profiles/server1/sound/hardware-configuration.nix @@ -0,0 +1,35 @@ +# 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 = [ ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "vfio_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/mnt/data/images/118/vm-118-disk-0.raw"; + fsType = "ext4"; + options = [ "loop" ]; + }; + + fileSystems."/media" = + { device = "//192.168.1.3/Eternor"; + fsType = "cifs"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eth0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/profiles/server1/sound/nixos.nix b/profiles/server1/sound/nixos.nix new file mode 100644 index 0000000..38664d2 --- /dev/null +++ b/profiles/server1/sound/nixos.nix @@ -0,0 +1,132 @@ +{ config, pkgs, modulesPath, ... }: + +{ + + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + nix.settings.experimental-features = ["nix-command" "flakes"]; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.useDHCP = true; + networking.enableIPv6 = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + listenAddresses = [{ + port = 22; + addr = "0.0.0.0"; + }]; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + + + proxmoxLXC.privileged = true; # manage hostname myself + + users.groups.lxc_pshares = { + gid = 110000; + members = [ + "navidrome" + "mpd" + "root" + ]; + }; + + users.groups.navidrome = { + gid = 61593; + }; + + users.groups.mpd = {}; + + users.users.navidrome = { + isSystemUser = true; + uid = 61593; + group = "navidrome"; + extraGroups = [ "audio" "utmp" ]; + }; + + users.users.mpd = { + isSystemUser = true; + group = "mpd"; + extraGroups = [ "audio" "utmp" ]; + }; + + sound = { + enable = true; + }; + + hardware.enableAllFirmware = true; + networking.hostName = "sound"; # Define your hostname. + networking.firewall.enable = false; + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + pciutils + alsa-utils + mpv + ]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/sound/secrets.yaml"; + sops.validateSopsFiles = false; + sops.secrets.mpdpass = { owner = "mpd";}; + + services.navidrome = { + enable = true; + settings = { + Address = "0.0.0.0"; + Port = 4040; + MusicFolder = "/media"; + EnableSharing = true; + EnableTranscodingConfig = true; + Scanner.GroupAlbumReleases = true; + ScanSchedule = "@every 1d"; + # Insert these values locally as sops-nix does not work for them + LastFM.ApiKey = TEMPLATE; + LastFM.Secret = TEMPLATE; + Spotify.ID = TEMPLATE; + Spotify.Secret = TEMPLATE; + UILoginBackgroundUrl = "https://i.imgur.com/OMLxi7l.png"; + UIWelcomeMessage = "~SwarselSound~"; + }; + }; + services.mpd = { + enable = true; + musicDirectory = "/media"; + user = "mpd"; + group = "mpd"; + network = { + port = 3254; + listenAddress = "any"; + }; + credentials = [ + { + passwordFile = config.sops.secrets.mpdpass.path; + permissions = [ + "read" + "add" + "control" + "admin" + ]; + } + ]; + }; +} diff --git a/profiles/server1/spotifyd/hardware-configuration.nix b/profiles/server1/spotifyd/hardware-configuration.nix new file mode 100644 index 0000000..438871d --- /dev/null +++ b/profiles/server1/spotifyd/hardware-configuration.nix @@ -0,0 +1,29 @@ +# 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 = [ ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "vfio_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/mapper/pve-vm--123--disk--0"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eth0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/profiles/server1/spotifyd/nixos.nix b/profiles/server1/spotifyd/nixos.nix new file mode 100644 index 0000000..7bf42ea --- /dev/null +++ b/profiles/server1/spotifyd/nixos.nix @@ -0,0 +1,85 @@ +{ config, pkgs, modulesPath, ... }: + +{ + + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + ]; + + + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + nix.settings.experimental-features = ["nix-command" "flakes"]; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.useDHCP = true; + networking.enableIPv6 = false; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + listenAddresses = [{ + port = 22; + addr = "0.0.0.0"; + }]; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + + + proxmoxLXC.privileged = true; # manage hostname myself + + users.groups.spotifyd = { + gid = 65136; + }; + + users.users.spotifyd = { + isSystemUser = true; + uid = 65136; + group = "spotifyd"; + extraGroups = [ "audio" "utmp" ]; + }; + + sound = { + enable = true; + }; + + hardware.enableAllFirmware = true; + networking.hostName = "spotifyd"; # Define your hostname. + networking.firewall.enable = false; + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + ]; + + # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + # sops.defaultSopsFile = "/.dotfiles/secrets/spotifyd/secrets.yaml"; + # sops.validateSopsFiles = false; + + services.spotifyd = { + enable = true; + settings = { + global = { + dbus_type = "session"; + use_mpris = false; + device = "default:CARD=PCH"; + device_name = "SwarselSpot"; + mixer = "alsa"; + zeroconf_port = 1025; + }; + }; + }; + +} diff --git a/profiles/server1/transmission/hardware-configuration.nix b/profiles/server1/transmission/hardware-configuration.nix new file mode 100644 index 0000000..30f4d28 --- /dev/null +++ b/profiles/server1/transmission/hardware-configuration.nix @@ -0,0 +1,29 @@ +# 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 = [ ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "vfio_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/mapper/pve-vm--122--disk--0"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eth0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/profiles/server1/transmission/nixos.nix b/profiles/server1/transmission/nixos.nix new file mode 100644 index 0000000..f802735 --- /dev/null +++ b/profiles/server1/transmission/nixos.nix @@ -0,0 +1,358 @@ +{ config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ./hardware-configuration.nix + # ./openvpn.nix #this file holds the vpn login data + ]; + + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + openvpn + jq + iptables + busybox + wireguard-tools + ]; + + users.groups.lxc_shares = { + gid = 10000; + members = [ + "vpn" + "radarr" + "sonarr" + "lidarr" + "readarr" + "root" + ]; + }; + users.groups.vpn = {}; + + users.users.vpn = { + isNormalUser = true; + group = "vpn"; + home = "/home/vpn"; + }; + + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; + }; + + nix.settings.experimental-features = ["nix-command" "flakes"]; + + sops.age.sshKeyPaths = [ "/etc/ssh/sops" ]; + sops.defaultSopsFile = "/.dotfiles/secrets/transmission/secrets.yaml"; + sops.validateSopsFiles = false; + + boot.kernelModules = [ "tun" ]; + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "transmission"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + + services.radarr = { + enable = true; + }; + + services.readarr = { + enable = true; + }; + services.sonarr = { + enable = true; + }; + services.lidarr = { + enable = true; + }; + services.prowlarr = { + enable = true; + }; + + # networking.interfaces = { + # lo = { + # useDHCP = false; + # ipv4.addresses = [ + # { address = "127.0.0.1"; prefixLength = 8; } + # ]; + # }; + # + # eth0 = { + # useDHCP = true; + # }; + # }; + + # networking.firewall.extraCommands = '' + # sudo iptables -A OUTPUT ! -o lo -m owner --uid-owner vpn -j DROP + # ''; + networking.iproute2 = { + enable = true; + rttablesExtraConfig = '' + 200 vpn + ''; + }; + # boot.kernel.sysctl = { + # "net.ipv4.conf.all.rp_filter" = 2; + # "net.ipv4.conf.default.rp_filter" = 2; + # "net.ipv4.conf.eth0.rp_filter" = 2; + # }; + environment.etc = { + "openvpn/iptables.sh" = + { source = ../../../scripts/server1/iptables.sh; + mode = "0755"; + }; + "openvpn/update-resolv-conf" = + { source = ../../../scripts/server1/update-resolv-conf; + mode = "0755"; + }; + "openvpn/routing.sh" = + { source = ../../../scripts/server1/routing.sh; + mode = "0755"; + }; + "openvpn/ca.rsa.2048.crt" = + { source = ../../../secrets/certs/ca.rsa.2048.crt; + mode = "0644"; + }; + "openvpn/crl.rsa.2048.pem" = + { source = ../../../secrets/certs/crl.rsa.2048.pem; + mode = "0644"; + }; + }; + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + listenAddresses = [{ + port = 22; + addr = "0.0.0.0"; + }]; + }; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + + system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change + # users.users.root.password = "TEMPLATE"; + + environment.shellAliases = { + nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;"; + }; + + sops.secrets.vpnuser = {}; + sops.secrets.rpcuser = {owner="vpn";}; + sops.secrets.vpnpass = {}; + sops.secrets.rpcpass = {owner="vpn";}; + sops.secrets.vpnprot = {}; + sops.secrets.vpnloc = {}; + # sops.secrets.crlpem = {}; + # sops.secrets.capem = {}; + sops.templates."transmission-rpc".owner = "vpn"; + sops.templates."transmission-rpc".content = builtins.toJSON { + rpc-username = config.sops.placeholder.rpcuser; + rpc-password = config.sops.placeholder.rpcpass; + }; + + sops.templates.pia.content = '' + ${config.sops.placeholder.vpnuser} + ${config.sops.placeholder.vpnpass} + ''; + + sops.templates.vpn.content = '' + client + dev tun + proto ${config.sops.placeholder.vpnprot} + remote ${config.sops.placeholder.vpnloc} + resolv-retry infinite + nobind + persist-key + persist-tun + cipher aes-128-cbc + auth sha1 + tls-client + remote-cert-tls server + + auth-user-pass ${config.sops.templates.pia.path} + compress + verb 1 + reneg-sec 0 + + crl-verify /etc/openvpn/crl.rsa.2048.pem + ca /etc/openvpn/ca.rsa.2048.crt + + disable-occ + dhcp-option DNS 209.222.18.222 + dhcp-option DNS 209.222.18.218 + dhcp-option DNS 8.8.8.8 + route-noexec + ''; + + # services.pia.enable = true; + # services.pia.authUserPass.username = "na"; + # services.pia.authUserPass.password = "na"; + + + # systemd.services.openvpn-vpn = { + # wantedBy = [ "multi-user.target" ]; + # after = [ "network.target" ]; + # description = "OpenVPN connection to pia"; + # serviceConfig = { + # Type = "forking"; + # RuntimeDirectory="openvpn"; + # PrivateTmp=true; + # KillMode="mixed"; + # ExecStart = ''@${pkgs.openvpn}/sbin/openvpn openvpn --daemon ovpn-pia --status /run/openvpn/pia.status 10 --cd /etc/openvpn --script-security 2 --config ${config.sops.templates.vpn.path} --writepid /run/openvpn/pia.pid''; + # PIDFile=''/run/openvpn/pia.pid''; + # ExecReload=''/run/current-system/sw/bin/kill -HUP $MAINPID''; + # WorkingDirectory="/etc/openvpn"; + # Restart="on-failure"; + # RestartSec=30; + # ProtectSystem="yes"; + # DeviceAllow=["/dev/null rw" "/dev/net/tun rw"]; + # }; + # }; + services.openvpn.servers = { + pia = { + autoStart = false; + updateResolvConf = true; +# up = '' +# export INTERFACE="tun0" +# export VPNUSER="vpn" +# export LOCALIP="192.168.1.191" +# export NETIF="eth0" +# export VPNIF="tun0" +# export GATEWAYIP=$(ifconfig $VPNIF | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' | egrep -v '255|(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})' | tail -n1) +# iptables -F -t nat +# iptables -F -t mangle +# iptables -F -t filter +# iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark +# iptables -t mangle -A OUTPUT ! --dest $LOCALIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 +# iptables -t mangle -A OUTPUT --dest $LOCALIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 +# iptables -t mangle -A OUTPUT --dest $LOCALIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 +# iptables -t mangle -A OUTPUT ! --src $LOCALIP -j MARK --set-mark 0x1 +# iptables -t mangle -A OUTPUT -j CONNMARK --save-mark +# iptables -A INPUT -i $INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +# iptables -A INPUT -i $INTERFACE -j REJECT +# iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT +# iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT +# iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE +# iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +# iptables -A OUTPUT ! --src $LOCALIP -o $NETIF -j REJECT +# if [[ `ip rule list | grep -c 0x1` == 0 ]]; then +# ip rule add from all fwmark 0x1 lookup $VPNUSER +# fi +# ip route replace default via $GATEWAYIP table $VPNUSER +# ip route append default via 127.0.0.1 dev lo table $VPNUSER +# ip route flush cache + # ''; + # down = "bash /etc/openvpn/update-resolv-conf"; + # these are outsourced to a local file, I am not sure if it can be done with sops-nix + # authUserPass = { + # username = "TODO:secrets"; + # password = "TODO:secrets"; + # }; + config = "config ${config.sops.templates.vpn.path}"; + }; + }; + + services.transmission = { + enable = true; + credentialsFile = config.sops.templates."transmission-rpc".path; + user = "vpn"; + group = "lxc_shares"; + settings = { + + alt-speed-down= 8000; + alt-speed-enabled= false; + alt-speed-time-begin= 0; + alt-speed-time-day= 127; + alt-speed-time-enabled= true; + alt-speed-time-end= 360; + alt-speed-up= 2000; + bind-address-ipv4= "0.0.0.0"; + bind-address-ipv6= "::"; + blocklist-enabled= false; + blocklist-url= "http://www.example.com/blocklist"; + cache-size-mb= 4; + dht-enabled= false; + download-dir= "/media/Eternor/New"; + download-limit= 100; + download-limit-enabled= 0; + download-queue-enabled= true; + download-queue-size= 5; + encryption= 2; + idle-seeding-limit= 30; + idle-seeding-limit-enabled= false; + incomplete-dir= "/var/lib/transmission-daemon/Downloads"; + incomplete-dir-enabled= false; + lpd-enabled= false; + max-peers-global= 200; + message-level= 1; + peer-congestion-algorithm= ""; + peer-id-ttl-hours= 6; + peer-limit-global= 100; + peer-limit-per-torrent= 40; + peer-port= 22371; + peer-port-random-high= 65535; + peer-port-random-low= 49152; + peer-port-random-on-start= false; + peer-socket-tos= "default"; + pex-enabled= false; + port-forwarding-enabled= false; + preallocation= 1; + prefetch-enabled= true; + queue-stalled-enabled= true; + queue-stalled-minutes= 30; + ratio-limit= 2; + ratio-limit-enabled= false; + rename-partial-files= true; + rpc-authentication-required= true; + rpc-bind-address= "0.0.0.0"; + rpc-enabled= true; + rpc-host-whitelist= ""; + rpc-host-whitelist-enabled= true; + rpc-port= 9091; + rpc-url= "/transmission/"; + rpc-whitelist= "127.0.0.1,192.168.3.2"; + rpc-whitelist-enabled= true; + scrape-paused-torrents-enabled= true; + script-torrent-done-enabled= false; + seed-queue-enabled= false; + seed-queue-size= 10; + speed-limit-down= 6000; + speed-limit-down-enabled= true; + speed-limit-up= 500; + speed-limit-up-enabled= true; + start-added-torrents= true; + trash-original-torrent-files= false; + umask= 2; + upload-limit= 100; + upload-limit-enabled= 0; + upload-slots-per-torrent= 14; + utp-enabled= false; + }; + }; + + # services.nginx = { + # enable = true; + # virtualHosts = { + + # "192.168.1.192" = { + # locations = { + # "/transmission" = { + # proxyPass = "http://127.0.0.1:9091"; + # extraConfig = '' + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # ''; + # }; + # }; + # }; + # }; + # }; + + + } diff --git a/programs/emacs/custom.el b/programs/emacs/custom.el index 227bd66..fa10b22 100644 --- a/programs/emacs/custom.el +++ b/programs/emacs/custom.el @@ -7,7 +7,8 @@ '("7ec8fd456c0c117c99e3a3b16aaf09ed3fb91879f6601b1ea0eeaee9c6def5d9" "badd1a5e20bd0c29f4fe863f3b480992c65ef1fa63951f59aa5d6b129a3f9c4c" "2e05569868dc11a52b08926b4c1a27da77580daa9321773d92822f7a639956ce" - default))) + default)) + '(send-mail-function 'sendmail-send-it)) ;; (custom-set-faces ;; ;; custom-set-faces was added by Custom. ;; ;; If you edit it by hand, you could mess it up, so be careful. diff --git a/programs/emacs/init.el b/programs/emacs/init.el index b69cac3..cf1e608 100644 --- a/programs/emacs/init.el +++ b/programs/emacs/init.el @@ -305,6 +305,7 @@ "C-c d" 'duplicate-line ; duplicate line on CURSOR "C-M-j" 'consult-buffer "C-s" 'consult-line + "C-" 'my-python-shell-run ) (setq inhibit-startup-message t) @@ -800,7 +801,8 @@ (add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'swarsel/org-babel-tangle-config))) -(use-package auctex) +;; (use-package auctex +;; :ensure nil) (setq TeX-auto-save t) (setq TeX-save-query nil) (setq TeX-parse-self t) @@ -1095,12 +1097,7 @@ (use-package git-timemachine :hook (git-time-machine-mode . evil-normalize-keymaps) :init (setq git-timemachine-show-minibuffer-details t) - :general - (general-nmap "SPC g t" 'git-timemachine-toggle) - (git-timemachine-mode-map - "C-k" 'git-timemachine-show-previous-revision - "C-j" 'git-timemachine-show-next-revision - "q" 'git-timemachine-quit)) +) (use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode)) @@ -1283,11 +1280,33 @@ :mode ("\\.rs" . rustic-mode)) ;; run the python inferior shell immediately upon entering a python buffer - (add-hook 'python-mode-hook 'swarsel/run-python) + ;; (add-hook 'python-mode-hook 'swarsel/run-python) -(defun swarsel/run-python () - (save-selected-window - (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command)))))) + ;; (defun swarsel/run-python () + ;; (save-selected-window + ;; (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command)))))) + +;; reload python shell automatically +(defun my-python-shell-run () + (interactive) + (when (get-buffer-process "*Python*") + (set-process-query-on-exit-flag (get-buffer-process "*Python*") nil) + (kill-process (get-buffer-process "*Python*")) + ;; Uncomment If you want to clean the buffer too. + ;;(kill-buffer "*Python*") + ;; Not so fast! + (sleep-for 0.5)) + (run-python (python-shell-parse-command) nil nil) + (python-shell-send-buffer) + ;; Pop new window only if shell isnt visible + ;; in any frame. + (unless (get-buffer-window "*Python*" t) + (python-shell-switch-to-shell))) + +(defun my-python-shell-run-region () + (interactive) + (python-shell-send-region (region-beginning) (region-end)) + (python-shell-switch-to-shell)) ;; (use-package cuda-mode) @@ -1750,7 +1769,7 @@ :config ;; This is set to 't' to avoid mail syncing issues when using mbsync - (setq send-mail-function 'smtpmail-send-it) + (setq send-mail-function 'sendmail-send-it) (setq mu4e-change-filenames-when-moving t) (setq mu4e-mu-binary (executable-find "mu")) diff --git a/scripts/server1/doublepuppet.yaml b/scripts/server1/doublepuppet.yaml new file mode 100644 index 0000000..f28f0f7 --- /dev/null +++ b/scripts/server1/doublepuppet.yaml @@ -0,0 +1,21 @@ +# The ID doesn't really matter, put whatever you want. +id: doublepuppet +# The URL is intentionally left empty (null), as the homeserver shouldn't +# push events anywhere for this extra appservice. If you use a +# non-spec-compliant server, you may need to put some fake URL here. +url: +# Generate random strings for these three fields. Only the as_token really +# matters, hs_token is never used because there's no url, and the default +# user (sender_localpart) is never used either. +as_token: doublepuppet +hs_token: notused +sender_localpart: notused +# Bridges don't like ratelimiting. This should only apply when using the +# as_token, normal user tokens will still be ratelimited. +rate_limited: false +namespaces: + users: + # Replace your\.domain with your server name (escape dots for regex) + - regex: '@.*:matrix2\.swarsel\.win' + # This must be false so the appservice doesn't take over all users completely. + exclusive: false diff --git a/scripts/server1/iptables.sh b/scripts/server1/iptables.sh new file mode 100644 index 0000000..dbf2e45 --- /dev/null +++ b/scripts/server1/iptables.sh @@ -0,0 +1,47 @@ +#! /usr/bin/env bash +export INTERFACE="tun0" +export VPNUSER="vpn" +export LOCALIP="192.168.1.191" +export NETIF="eth0" + +# flushes all the iptables rules, if you have other rules to use then add them into the script +iptables -F -t nat +iptables -F -t mangle +iptables -F -t filter + +# mark packets from $VPNUSER +iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark +iptables -t mangle -A OUTPUT ! --dest $LOCALIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 +iptables -t mangle -A OUTPUT --dest $LOCALIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 +iptables -t mangle -A OUTPUT --dest $LOCALIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 +iptables -t mangle -A OUTPUT ! --src $LOCALIP -j MARK --set-mark 0x1 +iptables -t mangle -A OUTPUT -j CONNMARK --save-mark + +# allow responses +iptables -A INPUT -i $INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + +# block everything incoming on $INTERFACE to prevent accidental exposing of ports +iptables -A INPUT -i $INTERFACE -j REJECT + +# let $VPNUSER access lo and $INTERFACE +iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT +iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT + +# all packets on $INTERFACE needs to be masqueraded +iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE +iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + +# reject connections from predator IP going over $NETIF +iptables -A OUTPUT ! --src $LOCALIP -o $NETIF -j REJECT + +VPNIF="tun0" +VPNUSER="vpn" +GATEWAYIP=$(ifconfig $VPNIF | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' | egrep -v '255|(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})' | tail -n1) +if [[ `ip rule list | grep -c 0x1` == 0 ]]; then +ip rule add from all fwmark 0x1 lookup $VPNUSER +fi +ip route replace default via $GATEWAYIP table $VPNUSER +ip route append default via 127.0.0.1 dev lo table $VPNUSER +ip route flush cache + +exit 0 diff --git a/scripts/server1/routing.sh b/scripts/server1/routing.sh new file mode 100644 index 0000000..64e0664 --- /dev/null +++ b/scripts/server1/routing.sh @@ -0,0 +1,14 @@ +#! /usr/bin/env bash +VPNIF="tun0" +VPNUSER="vpn" +GATEWAYIP=$(ifconfig $VPNIF | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' | egrep -v '255|(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})' | tail -n1) +if [[ `ip rule list | grep -c 0x1` == 0 ]]; then +ip rule add from all fwmark 0x1 lookup $VPNUSER +fi +ip route replace default via $GATEWAYIP table $VPNUSER +ip route append default via 127.0.0.1 dev lo table $VPNUSER +ip route flush cache + +bash /etc/openvpn/update-resolv-conf + +exit 0 diff --git a/scripts/server1/update-resolv-conf b/scripts/server1/update-resolv-conf new file mode 100644 index 0000000..f1e5586 --- /dev/null +++ b/scripts/server1/update-resolv-conf @@ -0,0 +1,45 @@ +#! /usr/bin/env bash +foreign_option_1='dhcp-option DNS 209.222.18.222' +foreign_option_2='dhcp-option DNS 209.222.18.218' +foreign_option_3='dhcp-option DNS 8.8.8.8' + +[ -x /sbin/resolvconf ] || exit 0 +[ "$script_type" ] || exit 0 +[ "$dev" ] || exit 0 + +split_into_parts() +{ + part1="$1" + part2="$2" + part3="$3" +} + +case "$script_type" in + up) + NMSRVRS="" + SRCHS="" + for optionvarname in ${!foreign_option_*} ; do + option="${!optionvarname}" + echo "$option" + split_into_parts $option + if [ "$part1" = "dhcp-option" ] ; then + if [ "$part2" = "DNS" ] ; then + NMSRVRS="${NMSRVRS:+$NMSRVRS }$part3" + elif [ "$part2" = "DOMAIN" ] ; then + SRCHS="${SRCHS:+$SRCHS }$part3" + fi + fi + done + R="" + [ "$SRCHS" ] && R="search $SRCHS +" + for NS in $NMSRVRS ; do + R="${R}nameserver $NS +" + done + echo -n "$R" | /sbin/resolvconf -a "${dev}.openvpn" + ;; + down) + /sbin/resolvconf -d "${dev}.openvpn" + ;; +esac diff --git a/secrets/calibre/secrets.yaml b/secrets/calibre/secrets.yaml new file mode 100644 index 0000000..1a5c0e5 --- /dev/null +++ b/secrets/calibre/secrets.yaml @@ -0,0 +1,55 @@ +smbuser: ENC[AES256_GCM,data:Xlz/NzVjKk0=,iv:DvhZOoOb0eXc4jIZPwDXGRkZxWI4Fg5MC9s1IRhYWuY=,tag:ApTT/Y9K7p0uPRZAlXgekA==,type:str] +smbpassword: ENC[AES256_GCM,data:t5ic3YoNkc3k2brjN6ZRjNKPEYD9WKk=,iv:lBtSSuEnUKipapqq7gYWmkdA8tcMIZuNy8EmqqKHFWU=,tag:qas1f2wlZm0mpcgPhsZtPA==,type:str] +smbdomain: ENC[AES256_GCM,data:TepG9EMhs8I=,iv:w+CxqNxrjIBx2G33EoKkLSuTHrSSzvDQ2JXuOHtUTmw=,tag:oy5vKyhYc/bOV76xEDaVqA==,type:str] +kavita: ENC[AES256_GCM,data:g+2WXcm7d8OxLhrtWXx15SdRx8VXax1SG2GHoWizXDUZEKnEwlQe7/Yk6DQ=,iv:bQ9s5z6jNGkf59cxzR1o+etA+GA3Y8L6Bgfv9e1Txww=,tag:NOB7iBH3yoE0/DtmuQyaBg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1q2k4j9m6ge6dgygehulzd8vqjcdgv5s7s4zrferaq29qlu94a4uqpv76s5 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGejQ4Vm9KWlJYeW9UdElP + MWVUNWx5V3dDQWNPSkRJNjl2a3ZLWVpQMXdJCkdjVlh6andkM0oyMWkvaDd5Zi9H + eEVZalFYQjNpQUszUW5HV1gzRU9FVXMKLS0tIGorWGRnQkE3TXhYRHArMDUrQU4v + UnVYNGRlZTRoZ29YQ1B5S0U1ZE84VHMKgp2XRaVtRcubXfjttQfk9UKbqZ6EbL/O + coZUAPXRrT//oRh2JFu0Q4+5zoewI2j1DhUS9HuejM5CIColYUasJg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2023-12-23T04:28:28Z" + mac: ENC[AES256_GCM,data:aLI7dMi2merChhkQaqmrlbvC2V6Bh0D67RE1RxTqZLYmFE8AINBewBka1ktIVc83IYYFyhpTLZDmhZF5q3McIOGXjlZUcEDtb1C2zZZEkXJrbFe3yoZG+DE/fOB4I2uXjzp5iOG+lZyWAGQgrSHMSFV+IbAg4bb++OilZ2oXWYk=,iv:M+rOanpm+LakksTb4jCzZph3zC7MI6XeV7nyXN7qo50=,tag:Ec+HJtUtzUtrxbeCe+wDJg==,type:str] + pgp: + - created_at: "2023-12-22T23:23:45Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTAQ//Y7LOX5knf4s2Dku9DsVIgxUe5Ox3u65uBKL6vTE5tLuN + Oeuyvd8eGaOWvPWcu/7bbr2Nd5Y3fyyG6yTSzjweyvPgnNbBswaYZxPhj+GfHSL/ + yRcdEVX8QTEirlUYhFTVuD3LLGAxiJI5dvIHF87FGfr9U+xiTg9fblwFlEGb9F5q + TYUOSLvXUS6KbSuGnNQR7kZua2eZ/nvAIW9gVh1RrTjLzSyGdPgiqRFC657MFOvP + IlVOVKEngY+FTFBUH8kRzAxAz94Gtern2oqpuuQu0agriizeE6TA9OPOoMNGhsQD + C/DK+pa8AQ2JV17iy64rPkpRGGXr7PqEEmV386z6+O6Nbq0e+1lqigPYkxJqWJjR + 5K55znA7jRxdit6AN978pKerY9xSmbiRx35qWoGa2WK3iZ5ACcsXbAxW4lHagmdH + TmcIB4qzzHdsYQ/TxLcUioFx1EooiNZ59Asa+Lj16QgMAyDF9SZ6Z8HhQGwIBdub + HNvu8+f3/D70I7/DreortuwSvzV370+OBb0knoVZcG+i7DlAM64LukZmety9PJIj + JhUVdvp/haL7FWI7zU2Aj5j/kXGKjmYlb3N5Zes3I+MLXdL+8qqeOG+NsQR0Xvxq + xEsgEUyqaXuMwJfyPFw89NkF7oj3qqWlbnLGBEXcrXRI28Urkpwet1Z//p+WpDCF + AgwDC9FRLmchgYQBD/4lskxdD99hF0I5Zx2h8Lt1UqXI+lMROqRjjTI726Z+R7xK + 2PEJ4l2neJIP02QMm3HTAOQJ5P5t0Lb0kM1vbBY8WOF3v6aLt6Va8pwBF6TxlfGn + 5UUCUQ8nLwHUyKGAI+atveZCcUkyfhy3y4pMbXK6BQ+2tbLGEjFeqVeakk9e5MGo + 8BwYbU0Rr4KqAeSVkYb/qCErycM9fQb07r8xiPqSnKuZe4RxolWfMTnwP6IEI3GJ + AteS6MdMOtK1BufP/XKX80aXIY9U/BimyEndmT4b/83aAid42xPH25BZTfC0r0Wh + EArA0CR2rop7wE1GQq1R+stet4kSyBPWefvJg3wVSpF3Xj/IsJHz3LAp452v18W5 + rEWa8bzUT3vlVBjINhoqUJt4VHGx74kJml6WY334XyCy2xxY1C3sSD53tw3O34Qf + rmcV6m/BeCwL4t4rsG+vWzwaZSmjqr1D6H4JI9h2HvwXb45y4m28OewvAu0FMcyK + tpjxnwdXAOQC/GdgKPuM1eomgurGBrfCeHbfXHsu3n2LPTm6RULWKW4jzj9dbjCC + CuD4IzflExz8E+lqTBW4CppBgfCBDx5IIS1sv7YVfqFf1Upl434kknKffkmkq6mk + G70ATAlUX8Ci94rUv/JCokCj5GcIzVmKUiI+zq0rCQKxcJ6uLMFYZGQ9v34kQ9Je + AQ+3j+iSzV7DGtkdHI9LEAsLj6ZeFPtePULfxsHc3tjfpUFTQgurMS/QDojQMMEn + 73sQEYv+ihSsV+WToRpgExM1ANIEZ5eFTloxKKmULkYQ6tiL8SIywxz1vJW81A== + =nyE3 + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/secrets/certs/ca.rsa.2048.crt b/secrets/certs/ca.rsa.2048.crt new file mode 100644 index 0000000..6deea60 --- /dev/null +++ b/secrets/certs/ca.rsa.2048.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV +BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu +dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx +IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB +FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1 +MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg +QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE +AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50 +ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy +bmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXD +L1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzX +lH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWp +cdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/ +8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB +/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RC +OfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tL +y8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZO +sqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpM +b3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4G +A1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUg +SW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2Vz +czEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5j +b22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAn +a5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xU +ryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK3 +7pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyC +GohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz +1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUt +YDQ8z9v+DMO6iwyIDRiU +-----END CERTIFICATE----- diff --git a/secrets/certs/crl.rsa.2048.pem b/secrets/certs/crl.rsa.2048.pem new file mode 100644 index 0000000..a58ef56 --- /dev/null +++ b/secrets/certs/crl.rsa.2048.pem @@ -0,0 +1,15 @@ +-----BEGIN X509 CRL----- +MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI +EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl +cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw +HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0 +ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl +aW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZa +MCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG +9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5 +jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EW +B4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Re +ze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA +5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqy +MR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A== +-----END X509 CRL----- diff --git a/secrets/general/secrets.yaml b/secrets/general/secrets.yaml index b9e5e06..30feb92 100644 --- a/secrets/general/secrets.yaml +++ b/secrets/general/secrets.yaml @@ -35,8 +35,8 @@ sops: TGF4USs2YVNlZnZhM3lZNlpHbm80SzgKxwh4DS1DnV3KFVfPw/8+zGWo/YVnhSYd Yi1hpRD8yD0eglGVGaWCItniPLRFDPpHL2B6kkHsYRx3Rl7daSJUVA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-12-13T19:28:18Z" - mac: ENC[AES256_GCM,data:5zQWLi1To2XUc2GXCXmwQMk1M/hPORhK1xuNbihLgJVt+ufEBScyQ9kk0BTcmQAbVXUZO7f4qHAx/P8M99CiY5g1H3Zm48kMrohC0ByJfSUHt9w8ApT01TFsyYrbtX8qoSVyAMwu4/dV1wTv0Nm0a0A9MhE8Azr7cPRgcmtixn4=,iv:rsMo8jY4jIkBlgnJ/EdGoyjl1aKKAeSnvde7ndWnBkE=,tag:pEiIb5LLULxR5dcD8RknSA==,type:str] + lastmodified: "2023-12-22T00:14:58Z" + mac: ENC[AES256_GCM,data:BhTS9q5mfdJegV2FA6r9aOxjhvjahhwzE2K7QeJQkEFfj7hnu3qIQy2CN10+9YDvrYVpoSWuEJ3L5uFFzugW6Ubz18SiuXjZGRUxfZn9+AzFDJYntpOs9r8rG72w7tY2ty+UefjhW4oDsTlv7TdPHlAWPVSY5X7H2G5qNxqHAfM=,iv:R3doafqNTyuvvBM9TG3UbZJeAtw5CDAePQDT+aGkA4c=,tag:BKTZBUSd9+CLl56jscjdRg==,type:str] pgp: - created_at: "2023-12-13T19:27:54Z" enc: |- diff --git a/secrets/keys/calibre.pub b/secrets/keys/calibre.pub new file mode 100644 index 0000000..1f5d83e --- /dev/null +++ b/secrets/keys/calibre.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGAkIdo0pObC1/QmOVwqhQnif6uf+CylNwlWnbJL+Zrh calibre sops diff --git a/secrets/keys/nginx.pub b/secrets/keys/nginx.pub new file mode 100644 index 0000000..01bab6b --- /dev/null +++ b/secrets/keys/nginx.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICtt5v96QEIttEKf1H3R04uwU64JFDB6yqJRvdsiyYz2 nginx sops diff --git a/secrets/matrix/secrets.yaml b/secrets/matrix/secrets.yaml new file mode 100644 index 0000000..a779a2f --- /dev/null +++ b/secrets/matrix/secrets.yaml @@ -0,0 +1,56 @@ +matrixsharedsecret: ENC[AES256_GCM,data:0VA//FJ+vlFAKpMPIHw/VLsXMgN5pnGwqXr8Xow5F/I9R5IgVip7b4qUPx+PU32D7eeEhW8QgbrwjeqI5wrt3g==,iv:A2iAYeDRQf1SFyF2hEKK1TLakcM40HBJMyi+Sv0rRuU=,tag:t+B6OFe1gNQg3w6qlU1AOQ==,type:str] +mautrixtelegram_as: ENC[AES256_GCM,data:6i7JlAbz3OwhxQjftjkHB4o9YCYhMmnHNgRW0cnXLcMYsG3SME5b5RLOvGgavvIG+9HBv45fZRIICh1K6xZ1ZQ==,iv:FXC15XJZxwepIP1QEWceQlthwyISsiA89w5MXrxUDnI=,tag:hfnDn1rEhPENKDvpXH9sgQ==,type:str] +mautrixtelegram_hs: ENC[AES256_GCM,data:ZMXfosvSZlMs/IEVNfhYRWrjS0l4u2Fc3u+nB3nrTGXuQNXN7X65y8fbbzLVSX2TKgPK1HOiQjtTcIxX+GstFA==,iv:xQkD+152yfOQM0S9ZtlEb0VV8EROLkPeD54Y9/FHkzk=,tag:8qCOZLQY6a0+9bKP4PqV6Q==,type:str] +mautrixtelegram_api_id: ENC[AES256_GCM,data:paljhNLosw==,iv:D4hiwm5/3nUoNRdcN3yoJMGE3anUIJ8lEQYbN48v4/Y=,tag:SpZ/9phnQI+F5m9OTGxU1A==,type:str] +mautrixtelegram_api_hash: ENC[AES256_GCM,data:GoVLL22uNQMNxlesc3pmuSWxz6YVTMIu4VBnQO0Y1jc=,iv:WSOZlHrWCBgCdCl+CAsc+FrGzTH79+GePMUCm+0/FCg=,tag:Ti6MNpjBBbUnmtRIVAPBwQ==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1t2uj8arq8nnmd5s3h32p7z7masj2gqe5ec49dtr8ex2nlgef3yfqtgcnj6 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTUGwrb0VncGtIc1BPN0N0 + dGpLamE5R05GQnp4VXYrekttTEFvQ1BZNEVzCjl1K0syTEQwTWZqRUVWREtuZ0U5 + VHo2WEF5dUwvZlhJOWZDcXdWWDZ4OGcKLS0tIE1YRDMzbVMwMU1ZL0RjbmkyRm1y + L0Fobml3T3puL2Z0dW5EaEZQSEdZRFkK/nnz1NrsGqU0MYV+4T9gRMP/iMkCWWB1 + B0yqYsJjUuWLIr1DckTF8di+uLIAwM9l/3t64dAsQPrEfkatGkh3+g== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2023-12-29T21:35:56Z" + mac: ENC[AES256_GCM,data:MeJ+FU+5AFMPrZpze7F0f5Of17lvNq1xdjOjLt4zNSO7qdwxEjtVLneQcIMMNAuDi5Uv20bCA83qFz7xPtwZ1Ftw36tySh/yrrsqTwIPNTZtZKzDvrHcxZsoi4VTvLnFR8b+Mxw+60LUxnztIPAHDcfbqaAGDaK7oKKZpj/jiMc=,iv:U73mNdp7vt19lHcjjzpSv6jaPMoHf3cwYi3SlbK5MdA=,tag:CwNHYXtBypDk5lt54U+Ihg==,type:str] + pgp: + - created_at: "2023-12-29T21:35:36Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTAQ//UtkRIJ7tpD27NQK+pFXWjurxppYbyTtt4ZvBiHLkUTFW + Em9gjI2n90rSYdy+Nf4EpnaVRoSmjkjlJbNWp14GDfxegrmgSd2QY1dFCl5UYBLD + a6inlamwajmAlSHXW4JpMvbvSdqcnGX2AKkUvvoFsFrTCekqBRWiqxOW15MAKRLY + GWlJ6uVeqvyNOtEy4FCC4OOHb8yh/qHttboN5JtOukJtTGPrrN1W457sgB7SEm7G + df4OzynX9X7Utz9+HvIHTbvLlvUdL05ATVRJMa4PIwKUzWf8bLciZh2DYDEUOh5E + G6G9AL7t9E6kNeL9s2UCZdcxUlxzkDRvCoIPnrxpBSrLxkMCJkxIBlE6vemz6jGw + AB8wXxIAhDwMSQHnaa6jrFqVpQ7xo5vaOFQIckIl1FdCwyfd3X1SPwF16xW3f9dV + pj4gxDV9QlR+lh702TZihNwbVDv9+xIUsrOyoBPeLzgPikcRHvm6dtc3ueA+vo77 + WR81K/3jhqgQJMKowRDr+tYxHJtiY5OOCxnY3pUElFawbUd6dZzmU8GQCMa8+TjV + ln4aCa8IwwDG95XpctOzMSpOi4OhsVh6kuvc8378xi5kOsv+d8QR6Cj7Ene0MAlZ + i8UpD8AQgPR2FPuud9gkKfEVW37F/PYLbgs25rN4gLpM20ca6nFiFAWUv9Qarm2F + AgwDC9FRLmchgYQBD/wPZ4CfC58Cwq4Qnvam+ddSZLkih/w/tYj2V07dXip4/+yD + pej09oQCqdIxC8NFKUj23MjKRS0wouMiVXq9Mo+iAp1ujrjQKY69OzfD4tVM7opW + 5SXHHlXyQwAlgZOVwiuV3odbUip2aax31uzgB9aXtb1UXc5Mh2zdN2OdEJ9jtFGN + Yi/DHHdJno/hTgEvV7L5xBSDrWTGzvdLvICm/okqmM+lCG/HARng21TV/sPDDz61 + DGhfGw8b/MuF5mTU3GYjUcVgg9+26YGUxi5SunJ11zuLNHwl7CEC67+Cw8hzkaaa + UapTIB5RlQP4Q8vI8436MqFrQn1D3GdZKrE5tN8pFoJRSD/uMe4ICBC7xc2Oq0XR + iwXsBKlP5+o0yvC278eb9FnHQHLHlExBAL+TkSt4fT6hbu1V6niGX8/ziac1r9Dx + mmEyt7QJA+1MIjT4MoQCQLVl+4zf/f3kF8WBz6Y60oTaiLgxwJt6YnHfVUl7A0OZ + W40oiRRHWSYdibTGVBS1KT2fA+n1MeH+bzw0PoyHDN4sQtAGj8xlY8/+lzBO8E4B + 8lJp0GPoyxUnztNVXAuoTXp7yB2YxMFipXsGi5rirsbc22Nb7A8W21ZYx1mxG3pj + k+PIZqMlYA5QGfWST0ESDiWn8lSC1rH9wtHzzWjOTZfWaNSKumyUbiO+41cjbdJe + AcuaYiRLmC06pFLdZ4OS/iAfvIeybondx01VWSMmhFvA5RntQG3Hz9ke//PKtjsa + YizbQ23YPPGIq1wdcOuPq4F/LpZ/zQPZ5n9F6UC6cQf3RHVOfHxHZc1y0Tq8Sw== + =Th9r + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/secrets/nginx/secrets.yaml b/secrets/nginx/secrets.yaml new file mode 100644 index 0000000..f55f007 --- /dev/null +++ b/secrets/nginx/secrets.yaml @@ -0,0 +1,54 @@ +dnsmail: ENC[AES256_GCM,data:sDKEORfYYHg3sXvQhs/2ZoQtIKpe,iv:DkzqpxVrFEu2En0PEwc/ZAAeAM927ZaX3Ll6eAxjYyc=,tag:+FrjolbwzCloyOyhw3XZlg==,type:str] +dnstoken: ENC[AES256_GCM,data:FD9G9D2e6GEmGzVcYAAGMia9m/dVIjXtdc8WZJ/7+F8Lwi0kQH/VRA==,iv:FMSakGp/r3L5MwhXFhvH3nTNY+B37XU4dMe5Wajs9ZU=,tag:cQsxWB/FGUbuClgrgqA0ng==,type:str] +dnstokenfull: ENC[AES256_GCM,data:5I9nJpwDxJb9QVZZ1YnQEBgYnkM1gCBnyhR2XSgVQRNejzd0NXAA1g==,iv:0jmRdEMg7S+SoOTserDeYsvh6fPq8k7VIxnuT0Gnmak=,tag:3EAH4xSoTTGweOyE0sfMhg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1zyts3egct4he229klgrfkd9r442xw9r3qg3hyydh44pvk3wjhd3s2zjqvt + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUNDJNZ3JrTzg5anRXZ2VP + cEo0aUEyU3I4VnVDaWhkemhRNi9vV3JwT2lZCi8wMm5wZFIrUEx1bmZUaWNFWXEr + bWxua0VCdWhRdVRmVmZTY0JDbjdLdlEKLS0tIGFVaXdnVzZUcVhkbjBFOE9PNFZv + SHJwU1NDSVg5MENDamJVYndjNU15M1kKHuibOTqcSUBwtrQVj0xzu2icc8dOxRTq + uILxeOCwd8eX/hyuyTe/9prPD/Q9rlwGji3gPJxEpm5X+R36yN4hWA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2023-12-25T01:23:07Z" + mac: ENC[AES256_GCM,data:18rZUjQ0LPsMZakxoU5DICZ73NNCM1Y0l8Uufd3e9sogwS6PGOXqtK1bq7yTDPsjsa+upIalXeuqvldubB7gvK9NVr6hQF2rOwVmzROTQVE0G61bTyOCzqqJ3BXdbbiCK7QBXZcboiOYeCuSHinr8qKrAQDATBj7myyYdLyZcdY=,iv:tiBp1JDu55jsfh6tMHSQ/3+hPAlpCQdHeMNxRWbwB30=,tag:iaXBEH51KbyqHmrfvQEJpQ==,type:str] + pgp: + - created_at: "2023-12-22T22:43:24Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTARAA0d33RcKMjMVlH0X0rBK5eIDz4XufU/E/ACJZQfqmzKla + SVF6xlYPVT9OYYvYaoEy1nDwd+bizPaGkp0t4o8Gh7BFRSHOT9f5lWggQ3SvPT4u + F/zhssgMWgVktAgimtlC593cYrnn/TbIPls7iLTstJznYYsGIFmD0UMcEeM97Tg9 + pgusMstRkRYrL9TLNeumV+KoUoHbh6G2ZNJXBOhA9c5d5CiCYicfqBHSbM0TSXH+ + 0AZK+4Ll9W/Bn2b1gTPwVzVhtyqMYOklJXoP6xhJhh1niO4OJwhkgbc5GFRmvqBl + lequVFZt0WH0HK8hcyf/HWZYwu/Y5tXujI+Qulov5KRkE413Zu70l7jyjwsGAojo + e+PtUwhH/qjGhD7wou+4fE6Gz3RAAnkqs1081RmowxzTpfRHBTxrk+PheFVYoyQX + VTWtr+DJRPyWyQ31Ljdaw/baci/8yfnViRhA0rY/XdsNGJn8BjLXmBmrMMYPudrf + hykvigmsr1+exwFbpwXqX5BDK9urvsagr+2oDIOR3AEEsBkShGrbqWi1U+syX3Y3 + g2bmoxD2W59+ODWfykTwhDOS2ZQ/PyI6Kq5AKdFWSOAhrwvwmwBt9hE6RAuYSoc5 + Od0BnY024SkE3WPlw+o9JZomPcKN/4xV5SzUZerB/5N7unP/3NQvMipvIt4SCW+F + AgwDC9FRLmchgYQBD/4yXIyRjhn0+41CgcfjWjqb8gyQWDq8oSUMlUSo0W7VJqSv + zojbWQ8YoJmdHWCazBGi6dLxaqkupC2YyRlfVgCvjlxfvP1b1JlLD5/QKfGJ+rzp + ZFC/FrzrHKLudutAZ0mwqEK6WC0zKLytSkpi+IKtFXJSbtagU0jETIjfYuKCxFZn + Sp/qzlbTfNdm50Gx7b6b9JmJEHwa9GevTVZER3e41+8beRFIocDnfBx1Z8FFTVng + F9fcc7/aNcMLBY8lmSCpPSpmcu207y3S9SFJsrLF+qOKcGKwZ5xnLAYAvAXY+EFo + 19ltQO3KyTsKjw00ljSdJ/kPQPanOlsDDlji0cQ2HgJ0rTNd9CNCLg8XzoCJh+Uy + lYImamgYqCW1BxBdYCt9+LPVpxR1D78oq22n3hKeKgJuSGzWXE7oIi61+jQCucWP + 2H8lSZ22kCzjQXu8sccNv1saOF6M7dnFhWAbFTuXaSUROBUnfzMaLx0HcI585p+X + oTrOkZr+pgKFIeGYhqXqJtDhKvCkJ5gO4mu/qNWqxt9TXOYEiEnd1T1BtmfFMMr/ + Ed01waKAxrqkED853CBG0L0ogGd+diMpVL1TBVq/9Bf85P1CGB2RsGgPl0eFkjck + 4KR4dvmSykZhusRRdih64ksktB/4quEZ+FvDxy33+OD4gO0NvfSJwKAEBJmlf9Je + AXCKsM/JHpqgBJCkJnb8gBYlJSl02BCIMmdhBLulqZA81KeWazu+yXEdfR8mbBks + OUX8+f/+cZwFVB8eGtDc2BKqL9mudLVr0tHfoEvT6i6mRfU5olUkSforH9urcg== + =aq6G + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/secrets/sound/secrets.yaml b/secrets/sound/secrets.yaml new file mode 100644 index 0000000..0ca210f --- /dev/null +++ b/secrets/sound/secrets.yaml @@ -0,0 +1,52 @@ +mpdpass: ENC[AES256_GCM,data:oPwpdBAg7Z1kfLm/awaTxXKZTIVWMQDTUw==,iv:jGWviJ+zwolzmYUkjfiFNepEPXSw7oJH530PaEV+GwY=,tag:nUr7TJCgkumAUZ1JrziI/g==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1w7tfe7k0r0hm6mzz0kmz8302kfn0rlh96w7g6zwqd4muqg7u9anqv07745 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwdW50NGJRM0Z5YytVa1pQ + akR1M21Ib3pyZWZWdUpCR04rb1pRalBiQ2tJCnNzajZpbVNVZWRrWEFvU2RnM1g5 + akhEaGZCN0V5dHR1Qm9IRVZvV2hGMUUKLS0tIHVGWUs2NnpTYlFxa0p5T0RJTFB3 + eFdSYkpPbzhGU0ZiM2FEWUtqcmtOa1UKCsQiVQpSI3GWpvU3zlvKSZPbnDbVNJJl + UFpygD0jqPWUvBFqALHKh8i3Li7B+ItR32IUO67R1bigS8HjYzdkkQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-01-30T20:54:55Z" + mac: ENC[AES256_GCM,data:XhknSHukwELPxfdskHSu6ewK1keNl3lcMQW0PqXqDn/ZxQtjQX1Dj5lez5QMHSFq5UAzXt4zljxRNvUtLNfnRBpTe5vWCgC7Bt1ZHz4ikmbp8/VCMteZVh3rWr+jM3j0eGsTo4LZD46IRUN6FDhVVFb4fCiiJGVKAusFonjaYOU=,iv:IP/iOv3Jb2O0PT96K4gBCf77PsMl5wt5V0O/xOUwnRE=,tag:enWN7CAMvFMvgPGuhqu3iw==,type:str] + pgp: + - created_at: "2024-01-30T20:54:19Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTARAAlhqb8zkCNVWJ3Iayj4IvqlHe6FZiYUtu1jucKh0Yniwh + LmctvOhIwB1UP6NEeX0ReqbMCBJLpB7Fh661RgYguOA+mU7qB9dkTi7OMVYN0fnS + OXeAl2+9SKNO9Tel3XqFtJZ9eCgSE3AR78U55SH8lhe+243U99+dXBx20Qzh8fYk + YGggcMhzJewou9OziOdQtq3hKh6BDlOPU8VEufreeoQUN3CDbWXeUzbDbH1Z5lbk + Ibp61T7RZ63IcfQu9GPWxyaxkCw1YOtbRxUN2H1BYIGjNuGag7Q1dRud9v1iQPAv + SN1JWq6y5q7zxIYUVliquMsiMRkkX4mAINnY6fo/J4aOTbZCUyO5bsqTHYjrsUwW + lDDErVl5HS4iAfEGZrKRlY5b9aZMZ3o/+DxEa61mvl5nFaSvpy6zQXV4TK73B++j + 3EqUji9V3RTcKXKJqu5dNL5Sh31GSv2U8RADpbPh2ned1igx+3DodC1RL64z/jP1 + HKFbhz2hYhfKxPLQTcYvmmrQbICjjuLNP46hQy7Fkk1IvPZw2hDEXFuZnKsFMLPc + tnIC4/yhnykdDU6Hx/LQbSJWs69daN+M2ty9fjqvW/Thl+lkCb89I9dA+H5TcHF8 + aFfosBJniw7Nm0tUOMYCtjh2lRYzs1Hm8GyYmL3SXkNq41n8kIF4viW/Q3SVVqGF + AgwDC9FRLmchgYQBEACiTLmTrucjVeNf5iRI+n927+S0KOqvjRJSAGC/2jjRQBxG + 8pX725XK3EuYHB0pWe/cwat/XzgrKbHhHLTOAoZLXkL8mailFYqDkyPWyY0KUOv0 + reeGO0oQxbbVaurtTsXVfNvkHYeAPcIgZoHgSaPh2fTsxQuvBpo5El7Nk6EGWp6R + I/obM0XMS72gUnxOEMReyk28C5xncsQXmC02NK7zvq2abKS0mv8KmMR7nvLWg0nh + Hy2Jh4e0B7CvMyLOdJo2VXBxoJhb4CGoyidXg8Fq+fHQSDOFCF7Tb2bgCfdqWowK + ip6CdHnj8mj331LWdpW/Yo/TYDN4fnVVHMO9aISiC1S50Lb06uwhJlBYG8HnWJ3Q + JCbftxDdiToJA0fDq/L+sRMcqN/l+WoaxS8PsmSF/6xuQsa+bt4S23XITQkWrtx2 + S7reh/xsl9YKR1L6cxOUiaazuYn3aGlUTqSY0PfGVsfVo5+vN95q5SYOqqx8s8+Z + h3jFLe2cGQu3yOSeUhHJYBjqho3dcRW3Mo6crCh0bj7LSIoeIntCC1G21MzAcXoL + Xa+u/gM5HzPQ0Czi9v/bdwtN9eELEx8gOVvq7zhJTM1ot+hxyt0XAz6nCkkVLr3D + sasN1xs20+VsiRqqKwPpNxvDwkSyt6zMHf7zDxVW0YhyTqiIHeWSA4f2aqxqstJe + AfxPey5NzP1PX2ovInUhaqVQc/L8u+04aJa7JFiW1wjZP6BesPiy/mRA7rBMUmE5 + tVlrec9utTLVp7aerjuODBsDarVILmFJetgDPb7vI42BvxTpjjCiJJjXPXQzcA== + =6npP + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/secrets/spotifyd/secrets.yaml b/secrets/spotifyd/secrets.yaml new file mode 100644 index 0000000..64a3228 --- /dev/null +++ b/secrets/spotifyd/secrets.yaml @@ -0,0 +1,53 @@ +username: ENC[AES256_GCM,data:8c739M/ygFSYP/xsDovnPem5wrUr,iv:LarOsdIzdz7UP2WtGt08bBTTZLo3Ne8RQM74mFJpHLI=,tag:r8GrK0d8+7C6m74vJ0X3mQ==,type:str] +password: ENC[AES256_GCM,data:E6CLOD1IZUzsjzQ=,iv:EL/EvTUOTatCBCZijCrnrz8ZIeP85znZWCM0PpCJ2y4=,tag:Dlas9VApM34F48l5/CVF6Q==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age16d6wulu4vzuawvsnqv0cqjhxdz9e20qm3xdnzq2lp7787srl8shqsqlfps + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmaUZRRlVoUE9PTGhSbFl5 + UU5GNnVqcm81RzZsaGNwaWc1ZmVKeHRiQldrCkVNUmFGeWhIaUlSTkM2UmtUakU0 + S2VMeGM1K1pJUjZJeGUyREo0OTlvTlkKLS0tIEw2d2xRNWsrT1ZmYkpxeDlwUVZS + bnk0T2dPRWFrTUg4dEpORjZLaWRFenMKw5pkVC3jaHlACgH2vCGcwoGP36ZRWfuu + yI3dITX/r02hZnDMuUrCT4W38VHhSYHckUs0NnpkkCKAL0CREgSo9w== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-01-30T06:13:01Z" + mac: ENC[AES256_GCM,data:IwZsL3uIw83Z3AflvpsqH0ML0VCUeTJT7AWzCDORFOxhyvWhzGGBnUHQiNOngKlepyV+WKclMOMpe7aHI/lMZXjA1cLiY22A2cNV6PCjKbnahzr2N7s6XyZ+9de0G7EIdR1fMR5aMECUR4Uwbb5AsOMVtO2wwhldBF3jn7pQV1o=,iv:wRY/RvhwFKECNSVt6xmGD6RWFPFuje58A9OLkmSL9II=,tag:cpBmf/1z1CnxGLY0WBvEQQ==,type:str] + pgp: + - created_at: "2024-01-30T06:12:12Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTAQ//QgyjJi1IFK+0ybpoeXE5nAbjXxJipG9viMhPhh2PD4wc + ZgCynE+sox+vzA0mzK72z+RqLAZctCafISjTzpDIxupz4HBQHjeWGHeZH/RqH+3Y + x0Z75Cv8G9n4S9SyDaMPN4dWC+LJ8SP2oMZvQ7G0HcZntHdWH7lcxQfw1WvbBx89 + obZ+duliV3QFsBMYT/Yci2z1mgJ93SIhFRBVv1F3VLlSVGtB/6uWNKgAtvgD9jls + VPO6XuUMIdyv+vv1nZ8n2gBOskhoyowX08w+ztDiMure1kS5LgsDxm7alx2eywip + HIqxpTTp0HTcWy0RIVlv0tnl00PzVRZ63KKGRaTmrNIfGHdE+qpSJdKWd2SuCimR + Zje7mOTPtqcE2TnTy2auWgRgOHaw1Js52Nwod15Z+3XotumMfFdIbEvbFOoew3Im + gbj7/YeTML2BAiAE7VnzVMTelB6ElqcFM/ZDfHCxFM3iWby5XZGyF90rBk3v/Z7T + tc+o+ImyvVPjFDJs0nizNDwEtTOJKCyA+KHfznzRw1qRYrSwUVpYQB7q8TvH5IcS + f2s7rySqpNp+79XMRPYoimw5tFebhR+IgSBZ/VdjwWLBXaebAdu8Sf3FtfLvGnD+ + CujOYuq/6t+EpSsgAOH3D7QZ9BlFwPFA762sSBqa92VEm1BULDZpZ3nB4qsT0/+F + AgwDC9FRLmchgYQBEACfOZetLUBqqa7Cgb6b+DZig+PuLHaCq9p6LqQPFmP4KL7M + /04xEAvL+2Tz+PhuNY3YtkimTeue2vZLJD6hHfxCYJO/GDr+ea2Rv9g23FvEFulw + klqRoDYCgjHp+Uk+9ux0fS8FJsDmQ8ZBPClx/OPGQTdQJ7sXB0AF+FT4TJY7gQP0 + 7+kcFbUwkXHH8EBw8tTnQHakPd1AIj2EVkMTAlU9mlZcJYmoCjSrH511zz94eQnR + L1J66vckCCdLPhrOq+NI8LTTr4ypjHRmJwW9TBcfamdXnsxZBrl1QTq3AAZURRPL + K7Bo6mWHJ7QBNNUnY1bKwpcY7zss7KzKklgPLuG+GxLZHfyKBMOe6Y/xLvfbtZ4u + sOdZSTrgruU7tuejPRlgP6eyh+yE9MSLJ0p4g0jkKf7qngA1Ec1IO21m7KnmqQ3a + Gr6+rI0K0FCUIf5q412OLg1cYixmlqb0Zfi566rJPYoHEoRhWUsP2ndvizNyULPJ + ocCaxEEV/kGk8vUwnSsb6EKfJxo3P0Xp4uFQ1SAhexbLXrqqlaLGYxulrXgwwgnf + 55Abk6B+O7RL3V401Phn4vRfzrSajzlXDUwFz+TPy6VMY+yx+FweIedjlJFTMtuX + 3EUyYyl6fuvAd7qmN6I1HlwVEJFEeMRKKxt+ufGj1m6fiqLvcus8Xw4r40zDO9Je + ATeO4b//yaoIs2lAshj2p97mVpU4xKzd48NOmuaZn+Y7/3m5xXk6vdb2dK2Gky/V + gSf+7TfGBdOOn9zlKxmuf9Q+HOyR/AbCibXUvhL6Ni9wqRHu4B0P6jIrTg1NCg== + =v009 + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/secrets/sync/secrets.yaml b/secrets/sync/secrets.yaml new file mode 100644 index 0000000..3a9182e --- /dev/null +++ b/secrets/sync/secrets.yaml @@ -0,0 +1,53 @@ +swarsel: ENC[AES256_GCM,data:WzMlNzg5iAu823s=,iv:U8ZutlrzBqq7z445kSnvluejtta4X/0YMIIOdcQuftg=,tag:IE0WMuXlNwnBHzXtrbVHKA==,type:str] +dnstokenfull: ENC[AES256_GCM,data:hxgxSm6pcXOEHZHdSwQkfZryFccQXrCu9idULJhWK/tQ44FyRIU4Yg==,iv:ObKf1M1qkgCltkKJX+URaPSiK5Itd3xlfBXPjf1iVak=,tag:PASR0pgBdcDYjdTZ2eEUCg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1glge4e97vgqzh332mqs5990vteezu2m8k4wq3z35jk0q8czw3gks2d7a3h + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyWGpWaXR6aytqTEM1YzQ2 + c3R6SjhWNGo1NnlNY1I0WmpkRWVSQno0cjBnCm1OQ0lhc2xYUHZza2w0R2s2OEkr + VlVSdkZSNW5iclB4N29Fbnl2QTM0cm8KLS0tIEtJbmdKOThaR0ZRaWovZ243UXBW + ckc3MmZZTVFyRGFaTVlVM29ra3JkbzQKNYhBd9oMS/jn2Oqyx9LpqJjQUx2mEZ/B + aUNbenlCRr8KCc+yBXuTyI31LPXJ3YHsI1cfp1K/LGzCY2F0uk4rSw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-02-01T01:06:53Z" + mac: ENC[AES256_GCM,data:pjpS4BX6PJtGl7d5QAR5vRkuzLceGWUEWIzvCG5NTdz6TX5STHZqjX7V+DEKUbNG8HWTMAqUokr+2uHT+hQ5RxCVYMZeEfUgVqcxpxJHoGHcs+iwLDghqlh3jEZInSVXqhD4UP8A0JSGLtAEBc5o3bELp7pTEw8mLq4trEm4RvM=,iv:hYpEG7ws8RKYcrbeOmH7m4pcZ/F4nIQY4wb6RW3BpR0=,tag:tsAIE4Px9ZPIJ2l/KQUmXQ==,type:str] + pgp: + - created_at: "2024-02-01T01:06:36Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTAQ/9El05oLwUKeG5pg1Ppyn8y+XEqXwbU4IQ/3HbtvdZT8iH + 5sywZNP9dtEV0nzEbJ3TR7W+u8Kx/4/EL1DpsUtEefn8XIbeuTb1guX7J6EYzda1 + xCCoaUg03iC6xKq3sDvOU86K5qLCoTrxm00DtHHWH8p6flHTdkLKzP/7AWQpu31v + tDLJ5vdJbdRvMBz8XMQ+wuWiTdiVvlmY+6HHuusF8LXx+uj4g2NI2u/bBJ9HIVYI + 1NLdIlyvdD7yn5YJSrUgmdH8IXsbnpBIJAjgT8TSZugfUlK3znv1ZB/x4O/h3JO1 + LTxm/1wRG8BN+YqLzNfyF7bk/4dTWYNJK0x/AetvLEqbrmo6e00R7Sb+zAA8aeSI + K3QEw4ayTsF7vGK1W5QTjSaYJqucyV1VaW/Heb3ZZSLGRdrZN4jO9q6xHnW6wMMt + GYg100+GNkEHPRzGDDz6fpY2gDsTXKgO2+GUSOLeHmoAkiIAwsvrpUO5ocrqKmrM + BzMKKo4xjr+5oCAwopWLYSFqHKBju6Puth7VL4JhCPt5WyKB7Nup7JMRuupzBZRG + 31DyD5nCsgbBeC5fa9Tvv9OVq30UOqUtJOCh5/IWFSBxuMfIPC1wb7zgP6obDZre + 6gX+a5XHW5c9wSV2/yP6z5UxCkaOTE1P58Qq/A4wx2XvIDN7jw/l/blni2CYj2aF + AgwDC9FRLmchgYQBD/4hBYKfky8PA5l2cY3xMz27bxVPPoH5jJJ/vSz96EOpQeos + gvksKb6PPngWpQqByHKnLVQgHoR+J1TkYxx69vInR+tMjzvTgJdWnmsn0Qs3oUZ8 + mnm27GnsvhvzvaTTMzEdgP32mVAH2WEoqOeSGtVUAyOeQqvehCK3hS/M6WcKX6DO + 0m1IkMqqMp3qweyunhgt0HOHbj04Ecw7B0vmlqezc7UF/Sr70Sv1v/u3FKFKyh1+ + Ti+I//Fx6X/q52SDEY7Btn12r/xUW3zDm5XeaXtOJbL9TaNnpIgEI9X6BhisajA9 + OKuvhtcG5Y+TjD0/o2Wm2AwI8FriwV7NalC4cpNOtDMVxaFMMY55JIvHrubtjCJP + xaObMyDmKceo2bG69Ht6NrITCl+6j4GaD2Iptun5EmgYH6Gc3tDZ4swxwJD8RU5L + qrY1pRWO/ddOA2V5sEesiJoJWB7ZuTjrf5JP0/aT8IIH9qEpIi4MRbvBdLYKDTlH + jhUnLkb2OLQMfolMP9EsnNyw7WOP+olbhlsjZhoSt85N7kXRYErv9TI8wb8s+A/V + fl2DDFkdezY6LdcSW5jmnwQtsgNL0httwOAXapJmpGyS57V3BCgE3nTTWrXnvyqw + uJIz5DuZVT4Bagqp8GBSLPn9bquHtLssKLofIOF+wr6lp7Jm2AdYcRfFSZ4jxtJc + AdY98vAQJxkzQ+2L6YJ/t/hEh4rXUkUeQJpdMwQ786FpMamJAqHPZ9k7PF0rKipH + 82p4T6nqBSKD96YiXCxU27D5t9suBeWhXNhkWIW/a5ujJQPP6q3Qy49vopU= + =NNT2 + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/secrets/transmission/secrets.yaml b/secrets/transmission/secrets.yaml new file mode 100644 index 0000000..afa4a5e --- /dev/null +++ b/secrets/transmission/secrets.yaml @@ -0,0 +1,57 @@ +vpnuser: ENC[AES256_GCM,data:7wytXrH3c6s=,iv:yoaWl5NCFuF/Ic2nkFXpvSZj9fQCHRtzKOHef+EEolQ=,tag:jzX5ewkmAHZhJMaq2ke90w==,type:str] +rpcuser: ENC[AES256_GCM,data:lO3735Ynaw==,iv:PDhpAifNEjKpZk2slowOqVUXxaVup+ZLrvGPq80RV40=,tag:8sb8PxZrEVnxhFIRu+Q/FA==,type:str] +vpnpass: ENC[AES256_GCM,data:pTnZjMu+fCJMOQ==,iv:aKLOtjJlXsr0uy+5OrcMxMBqaU8vwaG2Vcn6SirbYas=,tag:Pv2D8Qn+a7ihz16jSkUTbg==,type:str] +rpcpass: ENC[AES256_GCM,data:nknsULbLZMo68c2P7lmWBEZcyaLqDXrU,iv:1NUnew6AL9kmBTnLTXgwA8cm2AO85He0I2fP2oXhrdA=,tag:G7YgBNR7h7QmukVQLhG1pg==,type:str] +vpnprot: ENC[AES256_GCM,data:Ue2A,iv:NcYpWxPWhIKewOde7kYS4TJnipnADLq9+7Pb/l0xgkU=,tag:ACoL3u2gPHXaM2HlW9Msaw==,type:str] +vpnloc: ENC[AES256_GCM,data:X83semtc/SINDnJblMZduEO6UhSTUeziJRHO,iv:9u4ddDGisgDLlwQGQRL0AZHo1mPD15s6+X3qn9gDf4M=,tag:OeLdoXIDrfxJesJwCEWI2Q==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1wevwwytv5q8wx8yttc85gly678hn4k3qe4csgnq2frf3wxes63jqlt8kqs + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNc2F6RlBjTDRLcU5FYlhQ + c3ppdFI5REZpazc1cnFubE5iaUx1bEFDQUdFCmM5R2orbVUrSDI4bzgxSWt6amlJ + NkdBQkM3b0RWSVFuSE9GM1NsNWJuS2MKLS0tIGtWdi9Qd1BSTjduWCs2dWViQUlZ + UCtqdEMxZmIwVk5wY3RGUU50NUNMY1kKuCCh64itbGbWc1DrxV3BupImnZoIuqga + eC8BcM+CjEmeXDb+tAo81OADCVnnfH4UP1gJ5hHxn5rF7/zOkljXeA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2023-12-28T23:15:15Z" + mac: ENC[AES256_GCM,data:BS7Ma6oSrJxEMYHbCtEDwVePqIhgmgdWchUyVZhf/dlg2JJfE87gr9jDZrlGPmZM9pUD/gDm8VO1wtOLx61jpII5m1bfSfq6O8XEOQ+cLrJDHHaDo0JTF/TOWWpWPEbnNLpa+BjUb75aAX5Y8+Dw0yAqIRIGNxoanTbyh3NuZyk=,iv:gS4xdaF3DWaJ1bYBBmHgXfHrnr7diw0jtWVYR715RGo=,tag:LXDDCP7k/C660h2AzbuxCw==,type:str] + pgp: + - created_at: "2023-12-28T23:14:56Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTAQ//WKTeke13O+mZdeXrSaxQWQQ3gex86o8+hIDdkKxvUR/x + bATUaNh0GGU5ovosDFEIWM9F4FQzmAidYKl0i0j9zsR8tIj+0JH1ahmL0oxM/xbC + sh8/IKczRsPQjZYrO7g4fH8Is2d8zFh8nWIEwDuEQ1/TB6hzip4npKcLlp3hqsLx + 5/SwQvSSh3q6iND0YFLGYKwtlNV3ZhcTU0TeaOYJTmzaWU/XjsvTL3lOcHpVtXsY + IK6/b5bitB28zR5J1h3zpHiza+OabAlG/hXHkL5q9NDb1qkmkgxZfbBC9qJiu0ke + rBX5pPYF1yDZF/3QmCmsId3Rx0vEKJzw3vdkqHje4se9zNbnXJ968Gva/3QJJF81 + iYtetvO4/v3BR7xCwfpW4V6CnNQ+N87T0N6jC8racl/GeCiFmSvSUmOktN06AHBm + JO6Ie9vmzmnVH+sKMvRtFETzkqcxCsgNhPnvnGqzDZFf15nJAtRjzYTjMrrIEyTO + MC3V0GMOrdkorRPBbCcXDYC54sorbWuVdjxSX+pElvFO6XKhZJSfnZO2/QHlWU87 + 1EvyO0jS7314vwksrU0965nQ+uJA3lLZpIK7ZivoOK94Oo7lqi+IVbyUOKeQTRBe + USITZY8gTcGDvTGBkAPVBEIJMHOu5gMdPh4wbVwzGXwItSGfXOvh/SaL47AJ9QCF + AgwDC9FRLmchgYQBD/9ZsUCvmiC6BbkHBOSCpyksC1+GQ0k3jUMV6VYt/tIqt6a0 + ILU9Bw+jkOfo7i+t/7uTp7wXyb2JwsohiP/YreYDOOCHxXdJJExBI/tOXoS7nKC+ + dBjKwIz4BPDQz/1METECNoB3v23iUr+GeKTI9gjOO5btEh7UdiKO3inJttSRqVE8 + /kXN2kzSz9VY7W76h7JIMuqhmzorrn/FmSaZWZ4cWW8wvgkQ9mf96dwJElXpZ6I0 + JyPMPpnkd8UPI96MXJbrLToFdmTHgK2TQNmJKPACe2CEQFMvfADKpuC6vq+OckxD + oFZqr+jUsXXGXJUA9Zn59Pcw3FJDiDfJ/4/BqYgPx4IMU7pdp5UEj0PE02BhCPl4 + nkFHRqDA3oynBLFaKXAPa/ND4WLaF789V34RTOBvuiIpe9sGhI5gWhIuqW+eZfhA + Xtf9Wes+FDS+23K4zZ9aB6oTeV0W/JE1xAQ4NYCilrfsF3rwx/x4WYspe7WJjvGJ + e/LzfCaxna3gf6WyY+5Go/PI0JlicrEuPK/DWi5dTlgnx66mcskO0AIGG6a2syyZ + i4UvX/h165NbzoeswyHq3Mz4r/6WXpzO3Znb/pyfdAbifxatGtitm5X960/4tmFE + RZzig35VbhntomBSL2I/KBzEnrqwY+/nnf3DdjOJNbk9XWudaVRCnFJOSiuNFtJe + ASR5liWrbDKUkdnG2Pdqk//CYsg28xobBgVRG5roi3EuGFHkZCno0nC+r7e8Ad1A + l7CTOPtmhNr2RxNKbTzaYJDaivDmo9iILxOfX7i20m+DhtWTsPuML/LHfFc9hQ== + =Abzg + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/wallpaper/navidrome.png b/wallpaper/navidrome.png new file mode 100644 index 0000000000000000000000000000000000000000..4c94ca5c039cb93e52df95e966d953deb03c1b1b GIT binary patch literal 79385 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 z

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 I=akR{0CN-NOaK4? literal 0 HcmV?d00001