diff --git a/flake.nix b/flake.nix index 955df9f..132577e 100644 --- a/flake.nix +++ b/flake.nix @@ -59,6 +59,7 @@ mosh = pkgs.callPackage ./packages/mosh { }; flaresolverr = pkgs.callPackage ./packages/flaresolverr { }; htpdate = pkgs.callPackage ./packages/htpdate { }; + authelia = pkgs.callPackage ./packages/authelia { }; }; packages."aarch64-linux" = { tinystatus = import ./packages/tinystatus { pkgs = pkgsrpi; }; diff --git a/lib/default.nix b/lib/default.nix index 170181c..17827c4 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -32,6 +32,7 @@ in mosh = prev.pkgs.callPackage ../packages/mosh { }; flaresolverr = prev.pkgs.callPackage ../packages/flaresolverr { }; htpdate = prev.pkgs.callPackage ../packages/htpdate { }; + authelia = prev.pkgs.callPackage ../packages/authelia { }; }) inputs.neovim-nightly-overlay.overlay ]; @@ -52,6 +53,40 @@ in }; }; + mkPrivateSubdomain = name: port: { + luj.nginx.enable = true; + services.nginx.virtualHosts."${name}.julienmalka.me" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:${toString port}"; + extraConfig = '' + allow 10.100.0.0/24; + deny all; + ''; + }; + }; + }; + + mkVPNSubdomain = name: port: { + luj.nginx.enable = true; + services.nginx.virtualHosts."${name}.luj.home" = { + sslCertificate = "/etc/nginx/certs/subdomains/cert.pem"; + sslCertificateKey = "/etc/nginx/certs/subdomains/key.pem"; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:${toString port}"; + extraConfig = '' + allow 10.100.0.0/24; + deny all; + ''; + }; + }; + }; + + + + luj = import ./luj.nix final; } diff --git a/machines/lisa/default.nix b/machines/lisa/default.nix index 4f4db35..5a56d67 100644 --- a/machines/lisa/default.nix +++ b/machines/lisa/default.nix @@ -1,5 +1,4 @@ { config, lib, pkgs, modulesPath, ... }: - { imports = [ @@ -38,6 +37,7 @@ subdomain = "docs"; }; }; + homer.enable = true; bruit = { enable = true; nginx = { @@ -68,7 +68,7 @@ prefixLength = 120; }]; - networking.nameservers = [ "8.8.8.8" ]; + networking.nameservers = [ "10.100.0.2" ]; networking.hostId = "fbb334ae"; services.zfs.autoSnapshot.enable = true; services.zfs.autoScrub.enable = true; @@ -76,8 +76,7 @@ system.stateVersion = "21.11"; - networking.firewall = { - allowedTCPPorts = [ ]; + networking.firewall = { allowedUDPPorts = [ 51820 ]; }; networking.nat.enable = true; @@ -123,10 +122,18 @@ publicKey = "TAIP4faPBx6gk1cifC6fdfIP6slo1ir+HMVKxQXBejo="; } { - allowedIPs = [ "10.100.0.8" ]; + allowedIPs = [ "10.100.0.8/32" ]; publicKey = "EmWRWnZfr60ekm4ZLdwa6gXU6V3p39p6tWOZ03dL+DA="; } - ]; + { + allowedIPs = [ "10.100.0.9/32" ]; + publicKey = "z85y4nc+7O7t2I4VqP0SAKJOD46PlkXoEPiuGOBS+SI="; + } + { + allowedIPs = [ "10.100.0.10/32" ]; + publicKey = "SJ9tflQps1kssFsgVGLhqSSVKNPDspd+5xVMSu/aqk4="; + } + ]; }; }; @@ -134,10 +141,13 @@ - - - - - + services.nginx.virtualHosts."jellyfin.mondon.me" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyWebsockets = true; + proxyPass = "http://10.100.0.4"; + }; + }; } diff --git a/modules/authelia/authelia.conf b/modules/authelia/authelia.conf new file mode 100644 index 0000000..99dcafe --- /dev/null +++ b/modules/authelia/authelia.conf @@ -0,0 +1,38 @@ +# Virtual endpoint created by nginx to forward auth requests. +location /authelia { + internal; + set $upstream_authelia http://127.0.0.1:9091/api/verify; + proxy_pass_request_body off; + proxy_pass $upstream_authelia; + proxy_set_header Content-Length ""; + + # Timeout if the real server is dead + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; + + # [REQUIRED] Needed by Authelia to check authorizations of the resource. + # Provide either X-Original-URL and X-Forwarded-Proto or + # X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Uri or both. + # Those headers will be used by Authelia to deduce the target url of the user. + # Basic Proxy Config + client_body_buffer_size 128k; + proxy_set_header Host $host; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + 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_redirect http:// $scheme://; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_cache_bypass $cookie_session; + proxy_no_cache $cookie_session; + proxy_buffers 4 32k; + + # Advanced Proxy Config + send_timeout 5m; + proxy_read_timeout 240; + proxy_send_timeout 240; + proxy_connect_timeout 240; +} diff --git a/modules/authelia/configuration.yml b/modules/authelia/configuration.yml new file mode 100644 index 0000000..8b9a623 --- /dev/null +++ b/modules/authelia/configuration.yml @@ -0,0 +1,45 @@ +host: 0.0.0.0 +port: 9091 +server: + read_buffer_size: 4096 + write_buffer_size: 4096 + path: "authelia" +log_level: debug +jwt_secret: somethingsomethingrandomrecret +default_redirection_url: https://auth.julienmalka.me +authentication_backend: + disable_reset_password: false + file: + path: /config/users_database.yml + password: + algorithm: argon2id + iterations: 1 + key_length: 32 + salt_length: 16 + memory: 512 + parallelism: 8 +access_control: + default_policy: deny + rules: + - domain: + - series.julienmalka.me + policy: one_factor +session: + name: authelia_session + secret: somerandomsecret + expiration: 1h + inactivity: 5m + remember_me_duration: 1M + domain: series.julienmalka.me +regulation: + max_retries: 3 + find_time: 2m + ban_time: 5m +storage: + encryption_key: a_very_important_secret + local: + path: /config/db.sqlite3 +notifier: + disable_startup_check: false + filesystem: + filename: /config/notification.txt diff --git a/modules/authelia/default.nix b/modules/authelia/default.nix new file mode 100644 index 0000000..a778f81 --- /dev/null +++ b/modules/authelia/default.nix @@ -0,0 +1,80 @@ +{ pkgs, lib, config, ... }: +with lib; let + cfg = config.luj.authelia; +in +{ + options.luj.authelia = { + enable = mkEnableOption "enable authelia"; + }; + config = mkIf cfg.enable { + + virtualisation.docker.enable = true; + virtualisation.oci-containers.containers."authelia" = { + image = "authelia/authelia"; + environment = { + "TZ" = "Europe/Paris"; + }; + volumes = [ + "/srv/authelia:/config/" + ]; + + ports = [ "9091:9091" ]; + + }; + + services.nginx.appendHttpConfig = '' + server { + server_name auth.julienmalka.me; + listen 80; + return 301 https://$server_name$request_uri; + } + + server { + server_name auth.julienmalka.me; + listen 443 ssl http2; + + location / { + set $upstream_authelia http://127.0.0.1:9091; + proxy_pass $upstream_authelia; + + client_body_buffer_size 128k; + + #Timeout if the real server is dead + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; + + # Advanced Proxy Config + send_timeout 5m; + proxy_read_timeout 360; + proxy_send_timeout 360; + proxy_connect_timeout 360; + + # Basic Proxy Config + 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; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Uri $request_uri; + proxy_set_header X-Forwarded-Ssl on; + proxy_redirect http:// $scheme://; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_cache_bypass $cookie_session; + proxy_no_cache $cookie_session; + proxy_buffers 64 256k; + + # If behind reverse proxy, forwards the correct IP + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.0.0.0/8; + 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; + } + } + ''; + + + }; +} + diff --git a/modules/authelia/secure.conf b/modules/authelia/secure.conf new file mode 100644 index 0000000..fba6964 --- /dev/null +++ b/modules/authelia/secure.conf @@ -0,0 +1,23 @@ +# Basic Authelia Config +# Send a subsequent request to Authelia to verify if the user is authenticated +# and has the right permissions to access the resource. +auth_request /authelia; +# Set the `target_url` variable based on the request. It will be used to build the portal +# URL with the correct redirection parameter. +auth_request_set $target_url $scheme://$http_host$request_uri; +# Set the X-Forwarded-User and X-Forwarded-Groups with the headers +# returned by Authelia for the backends which can consume them. +# This is not safe, as the backend must make sure that they come from the +# proxy. In the future, it's gonna be safe to just use OAuth. +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; +proxy_set_header Remote-User $user; +proxy_set_header Remote-Groups $groups; +proxy_set_header Remote-Name $name; +proxy_set_header Remote-Email $email; +# If Authelia returns 401, then nginx redirects the user to the login portal. +# If it returns 200, then the request pass through to the backend. +# For other type of errors, nginx will handle them as usual. +error_page 401 =302 https://auth.julienmalka.me/?rd=$target_url; diff --git a/modules/authelia/users.yml b/modules/authelia/users.yml new file mode 100644 index 0000000..48812f0 --- /dev/null +++ b/modules/authelia/users.yml @@ -0,0 +1,7 @@ +users: + julien: + displayname: "Julien Malka" + email: julien@malka.sh + groups: + - admins + - dev diff --git a/modules/deluge/default.nix b/modules/deluge/default.nix index b7a2bb6..ef2acf3 100644 --- a/modules/deluge/default.nix +++ b/modules/deluge/default.nix @@ -54,8 +54,10 @@ in }; } - (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port))]); + + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + } diff --git a/modules/docs/default.nix b/modules/docs/default.nix index 22dfbc8..e25dfee 100644 --- a/modules/docs/default.nix +++ b/modules/docs/default.nix @@ -46,8 +46,11 @@ in }; } - (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port))]); + (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port)) + + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + diff --git a/modules/drone/default.nix b/modules/drone/default.nix index 6244c72..05a18a7 100644 --- a/modules/drone/default.nix +++ b/modules/drone/default.nix @@ -94,5 +94,5 @@ in path = [ pkgs.nixUnstable pkgs.git pkgs.openssh ]; }; - } (mkSubdomain cfg.subdomain port)); + } (recursiveUpdate (mkSubdomain cfg.subdomain port) (mkVPNSubdomain cfg.subdomain port))); } diff --git a/modules/homer/default.nix b/modules/homer/default.nix new file mode 100644 index 0000000..3e0a6b1 --- /dev/null +++ b/modules/homer/default.nix @@ -0,0 +1,24 @@ +{ lib, pkgs, inputs, config, ... }: +with lib; +let + cfg = config.luj.homer; +in +{ + options.luj.homer = { + enable = mkEnableOption "enable homer"; + }; + + config = mkIf cfg.enable + { + luj.nginx.enable = true; + + services.nginx.virtualHosts."luj.home" = { + sslCertificate = "/etc/nginx/certs/home/cert.pem"; + sslCertificateKey = "/etc/nginx/certs/home/key.pem"; + forceSSL = true; + root = "/srv/homer/"; + }; + + + }; +} diff --git a/modules/jackett/default.nix b/modules/jackett/default.nix index 951ca63..757730a 100644 --- a/modules/jackett/default.nix +++ b/modules/jackett/default.nix @@ -38,8 +38,11 @@ in } - (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port))]); + (mkIf cfg.nginx.enable (mkPrivateSubdomain cfg.nginx.subdomain port)) + + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + diff --git a/modules/jellyfin/default.nix b/modules/jellyfin/default.nix index b113eb8..aae3c7f 100644 --- a/modules/jellyfin/default.nix +++ b/modules/jellyfin/default.nix @@ -35,10 +35,44 @@ in user = cfg.user; group = cfg.group; }; + + # services.nginx.appendHttpConfig = '' + # server { + # server_name tv.julienmalka.me; + # listen 80; + # return 301 https://$server_name$request_uri; + # } + + # server { + # server_name tv.julienmalka.me; + # listen 443 ssl http2; + + # include ${../authelia/authelia.conf}; # Authelia auth endpoint + + # location / { + # proxy_pass http://127.0.0.1:8096; + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # include ${../authelia/secure.conf}; # Protect this endpoint + # } + # } + # ''; + + + + + + + } - (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port) )]); - + (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port)) + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + + + diff --git a/modules/lidarr/default.nix b/modules/lidarr/default.nix index ea9f95c..09a5437 100644 --- a/modules/lidarr/default.nix +++ b/modules/lidarr/default.nix @@ -38,8 +38,11 @@ in }; } - (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port) )]); + (mkIf cfg.nginx.enable (mkPrivateSubdomain cfg.nginx.subdomain port) ) + + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + diff --git a/modules/mailserver/default.nix b/modules/mailserver/default.nix index d919eae..2de3680 100644 --- a/modules/mailserver/default.nix +++ b/modules/mailserver/default.nix @@ -28,6 +28,10 @@ with lib; "camille.mondon@ens.school" = { hashedPasswordFile = "/run/secrets/ensmailmondon-pw"; }; + + "docs@malka.sh" = { + hashedPassword = "$2y$05$NdEncXtSDspL0reJDrm1NubqOrSi2IO.4qVJvsUVFDsAdItVuc2Fa"; + }; }; certificateScheme = 3; }; diff --git a/modules/mediaserver/default.nix b/modules/mediaserver/default.nix index 27d1121..db98889 100644 --- a/modules/mediaserver/default.nix +++ b/modules/mediaserver/default.nix @@ -87,7 +87,7 @@ in user = "mediaserver"; group = "mediaserver"; nginx.enable = true; - nginx.subdomain = "musique"; + nginx.subdomain = "music"; }; diff --git a/modules/navidrome/default.nix b/modules/navidrome/default.nix index 06f7b49..59455cd 100644 --- a/modules/navidrome/default.nix +++ b/modules/navidrome/default.nix @@ -61,17 +61,11 @@ in } - ({ - services.nginx.virtualHosts."music.julienmalka.me" = { - forceSSL = true; - enableACME = true; - locations."/" = { - proxyPass = "http://localhost:${toString port}"; - }; - }; -}) - -]); - + + (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port)) + + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + + } diff --git a/modules/nginx/404/index.html b/modules/nginx/404/index.html new file mode 100644 index 0000000..0728bc9 --- /dev/null +++ b/modules/nginx/404/index.html @@ -0,0 +1,13 @@ + + + + + + + + +
404
+
Ooops...
page not found
+ + + diff --git a/modules/nginx/404/main.css b/modules/nginx/404/main.css new file mode 100644 index 0000000..682c16b --- /dev/null +++ b/modules/nginx/404/main.css @@ -0,0 +1,55 @@ +body { + display: flex; + flex-flow: row wrap; + align-content: center; + justify-content: center; + position:absolute; + top:50%; + left:50%; + transform: translate(-50%, -50%); +} + +div { + width: 100%; + text-align: center; +} + +.number { + background: #fff; + position: relative; + font: 900 30vmin "Consolas"; + letter-spacing: 5vmin; + text-shadow: 2px -1px 0 #000, 4px -2px 0 #0a0a0a, 6px -3px 0 #0f0f0f, 8px -4px 0 #141414, 10px -5px 0 #1a1a1a, 12px -6px 0 #1f1f1f, 14px -7px 0 #242424, 16px -8px 0 #292929; +} +.number::before { + background-color: #673ab7; + background-image: radial-gradient(closest-side at 50% 50%, #ffc107 100%, rgba(0, 0, 0, 0)), radial-gradient(closest-side at 50% 50%, #e91e63 100%, rgba(0, 0, 0, 0)); + background-repeat: repeat-x; + background-size: 40vmin 40vmin; + background-position: -100vmin 20vmin, 100vmin -25vmin; + width: 100%; + height: 100%; + mix-blend-mode: screen; + -webkit-animation: moving 10s linear infinite both; + animation: moving 10s linear infinite both; + display: block; + position: absolute; + content: ""; +} +@-webkit-keyframes moving { + to { + background-position: 100vmin 20vmin, -100vmin -25vmin; + } +} +@keyframes moving { + to { + background-position: 100vmin 20vmin, -100vmin -25vmin; + } +} + +.text { + font: 400 5vmin "Courgette"; +} +.text span { + font-size: 10vmin; +} diff --git a/modules/nginx/default.nix b/modules/nginx/default.nix index 6a9cd62..b7c9d4e 100644 --- a/modules/nginx/default.nix +++ b/modules/nginx/default.nix @@ -1,7 +1,8 @@ { lib, pkgs, config, ... }: with lib; let cfg = config.luj.nginx; -in { +in +{ options.luj.nginx = { enable = mkEnableOption "activate nginx service"; @@ -30,5 +31,15 @@ in { ''; }; + services.nginx.virtualHosts."404.julienmalka.me" = { + default = true; + locations."/" = { + root = "${./404}"; + }; + }; + + + + }; } diff --git a/modules/radarr/default.nix b/modules/radarr/default.nix index 2900ede..9fad521 100644 --- a/modules/radarr/default.nix +++ b/modules/radarr/default.nix @@ -38,9 +38,10 @@ in }; } - (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port)) - - ]); + (mkIf cfg.nginx.enable (mkPrivateSubdomain cfg.nginx.subdomain port)) + + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + diff --git a/modules/sonarr/default.nix b/modules/sonarr/default.nix index dd23564..411d3d6 100644 --- a/modules/sonarr/default.nix +++ b/modules/sonarr/default.nix @@ -38,8 +38,10 @@ in }; } - (mkIf cfg.nginx.enable (mkSubdomain cfg.nginx.subdomain port) )]); - + (mkIf cfg.nginx.enable (mkPrivateSubdomain cfg.nginx.subdomain port) ) + + (mkIf cfg.nginx.enable (mkVPNSubdomain cfg.nginx.subdomain port))]); + diff --git a/packages/authelia/default.nix b/packages/authelia/default.nix new file mode 100644 index 0000000..649b266 --- /dev/null +++ b/packages/authelia/default.nix @@ -0,0 +1,26 @@ +{ stdenv, fetchurl, ... }: +stdenv.mkDerivation { + pname = "authelia"; + version = "4.33.2"; + + src = fetchurl { + url = "https://github.com/authelia/authelia/releases/download/v4.33.2/authelia-v4.33.2-linux-amd64.tar.gz"; + sha256 = "sha256-uxRDhhkq8sUll1KH1xAjw0Kz3lH8NWJu3in3Owf9rrA="; + }; + + sourceRoot = "."; + + installPhase = '' + mkdir -p $out/bin + + cp authelia-linux-amd64 $out/bin/authelia + cp config.template.yml $out/ + ''; + + preFixup = '' + patchelf \ + --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \ + $out/bin/authelia + ''; +} +