commit e46461326dfe7a3fb6af6beed0c3547cce019acb Author: Stuce Date: Thu Nov 13 16:01:47 2025 +0100 first commit diff --git a/authelia.nix b/authelia.nix new file mode 100644 index 0000000..7be82d0 --- /dev/null +++ b/authelia.nix @@ -0,0 +1,118 @@ +let host = "stuce.ch"; +in { + services.authelia.instances."main" = { + enable = true; + secrets = { + oidcHmacSecretFile = "/var/lib/authelia-main/hmac"; + oidcIssuerPrivateKeyFile = "/var/lib/authelia-main/rsa.2048.key"; + jwtSecretFile = "/var/lib/authelia-main/jwtSecret"; + storageEncryptionKeyFile = + "/var/lib/authelia-main/storageEncryptionKeyFile"; + }; + environmentVariables = { + AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = + "/var/lib/authelia-main/ldap_password"; + AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = + "/var/lib/authelia-main/smtp_password"; + }; + settings = { + authentication_backend = { + password_reset = { disable = false; }; + refresh_interval = "1m"; + ldap = { + implementation = "lldap"; + address = "ldap://localhost:3890"; + base_dn = "dc=stuce,dc=ch"; + user = "uid=bind_user,ou=people,dc=stuce,dc=ch"; + additional_users_dn = "ou=people"; + }; + }; + storage = { local = { path = "/var/lib/authelia-main/db.sqlite3"; }; }; + session = { + cookies = [{ + domain = "${host}"; + authelia_url = "https://auth.${host}"; + default_redirection_url = "https://${host}"; + }]; + }; + + notifier = { + # disable_startup_check = false; + # filesystem = { filename = "/var/lib/authelia-main/notification.txt"; }; + smtp = { + address = "submissions://mail.infomaniak.com:465"; + username = "no-reply@stuce.ch"; + sender = "no-reply@stuce.ch"; + }; + }; + + identity_providers = { + oidc = { + jwks = { }; + clients = [{ + client_id = + "uZzbagYr2RZqyVFsOZ23XJktE9tjllwuMnxiicfT5Ykcx6pX9JZDXlYahdg.DRq5guobvv0k"; + client_name = "Forgejo"; + # TODO: find a way to remove this secret from here + client_secret = + "$pbkdf2-sha512$310000$xmkdkyjsCmFEi2U.exnetg$UYnG78UZhBY/6Fb8ztGsKdJDtJdIRUrki4bngIPnsOfKxddzQovCDZ8AWETFim0Nar5AhbecJJTEM1kf2lG3WQ"; + public = false; + authorization_policy = "two_factor"; + require_pkce = true; + pkce_challenge_method = "S256"; + redirect_uris = + [ "https://git.stuce.ch/user/oauth2/authelia/callback" ]; + scopes = [ "openid" "email" "profile" ]; + response_types = [ "code" ]; + grant_types = [ "authorization_code" ]; + access_token_signed_response_alg = "none"; + userinfo_signed_response_alg = "none"; + token_endpoint_auth_method = "client_secret_basic"; + }]; + }; + }; + + access_control = { + default_policy = "deny"; + rules = [ + { + domain = "todo.Stuce.ch"; + policy = "one_factor"; + # resources = ["^/.*"]; + subject = [ "group:maison" ]; + } + { + domain = "armee.stuce.ch"; + policy = "one_factor"; + resources = [ "^/todo.*" ]; + subject = [ "user:sgt" ]; + } + { + domain = "vault.stuce.ch"; + policy = "two_factor"; + resources = [ "^/admin.*" ]; + subject = [ "user:stuce" ]; + } + { + domain = "ldap.stuce.ch"; + policy = "two_factor"; + subject = [ "user:stuce" ]; + } + { + domain = "armee.stuce.ch"; + policy = "one_factor"; + resources = [ "^/notes.*" ]; + subject = [ "group:ada" "group:cader" ]; + } + { + domain = "armee.stuce.ch"; + policy = "one_factor"; + resources = [ "^/plans.*" ]; + subject = [ "user:lt" ]; + } + + ]; + }; + }; + }; +} diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..81d4079 --- /dev/null +++ b/configuration.nix @@ -0,0 +1,89 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page, on +# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). + +{ config, lib, pkgs, ... }: + +{ + imports = [ # Include the results of the hardware scan. + ./hardware-configuration.nix + ./nginx.nix + ./forgejo.nix + ./authelia.nix + ./lldap.nix + ./vaultwarden.nix + ./radicale.nix + ]; + # Add flake support + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + # Use the GRUB 2 boot loader. + networking.usePredictableInterfaceNames = true; + users.users.stuce = { + isNormalUser = true; + home = "/home/stuce"; + description = "Stuce"; + extraGroups = [ "wheel" ]; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBcOyttPiox/YITnjSXzUXkuaeOySy6D+wzBiqSwE9AL lefabricesaucy@outlook.com" + ]; + }; + + boot.loader.grub.enable = true; + # boot.loader.grub.efiSupport = true; + # boot.loader.grub.efiInstallAsRemovable = true; + # boot.loader.efi.efiSysMountPoint = "/boot/efi"; + # Define on which hard drive you want to install Grub. + boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only + + networking.hostName = "nixos"; # Define your hostname. + + # Set your time zone. + time.timeZone = "Europe/Amsterdam"; + + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; [ + # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. + wget + sqlite + git + htop + ]; + programs.neovim.enable = true; + programs.neovim.defaultEditor = true; + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + # programs.mtr.enable = true; + # programs.gnupg.agent = { + # enable = true; + # enableSSHSupport = true; + # }; + + # List services that you want to enable: + + services.fail2ban.enable = true; + services.openssh = { + enable = true; + startWhenNeeded = true; + settings = { + PasswordAuthentication = false; + UsePAM = false; + PrintMotd = true; + PermitRootLogin = "no"; + KbdInteractiveAuthentication = false; + }; + }; + + # Open ports in the firewall. + networking.firewall.allowedTCPPorts = [ 80 443 ]; + # networking.firewall.allowedUDPPorts = [ ]; + + system.stateVersion = "25.05"; + + environment.etc."motd" = { + text = '' + Successfully connected so stuce's vps ! + ''; + }; +} + diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..40bf56a --- /dev/null +++ b/flake.nix @@ -0,0 +1,40 @@ +{ + description = "Config flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; + + # nvf = { + # url = "github:notashelf/nvf"; + # inputs.nixpkgs.follows = "nixpkgs"; + # }; + + # home-manager = { + # url = "github:nix-community/home-manager"; + # inputs.nixpkgs.follows = "nixpkgs"; + # }; + # sTodo = { + # url = "git+https://git.stuce.ch/stuce/sTodo"; + # }; + }; + + outputs = { self, nixpkgs, + # home-manager, + # nvf, + sTodo, ... }: { + nixosConfigurations.nixos = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = nixpkgs; + modules = [ + ./configuration.nix + # nvf.nixosModules.default + sTodo.nixosModules.sTodo + # home-manager.nixosModules.home-manager + # { + # home-manager.useGlobalPkgs = true; + # home-manager.users.stuce.imports = [./home.nix]; + # } + ]; + }; + }; +} diff --git a/forgejo.nix b/forgejo.nix new file mode 100644 index 0000000..32a5b91 --- /dev/null +++ b/forgejo.nix @@ -0,0 +1,20 @@ +{ + services.forgejo = { + enable = true; + settings = { + server = { ROOT_URL = "https://git.stuce.ch"; }; + openid = { + ENABLE_OPENID_SIGNIN = false; + ENABLE_OPENID_SIGNUP = true; + WHITELISTED_URIS = "auth.stuce.ch"; + }; + service = { + DISABLE_REGISTRATION = false; + ALLOW_ONLY_EXTERNAL_REGISTRATION = true; + SHOW_REGISTRATION_BUTTON = false; + }; + session = { COOKIE_SECURE = true; }; + + }; + }; +} diff --git a/hardware-configuration.nix b/hardware-configuration.nix new file mode 100644 index 0000000..13b85bf --- /dev/null +++ b/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 = [ (modulesPath + "/profiles/qemu-guest.nix") ]; + + boot.initrd.availableKernelModules = + [ "ata_piix" "virtio_pci" "virtio_scsi" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/25df457a-21d0-41ab-9de5-88ffc00e3469"; + fsType = "btrfs"; + }; + + fileSystems."/efi" = { + device = "systemd-1"; + fsType = "autofs"; + }; + + 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/lldap.nix b/lldap.nix new file mode 100644 index 0000000..34dd3dc --- /dev/null +++ b/lldap.nix @@ -0,0 +1,23 @@ +{ + services.lldap = { + enable = true; + settings = { + ldap_base_dn = "dc=stuce,dc=ch"; + # forces localhost only ! + ldap_host = "127.0.0.1"; + http_host = "127.0.0.1"; + ldap_user_email = "admin@example.com"; + ldap_user_dn = "stuce_admin"; + ldap_key = ""; # so warning shuts up ! + # NOTE: this is in case of oopsies ! + force_ldap_user_pass_reset = false; + # TODO: jwt file + user password + key seed script to gen + + }; + environment = { + LLDAP_JWT_SECRET_FILE = "/var/lib/lldap/jwt_secret"; + LLDAP_LDAP_USER_PASS_FILE = "/var/lib/lldap/user_password"; + LLDAP_KEY_SEED = "/var/lib/lldap/key_seed"; + }; + }; +} diff --git a/nginx.nix b/nginx.nix new file mode 100644 index 0000000..dbed4d4 --- /dev/null +++ b/nginx.nix @@ -0,0 +1,265 @@ +{ config, pkgs, ... }: +let host = "stuce.ch"; +in { + security.acme = { + acceptTerms = true; + defaults.email = "lefabricesaucy@outlook.com"; + }; + services.nginx = { + enable = true; + # Use recommended settings + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + # Only allow PFS-enabled ciphers with AES256 + sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL"; + + virtualHosts."cal.${host}" = { + enableACME = true; + forceSSL = true; + extraConfig = + " rewrite ^/.well-known/carddav / redirect;\n rewrite ^/.well-known/caldav / redirect;\n "; + locations."/" = { + proxyPass = "http://localhost:5232"; + # extraConfig = '' + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Host $host; + # proxy_set_header X-Forwarded-Port $server_port; + # proxy_set_header X-Forwarded-Proto $scheme; + # proxy_set_header Host $http_host; + # proxy_pass_header Authorization; + # ''; + }; + }; + virtualHosts."eink.${host}" = { + forceSSL = true; + sslCertificateKey = "/etc/nginx/certs/ca.key"; + sslCertificate = "/etc/nginx/certs/ca.crt"; + extraConfig = "ssl_client_certificate /etc/nginx/certs/client.crt;"; + locations."/" = { + # TODO fastcgi to the script + extraConfig = "return 200 'handshake worked !!!';"; + }; + }; + + virtualHosts."vault.${host}" = { + enableACME = true; + forceSSL = true; + extraConfig = "include /etc/nginx/snippets/authelia-location.conf;"; + locations."/" = { + proxyPass = "http://127.0.0.1:${ + toString config.services.vaultwarden.config.ROCKET_PORT + }"; + }; + locations."/admin" = { + extraConfig = '' + include /etc/nginx/snippets/proxy.conf; + include /etc/nginx/snippets/authelia-authrequest.conf; + ''; + proxyPass = "http://127.0.0.1:${ + toString config.services.vaultwarden.config.ROCKET_PORT + }"; + }; + }; + + virtualHosts."armee.${host}" = { + root = "/var/www/armee"; + forceSSL = true; + enableACME = true; + extraConfig = "include /etc/nginx/snippets/authelia-location.conf;"; + + locations."/plans" = { + extraConfig = '' + autoindex on; + include /etc/nginx/snippets/proxy.conf; + include /etc/nginx/snippets/authelia-authrequest.conf; + ''; + }; + locations."/notes" = { + extraConfig = '' + include /etc/nginx/snippets/proxy.conf; + include /etc/nginx/snippets/authelia-authrequest.conf; + ''; + }; + }; + + virtualHosts."auth.${host}" = { + forceSSL = true; + enableACME = true; + locations."/" = { + proxyPass = "http://localhost:9091"; + proxyWebsockets = true; + extraConfig = "include /etc/nginx/snippets/proxy.conf;"; + }; + locations."/api/verify" = { proxyPass = "http://localhost:9091"; }; + locations."/api/authz" = { proxyPass = "http://localhost:9091"; }; + }; + + virtualHosts."ldap.${host}" = { + forceSSL = true; + enableACME = true; + extraConfig = "include /etc/nginx/snippets/authelia-location.conf;"; + locations."/" = { + proxyPass = "http://localhost:17170"; + extraConfig = "include /etc/nginx/snippets/proxy.conf;"; + }; + }; + + virtualHosts."git.${host}" = { + forceSSL = true; + enableACME = true; + locations."/" = { + extraConfig = '' + client_max_body_size 512M; + proxy_pass http://localhost:3000; + proxy_set_header Connection $http_connection; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Host $host; + 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 $scheme; + ''; + }; + }; + }; + + # TODO: make a simple script around here that lets you regenerate or update every value when needed + # NOTE: does not work as expected + # systemd.services.authelia-main.preStart = '' + # [ -f /var/lib/authelia-main/jwt-secret ] || { + # "${pkgs.openssl}/bin/openssl" rand -base64 32 > /var/lib/authelia-main/jwtSecret + # } + # [ -f /var/lib/authelia-main/storage-encryption-file ] || { + # "${pkgs.openssl}/bin/openssl" rand -base64 32 > /var/lib/authelia-main/storageEncryptionKeyFile + # } + # [ -f /var/lib/authelia-main/session-secret-file ] || { + # "${pkgs.openssl}/bin/openssl" rand -base64 32 > /var/lib/authelia-main/sessionSecretFile + # } + # [ -f /var/lib/authelia-main/hmac ] || { + # "${pkgs.openssl}/bin/openssl" rand -base64 64 > /var/lib/authelia-main/hmac + # } + # [ -f /var/lib/authelia-main/rsa.2048.key ] || { + # "${pkgs.openssl}/bin/openssl" genpkey -algorithm RSA -out /var/lib/authelia-main/rsa.2048.key -pkeyopt rsa_keygen_bits:2048 + # } + # ''; + + environment.etc."nginx/snippets/authelia-location.conf" = { + user = "nginx"; + group = "nginx"; + text = '' + set $upstream_authelia http://localhost:9091/api/authz/auth-request; + + ## Virtual endpoint created by nginx to forward auth requests. + location /internal/authelia/authz { + ## Essential Proxy Configuration + internal; + proxy_pass $upstream_authelia; + + ## Headers + ## The headers starting with X-* are required. + proxy_set_header X-Original-Method $request_method; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Content-Length ""; + proxy_set_header Connection ""; + + ## Basic Proxy Configuration + proxy_pass_request_body off; + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead + proxy_redirect http:// $scheme://; + #proxy_http_version 1.1; + proxy_cache_bypass $cookie_session; + proxy_no_cache $cookie_session; + proxy_buffers 4 32k; + client_body_buffer_size 128k; + + ## Advanced Proxy Configuration + send_timeout 5m; + proxy_read_timeout 240; + proxy_send_timeout 240; + proxy_connect_timeout 240; + } + ''; + }; + environment.etc."nginx/snippets/authelia-authrequest.conf" = { + user = "nginx"; + group = "nginx"; + text = '' + ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. + auth_request /internal/authelia/authz; + + ## Save the upstream metadata response headers from Authelia to variables. + auth_request_set $user $upstream_http_remote_user; + auth_request_set $groups $upstream_http_remote_groups; + auth_request_set $name $upstream_http_remote_name; + auth_request_set $email $upstream_http_remote_email; + + ## Inject the metadata response headers from the variables into the request made to the backend. + proxy_set_header Remote-User $user; + proxy_set_header Remote-Groups $groups; + proxy_set_header Remote-Email $email; + proxy_set_header Remote-Name $name; + + ## Configure the redirection when the authz failure occurs. Lines starting with 'Modern Method' and 'Legacy Method' + ## should be commented / uncommented as pairs. The modern method uses the session cookies configuration's authelia_url + ## value to determine the redirection URL here. It's much simpler and compatible with the mutli-cookie domain easily. + + ## Modern Method: Set the $redirection_url to the Location header of the response to the Authz endpoint. + auth_request_set $redirection_url $upstream_http_location; + + ## Modern Method: When there is a 401 response code from the authz endpoint redirect to the $redirection_url. + error_page 401 =302 $redirection_url; + + ## Legacy Method: Set $target_url to the original requested URL. + ## This requires http_set_misc module, replace 'set_escape_uri' with 'set' if you don't have this module. + # set_escape_uri $target_url $scheme://$http_host$request_uri; + + ## Legacy Method: When there is a 401 response code from the authz endpoint redirect to the portal with the 'rd' + ## URL parameter set to $target_url. This requires users update 'auth.stuce.com/' with their external authelia URL. + # error_page 401 =302 https://auth.stuce.com/?rd=$target_url; + ''; + }; + + environment.etc."nginx/snippets/proxy.conf" = { + user = "nginx"; + group = "nginx"; + text = '' + ## Headers + proxy_set_header Host $host; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-URI $request_uri; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Real-IP $remote_addr; + + ## Basic Proxy Configuration + client_body_buffer_size 128k; + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; ## Timeout if the real server is dead. + proxy_redirect http:// $scheme://; + #proxy_http_version 1.1; + proxy_cache_bypass $cookie_session; + proxy_no_cache $cookie_session; + proxy_buffers 64 256k; + + ## Trusted Proxies Configuration + ## Please read the following documentation before configuring this: + ## https://www.authelia.com/integration/proxies/nginx/#trusted-proxies + # set_real_ip_from 10.0.0.0/8; + # set_real_ip_from 172.16.0.0/12; + # set_real_ip_from 192.168.0.0/16; + # set_real_ip_from fc00::/7; + real_ip_header X-Forwarded-For; + real_ip_recursive on; + + ## Advanced Proxy Configuration + send_timeout 5m; + proxy_read_timeout 360; + proxy_send_timeout 360; + proxy_connect_timeout 360; + ''; + }; + +} diff --git a/radicale.nix b/radicale.nix new file mode 100644 index 0000000..a2011b6 --- /dev/null +++ b/radicale.nix @@ -0,0 +1,19 @@ +{...}:{ +services.radicale = { + enable = true; + settings = { + server.hosts = [ "0.0.0.0:5232" ]; + auth = { + type = "ldap"; + ldap_uri = "ldap://localhost:3890"; + ldap_base = "dc=stuce,dc=ch"; + ldap_reader_dn = "uid=bind_user_radicale,ou=people,dc=stuce,dc=ch"; + ldap_secret_file = "/var/lib/radicale/ldap_password"; + ldap_filter = "(&(objectClass=person)(uid={0})(memberOf=cn=calendrier,ou=groups,dc=stuce,dc=ch))"; + # ldap_filter = "(&(objectClass=person)(uid={0}))"; + lc_username = true; + + }; + }; +}; +} diff --git a/sTodo.nix b/sTodo.nix new file mode 100644 index 0000000..e69de29 diff --git a/vaultwarden.nix b/vaultwarden.nix new file mode 100644 index 0000000..deeb8ea --- /dev/null +++ b/vaultwarden.nix @@ -0,0 +1,23 @@ +{ + services.vaultwarden = { + enable = true; + config = { + DOMAIN = "https://vault.stuce.ch"; + SIGNUPS_ALLOWED = false; + INVITATIONS_ALLOWED = false; + + SMTP_HOST = "mail.infomaniak.com"; + SMTP_PORT = "465"; + SMTP_FROM = "no-reply@stuce.ch"; + SMTP_FROM_NAME = "Stuce's vaultwarden"; + SMTP_USERNAME = "no-reply@stuce.ch"; + SMTP_PASSWORD_FILE = "/var/lib/vaultwarden/smtp_password"; + SMTP_SECURITY = "force_tls"; + ADMIN_TOKEN = + "$argon2id$v=19$m=65540,t=3,p=4$Ic844Ym+XKWlEcj5mVjk9WsWO13bF+iWTbqWlTRESzo$DPncN5oYnFXfea57zBdVC80dEzNQkkKMqdzGRIAAb/o"; + + ROCKET_PORT = 8222; + }; + backupDir = "/var/backup/vaultwarden"; + }; +}