raito
a3f6f573a2
Introduces a full-fleged self-hosted S3 NixOS module with various knobs to perform NGINX-based redirects and s3-revproxy endpoints. Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
107 lines
3 KiB
Nix
107 lines
3 KiB
Nix
{ lib, config, ... }:
|
|
let
|
|
inherit (lib) mkOption mkIf types mapAttrs;
|
|
cfgParent = config.bagel.s3;
|
|
cfg = config.bagel.s3.reverse-proxy;
|
|
mkTarget = { name, bucket ? name }: {
|
|
mount = {
|
|
host = "${name}.${cfgParent.webRootDomain}";
|
|
path = [ "/" ];
|
|
};
|
|
actions.GET = {
|
|
enabled = true;
|
|
config = {
|
|
# e.g. /2.90 will 404, so it will redirect to /2.90/ if it is a directory
|
|
redirectWithTrailingSlashForNotFoundFile = true;
|
|
indexDocument = "index.html";
|
|
};
|
|
};
|
|
|
|
bucket = {
|
|
name = bucket;
|
|
region = "garage";
|
|
s3Endpoint = "https://${cfgParent.s3RootDomain}";
|
|
credentials = {
|
|
accessKey.env = "AWS_ACCESS_KEY_ID";
|
|
secretKey.env = "AWS_SECRET_KEY";
|
|
};
|
|
};
|
|
};
|
|
in
|
|
{
|
|
options.bagel.s3.reverse-proxy = {
|
|
targets = mkOption {
|
|
type = types.attrsOf (types.submodule ({ name, ... }: {
|
|
bucket = mkOption {
|
|
type = types.str;
|
|
default = name;
|
|
};
|
|
}));
|
|
default = { };
|
|
};
|
|
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 10652;
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
age.secrets.s3-revproxy-api-key-env.file = ./s3-revproxy-env.age;
|
|
# this solves garage supporting neither anonymous access nor automatic
|
|
# directory indexing by simply ignoring garage's web server and replacing it
|
|
# with overengineered golang instead.
|
|
services.s3-revproxy = {
|
|
enable = true;
|
|
settings = {
|
|
templates = {
|
|
helpers = [ ./s3-revproxy-templates/_helpers.tpl ];
|
|
notFoundError = {
|
|
headers = {
|
|
"Content-Type" = "{{ template \"main.headers.contentType\" . }}";
|
|
};
|
|
status = "404";
|
|
};
|
|
folderList = {
|
|
path = ./s3-revproxy-templates/folder-list.tpl;
|
|
headers = {
|
|
"Content-Type" = "{{ template \"main.headers.contentType\" . }}";
|
|
};
|
|
# empty s3 directories are not real and cannot hurt you.
|
|
# due to redirectWithTrailingSlashForNotFoundFile, garbage file names
|
|
# get redirected as folders, which then appear as empty, yielding
|
|
# poor UX.
|
|
status = ''
|
|
{{- if eq (len .Entries) 0 -}}
|
|
404
|
|
{{- else -}}
|
|
200
|
|
{{- end -}}
|
|
'';
|
|
};
|
|
};
|
|
/* For metrics and debugging (e.g. pulling the config)
|
|
internalServer = {
|
|
listenAddr = "127.0.0.1";
|
|
port = 1337;
|
|
};
|
|
*/
|
|
server = {
|
|
listenAddr = "127.0.0.1";
|
|
port = cfg.port;
|
|
|
|
# it's going right into nginx, so no point
|
|
compress.enabled = false;
|
|
cors = {
|
|
enabled = true;
|
|
allowMethods = [ "GET" ];
|
|
allowOrigins = [ "*" ];
|
|
};
|
|
};
|
|
targets = mapAttrs mkTarget cfg.targets;
|
|
};
|
|
environmentFile = config.age.secrets.s3-revproxy-api-key-env.path;
|
|
};
|
|
};
|
|
}
|