From c9f2c2b9df9c5509f7ea282c5968ab1f0f57b74a Mon Sep 17 00:00:00 2001 From: Alexander Tomokhov Date: Wed, 27 Dec 2023 01:49:13 +0400 Subject: [PATCH] from meow-corp.xyz --- alexoundos.nix | 69 ++++++++++++++++++++++++++++++ api/api-module.nix | 15 ++++++- configuration.nix | 16 ++++++- example-systemd-service.nix | 62 +++++++++++++++++++++++++++ letsencrypt/acme.nix | 2 +- netdata.nix | 50 ++++++++++++++++++++++ prometheus-grafana.nix | 47 +++++++++++++++++++++ resources/limits.nix | 14 +++---- stalwart.nix | 83 +++++++++++++++++++++++++++++++++++++ victoriametrics-grafana.nix | 62 +++++++++++++++++++++++++++ 10 files changed, 410 insertions(+), 10 deletions(-) create mode 100644 alexoundos.nix create mode 100644 example-systemd-service.nix create mode 100644 netdata.nix create mode 100644 prometheus-grafana.nix create mode 100644 stalwart.nix create mode 100644 victoriametrics-grafana.nix diff --git a/alexoundos.nix b/alexoundos.nix new file mode 100644 index 0000000..fac9363 --- /dev/null +++ b/alexoundos.nix @@ -0,0 +1,69 @@ +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + mc + nixpkgs-fmt + tcpdump + ]; + environment = { + shellAliases = { + cp = "cp --reflink=auto"; + diff = "diff --color"; + dmesg = "dmesg --time-format=iso"; + grep = "grep --color"; + }; + variables = { + HISTCONTROL = "ignoredups:ignorespace"; + HISTFILESIZE = "10000"; + HISTSIZE = "10000"; + TIME_STYLE = "long-iso"; + }; + etc."inputrc".text = '' + set colored-stats on + set bell-style none + set blink-matching-paren on + set editing-mode vi + set keyseq-timeout 0 + set show-mode-in-prompt on + set keymap vi-insert + set vi-ins-mode-string \1\e[6 q\2 + set vi-cmd-mode-string \1\e[2 q\2 + ''; + }; + + programs.neovim = { + enable = true; + vimAlias = true; + defaultEditor = true; + configure = { + packages.myPlugin = with pkgs.vimPlugins; { + #start = [ vim-nix ]; + start = [ vim-lastplace vim-nix nerdtree ]; + #opt = [ YouCompleteMe ]; + }; + customRC = '' + set nocompatible + set tabstop=8 + set expandtab + set shiftwidth=4 + if $TERM == 'linux' + hi Visual cterm=reverse + endif + " au FileType nix exec 'syntax clear nixString' + ''; + }; + }; + programs.htop.enable = true; + programs.tmux.enable = true; + + #systemd.services.netdata.serviceConfig = { + # ExecStartPre = "${pkgs.tmux}/bin/tmux -S /run/netdata/tmux.socket new-session -s my-session -d"; + # ExecStopPost = "${pkgs.tmux}/bin/tmux -S /run/netdata/tmux.socket kill-session -t my-session"; + #}; + + #systemd.services.phpfpm-nextcloud.serviceConfig = { + # #User = "nextcloud"; + # ExecStartPre = "${pkgs.tmux}/bin/tmux -S /run/phpfpm/tmux.socket new-session -s my-session -d"; + # ExecStopPost = "${pkgs.tmux}/bin/tmux -S /run/phpfpm/tmux.socket kill-session -t my-session"; + #}; +} diff --git a/api/api-module.nix b/api/api-module.nix index 2ec0509..b416a01 100644 --- a/api/api-module.nix +++ b/api/api-module.nix @@ -8,6 +8,7 @@ let if cfg.direction == "" then "" else "--direction=${cfg.direction}"; + api-user = "sp-api-user"; in { options.services.selfprivacy-api = { @@ -21,11 +22,21 @@ in }; config = lib.mkIf cfg.enable { + users.users = { + ${api-user} = { + group = api-user; + isSystemUser = true; + createHome = false; + }; + }; + users.groups.${api-user} = { }; + + systemd.services.selfprivacy-api = { description = "API Server used to control system from the mobile application"; environment = config.nix.envVars // { inherit (config.environment.sessionVariables) NIX_PATH; - HOME = "/root"; + #HOME = "/root"; PYTHONUNBUFFERED = "1"; } // config.networking.proxy.envVars; path = [ @@ -50,6 +61,8 @@ in after = [ "network-online.target" ]; wantedBy = [ "network-online.target" ]; serviceConfig = { + # FIXME + # User = api-user; User = "root"; ExecStart = "${pkgs.selfprivacy-graphql-api}/bin/app.py"; Restart = "always"; diff --git a/configuration.nix b/configuration.nix index 29e803c..1af5f44 100644 --- a/configuration.nix +++ b/configuration.nix @@ -2,6 +2,12 @@ let url-overlay = "https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nix-repo/archive/22-11-backups.tar.gz"; nix-overlay = (import (builtins.fetchTarball url-overlay)); + nixos-unstable-path = + builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/c757e9bd77b16ca2e03c89bf8bc9ecb28e0c06ad.tar.gz"; + sha256 = "04msycqlccsk1wa78syc4l60557iia6yvarp5pvp0qn1j55mq9f5"; + }; + nixos-unstable = import nixos-unstable-path {}; in { imports = [ @@ -11,7 +17,7 @@ in ./files.nix ./volumes.nix ./users.nix - ./mailserver/system/mailserver.nix + #./mailserver/system/mailserver.nix ./vpn/ocserv.nix ./api/api.nix ./api/api-module.nix @@ -25,6 +31,14 @@ in ./resources/limits.nix ./videomeet/jitsi.nix ./git/gitea.nix + + ./alexoundos.nix + #./prometheus-grafana.nix + #./victoriametrics-grafana.nix + ./netdata.nix + #./example-systemd-service.nix + "${nixos-unstable-path}/nixos/modules/services/mail/stalwart-mail.nix" + (import ./stalwart.nix nixos-unstable) ]; nixpkgs.overlays = [ (nix-overlay) ]; diff --git a/example-systemd-service.nix b/example-systemd-service.nix new file mode 100644 index 0000000..cdf8b41 --- /dev/null +++ b/example-systemd-service.nix @@ -0,0 +1,62 @@ +{ pkgs, ... }: +let + service-name = "example-service"; + user = "example-service-user"; +in +{ + users.users = { + ${user} = { + group = user; + isNormalUser = true; + createHome = false; + }; + }; + users.groups.${user} = { }; + + systemd.services.${service-name} = { + serviceConfig = { + User = user; + Group = user; + + # Runtime directory and mode + RuntimeDirectory = service-name; + RuntimeDirectoryMode = "0750"; + # State directory and mode + StateDirectory = service-name; + StateDirectoryMode = "0750"; + # Cache directory and mode + CacheDirectory = service-name; + CacheDirectoryMode = "0750"; + # Logs directory and mode + LogsDirectory = service-name; + LogsDirectoryMode = "0750"; + # Configuration directory and mode + ConfigurationDirectory = service-name; + ConfigurationDirectoryMode = "0755"; + + # Sandboxing + ProtectSystem = "full"; + ProtectHome = "read-only"; + PrivateTmp = true; + ProtectControlGroups = true; + PrivateMounts = true; + + ExecStart = "${pkgs.tmux}/bin/tmux -S /run/${service-name}/tmux.socket new-session -s my-session -d"; + ExecStop = "${pkgs.tmux}/bin/tmux -S /run/${service-name}/tmux.socket kill-session -t my-session"; + Type = "forking"; + }; + + #confinement.enable = true; + }; + + networking = { + firewall = { + extraCommands = '' + iptables -t filter -I OUTPUT 1 -m owner --uid-owner ${user} -m state --state NEW -j REJECT + ''; + extraStopCommands = '' + iptables -t filter -D OUTPUT 1 -m owner --uid-owner ${user} -m state --state NEW + ''; + }; + }; +} diff --git a/letsencrypt/acme.nix b/letsencrypt/acme.nix index e38d67e..05e2841 100644 --- a/letsencrypt/acme.nix +++ b/letsencrypt/acme.nix @@ -5,7 +5,7 @@ let in { users.groups.acmerecievers = { - members = [ "nginx" "dovecot2" "postfix" "virtualMail" "ocserv" ]; + members = [ "nginx" "dovecot2" "postfix" "virtualMail" "ocserv" "stalwart-mail" ]; }; security.acme = { acceptTerms = true; diff --git a/netdata.nix b/netdata.nix new file mode 100644 index 0000000..2c24ad2 --- /dev/null +++ b/netdata.nix @@ -0,0 +1,50 @@ +{ config, pkgs, ...}: +let + domain = config.services.userdata.domain; +in +{ + services.netdata = { + enable = true; + package = pkgs.netdata.override { + withCloud = false; # don't need Netdata Cloud integration + withSsl = false; # we proxy-pass via nginx, which does SSL + }; + config = { + #global = { + # "default port" = 19191; + # "page cache size" = 96; + #}; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + #why not use "enableACME"? + #sslCertificate = "/var/lib/acme/${domain}/fullchain.pem"; + #sslCertificateKey = "/var/lib/acme/${domain}/key.pem"; + + #root = "/var/www/social.${domain}"; + #forceSSL = true; + #extraConfig = '' + # add_header Strict-Transport-Security $hsts_header; + # #add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always; + # add_header 'Referrer-Policy' 'origin-when-cross-origin'; + # add_header X-Frame-Options DENY; + # add_header X-Content-Type-Options nosniff; + # add_header X-XSS-Protection "1; mode=block"; + # proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; + # expires 10m; + #''; + locations = { + "/netdata/" = { + proxyPass = "http://127.0.0.1:19999/"; + }; + }; + }; + # TODO Netdata must communicate with nginx via unix domain socket as + # described here: https://learn.netdata.cloud/docs/configuring/securing-netdata-agents/reverse-proxies/nginx#limit-direct-access-to-netdata + + systemd.services.netdata.serviceConfig = { + IPAddressDeny = "any"; + IPAddressAllow = "localhost"; + }; +} diff --git a/prometheus-grafana.nix b/prometheus-grafana.nix new file mode 100644 index 0000000..4dd2aad --- /dev/null +++ b/prometheus-grafana.nix @@ -0,0 +1,47 @@ +{ + services.prometheus = { + enable = true; + exporters = { + node = { + enable = true; + enabledCollectors = [ "systemd" ]; + }; + systemd = { + enable = true; + }; + process = { + enable = true; + #settings.process_names = []; + }; + }; + scrapeConfigs = [{ + job_name = "nodes"; + static_configs = [{ + targets = [ + "127.0.0.1:9100" # node exporter + "127.0.0.1:9558" # systemd exporter + "127.0.0.1:9256" # process exporter + ]; + }]; + }]; + }; + services.grafana = { + enable = true; + provision = { + enable = true; + datasources.settings.datasources = [{ + name = "Prometheus"; + type = "prometheus"; + access = "proxy"; + url = "http://127.0.0.1:9090"; + }]; + }; + settings = { + server = { + http_port = 30000; + #domain = "meow-corp.xyz"; + domain = "localhost"; + }; + }; + }; +} diff --git a/resources/limits.nix b/resources/limits.nix index 5d341cb..2b08c79 100644 --- a/resources/limits.nix +++ b/resources/limits.nix @@ -35,13 +35,13 @@ }; nginx = { serviceConfig = { - cpuAccounting = true; - cpuQuota = "70%"; - memoryAccounting = true; - memoryMax = "768M"; - startLimitIntervalSec = 500; - startLimitBurst = 5; - blockIOWeigth = 10; + CpuAccounting = true; + CpuQuota = "70%"; + MemoryAccounting = true; + MemoryMax = "768M"; + StartLimitIntervalSec = 500; + StartLimitBurst = 5; + BlockIOWeigth = 10; }; }; }; diff --git a/stalwart.nix b/stalwart.nix new file mode 100644 index 0000000..86a988a --- /dev/null +++ b/stalwart.nix @@ -0,0 +1,83 @@ +nixos-unstable: { config, ... }: +let + #certs = import "${nixos-unstable.path}/nixos/tests/common/acme/server/snakeoil-certs.nix"; + #domain = certs.domain; + domain = config.services.userdata.domain; +in +{ + networking.firewall.allowedTCPPorts = [ 143 587 ]; + + #security.pki.certificateFiles = [ certs.ca.cert ]; + + services.stalwart-mail.enable = true; + services.stalwart-mail.package = nixos-unstable.stalwart-mail; + services.stalwart-mail.settings = { + server.hostname = domain; + + certificate."meow" = { + #cert = "file://${certs.${domain}.cert}"; + #private-key = "file://${certs.${domain}.key}"; + cert = "file:///var/lib/acme/${domain}/fullchain.pem"; + private-key = "file:///var/lib/acme/${domain}/key.pem"; + }; + + server.tls = { + certificate = "meow"; + enable = true; + implicit = false; + }; + + server.listener = { + "smtp-submission" = { + bind = [ "0.0.0.0:587" ]; + protocol = "smtp"; + }; + + "imap" = { + bind = [ "0.0.0.0:143" ]; + protocol = "imap"; + }; + }; + session.auth.mechanisms = [ "PLAIN" ]; + session.auth.directory = "in-memory"; + jmap.directory = "in-memory"; # shared with imap + + session.rcpt.directory = "in-memory"; + queue.outbound.next-hop = [ "local" ]; + + directory."in-memory" = { + type = "memory"; + users = [ + { + name = "alice"; + secret = "BAAfdWJ2"; + email = [ "alice@${domain}" ]; + } + { + name = "bob"; + secret = "6eeuHZS3"; + email = [ "bob@${domain}" ]; + } + ]; + }; + + #auth.dkim = { + # #sign = [ { if = "listener"; ne = "smtp"; then = ["rsa"]; } + # # { else = ["rsa"]; } ]; + # sign = [ "rsa" ]; + #}; + #signature."rsa" = { + # private-key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4xFGe+tfbZbYTvDPTnoTGiV8NUOY1747fBK04X0VriBN/taRbiqyL/rzczErCKBL+R2Hr6A3ptS+zDWN/7L/PJw3QWhB5M5YWQTdMKYLXwmQlldGmp107iKzVpg2m3Qv4ipXgrzkSDLbt/snf77sCPOGZNp2SJ5DOzyKETOq0RwIDAQAB"; + # domain = "${domain}"; + # selector = "rsa_default"; + # headers = ["From" "To" "Date" "Subject" "Message-ID"]; + # algorithm = "rsa-sha256"; + # canonicalization = "relaxed/relaxed"; + # expire = "10d"; + # set-body-length = false; + # report = true; + #}; + + }; +} + diff --git a/victoriametrics-grafana.nix b/victoriametrics-grafana.nix new file mode 100644 index 0000000..7eebde6 --- /dev/null +++ b/victoriametrics-grafana.nix @@ -0,0 +1,62 @@ +let + prometheus-yaml = builtins.toFile "prometheus.yml" '' + "scrape_configs": [ + { + "job_name": "nodes", + "static_configs": [ + { + "labels": {}, + "targets": [ + "127.0.0.1:9100", + "127.0.0.1:9558", + "127.0.0.1:9256" + ] + } + ] + } + ] + ''; +in +{ + services.prometheus = { + enable = false; + exporters = { + node = { + enable = true; + enabledCollectors = [ "systemd" ]; + }; + systemd = { + enable = true; + }; + process = { + enable = true; + #settings.process_names = []; + }; + }; + }; + services.victoriametrics = { + enable = true; + extraOptions = [ "-promscrape.config=${prometheus-yaml}" ]; + }; + services.grafana = { + enable = true; + provision = { + enable = true; + datasources.settings.datasources = [ + { + name = "Victoriametrics2"; + type = "prometheus"; + access = "proxy"; + url = "http://127.0.0.1:8428"; + } + ]; + }; + settings = { + server = { + http_port = 30000; + #domain = "meow-corp.xyz"; + domain = "localhost"; + }; + }; + }; +}