Move upload auth from nginx into the app
Add HTTP Basic auth on /upload, reading an Apache htpasswd file (bcrypt only, `htpasswd -B`) via a new -htpasswd flag. This lets nginx drop to a plain TLS-terminating reverse proxy with no auth of its own; the existing bcrypt htpasswd file is reused unchanged. auth.go: - LoadHtpasswd parses user:hash lines, keeps bcrypt entries ($2a/$2b/$2y, so Apache's $2y$ works), skips MD5/SHA/crypt with a warning. - Unknown users still pay for one bcrypt comparison against a dummy hash, so response timing can't enumerate valid usernames. - Failed attempts draw from a token-bucket limiter (x/time/rate, ~1/s, burst 10) returning 429 once drained; successful and bulk uploads never touch it. server.go guards both /upload routes (nil authenticator = passthrough for local --serve). /health stays open. Empty -htpasswd disables auth and logs a warning. module.nix: new top-level services.photogallery.htpasswdFile, delivered to the service via systemd LoadCredential (so a root-owned 0400 secret works). Removed nginx.htpasswdFile and basic_auth from the vhost. Both uploader apps already send Basic credentials, so no client change. Deps: golang.org/x/crypto/bcrypt, golang.org/x/time/rate (vendorHash bump).
This commit is contained in:
1 parent
3291ed67d5
commit
434d2437bf
9 files changed
+342
-27
No files matched your search
+27
-12
@@ -82,6 +82,20 @@ in {
|
||||
description = "Photos per page on the timeline and grid views. The timeline packs whole days, so pages vary slightly around this.";
|
||||
};
|
||||
|
||||
htpasswdFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = "/run/secrets/photogallery-htpasswd";
|
||||
description = ''
|
||||
Path to an Apache htpasswd file (bcrypt only — generate with
|
||||
`htpasswd -B`) guarding the /upload endpoint. The app performs the
|
||||
auth itself, so nginx no longer needs basic_auth. The file is handed
|
||||
to the service via systemd LoadCredential, so it may be root-owned and
|
||||
0400 (e.g. an agenix/sops secret). Leave null to disable auth entirely
|
||||
— then /upload is open and anyone who can reach it can post.
|
||||
'';
|
||||
};
|
||||
|
||||
nginx = {
|
||||
enable = lib.mkEnableOption "nginx virtual host for photogallery";
|
||||
|
||||
@@ -91,12 +105,6 @@ in {
|
||||
description = "Domain for the nginx virtual host.";
|
||||
};
|
||||
|
||||
htpasswdFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
example = "/etc/nginx/.htpasswd-gallery";
|
||||
description = "Path to htpasswd file protecting the /upload endpoint.";
|
||||
};
|
||||
|
||||
enableACME = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
@@ -134,7 +142,7 @@ in {
|
||||
User = "photogallery";
|
||||
Group = "photogallery";
|
||||
|
||||
ExecStart = lib.escapeShellArgs [
|
||||
ExecStart = lib.escapeShellArgs ([
|
||||
"${pkg}/bin/photogallery"
|
||||
"--content" cfg.contentDir
|
||||
"--output" cfg.outputDir
|
||||
@@ -148,7 +156,14 @@ in {
|
||||
"--serif-family" cfg.serifFamily
|
||||
"--ml-family" cfg.mlFamily
|
||||
"--page-size" (toString cfg.pageSize)
|
||||
];
|
||||
] ++ lib.optionals (cfg.htpasswdFile != null) [
|
||||
# %d is systemd's credentials directory; LoadCredential below drops
|
||||
# the htpasswd file there, readable only by this service.
|
||||
"--htpasswd" "%d/htpasswd"
|
||||
]);
|
||||
|
||||
LoadCredential = lib.optional (cfg.htpasswdFile != null)
|
||||
"htpasswd:${cfg.htpasswdFile}";
|
||||
|
||||
Restart = "on-failure";
|
||||
RestartSec = "5s";
|
||||
@@ -178,11 +193,11 @@ in {
|
||||
tryFiles = "$uri $uri/ =404";
|
||||
};
|
||||
|
||||
# Auth is handled by the app (see services.photogallery.htpasswdFile),
|
||||
# so nginx just proxies the upload endpoint.
|
||||
locations."/upload" = {
|
||||
proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
||||
basicAuth = "gallery";
|
||||
basicAuthFile = cfg.nginx.htpasswdFile;
|
||||
extraConfig = "client_max_body_size 20M;";
|
||||
proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
||||
extraConfig = "client_max_body_size 20M;";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user