1
1
Fork 1
mirror of https://github.com/oddlama/nix-config.git synced 2025-10-10 14:50:40 +02:00

feat: add ente

This commit is contained in:
oddlama 2025-05-18 13:55:20 +02:00
parent ac34b94f87
commit 4c2f98f9e4
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
48 changed files with 904 additions and 4 deletions

View file

@ -54,4 +54,15 @@
# This node shall monitor the infrastructure
availableMonitoringNetworks = [ "internet" ];
};
services.ente.web = {
enable = true;
domains = {
api = "api.photos.${globals.domains.me}";
accounts = "accounts.photos.${globals.domains.me}";
albums = "albums.photos.${globals.domains.me}";
cast = "cast.photos.${globals.domains.me}";
photos = "photos.${globals.domains.me}";
};
};
}

View file

@ -12,7 +12,12 @@ let
# FIXME: new entry here? make new firezone gateway on ward entry too.
homeDomains = [
globals.services.grafana.domain
globals.services.ente.domain
"accounts.photos.${globals.domains.me}"
"albums.photos.${globals.domains.me}"
"api.photos.${globals.domains.me}"
"cast.photos.${globals.domains.me}"
"photos.${globals.domains.me}"
"s3.photos.${globals.domains.me}"
globals.services.immich.domain
globals.services.influxdb.domain
globals.services.loki.domain

Binary file not shown.

View file

@ -150,7 +150,9 @@
}
// mkMicrovm "ai" { }
// mkMicrovm "minecraft" { }
// mkMicrovm "ente" { }
// mkMicrovm "ente" {
enableStorageDataset = true;
}
#// mkMicrovm "fasten-health" {}
);
}

238
hosts/sire/guests/ente.nix Normal file
View file

@ -0,0 +1,238 @@
{
config,
globals,
lib,
pkgs,
...
}:
let
enteAccountsDomain = "accounts.photos.${globals.domains.me}";
enteAlbumsDomain = "albums.photos.${globals.domains.me}";
enteApiDomain = "api.photos.${globals.domains.me}";
enteCastDomain = "cast.photos.${globals.domains.me}";
entePhotosDomain = "photos.${globals.domains.me}";
s3Domain = "s3.photos.${globals.domains.me}";
proxyConfig = remoteAddr: nginxExtraConfig: {
upstreams.ente = {
servers."${remoteAddr}:80" = { };
extraConfig = ''
zone ente 64k;
keepalive 20;
'';
monitoring.enable = true;
};
upstreams.museum = {
servers."${remoteAddr}:8080" = { };
extraConfig = ''
zone museum 64k;
keepalive 20;
'';
};
upstreams.minio = {
servers."${remoteAddr}:9000" = { };
extraConfig = ''
zone minio 64k;
keepalive 20;
'';
};
virtualHosts =
{
${enteApiDomain} = {
forceSSL = true;
useACMEWildcardHost = true;
locations."/".proxyPass = "http://museum";
extraConfig = ''
client_max_body_size 4M;
${nginxExtraConfig}
'';
};
${s3Domain} = {
forceSSL = true;
useACMEWildcardHost = true;
locations."/".proxyPass = "http://minio";
extraConfig = ''
client_max_body_size 32M;
proxy_buffering off;
proxy_request_buffering off;
${nginxExtraConfig}
'';
};
}
// lib.genAttrs
[
enteAccountsDomain
enteAlbumsDomain
enteCastDomain
entePhotosDomain
]
(_domain: {
useACMEWildcardHost = true;
extraConfig = nginxExtraConfig;
});
};
in
{
wireguard.proxy-sentinel = {
client.via = "sentinel";
firewallRuleForNode.sentinel.allowedTCPPorts = [
80
9000
];
};
wireguard.proxy-home = {
client.via = "ward";
firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [
80
9000
];
};
globals.services.ente.domain = entePhotosDomain;
# FIXME: also monitor from internal network
globals.monitoring.http.ente = {
url = "https://${entePhotosDomain}";
expectedBodyRegex = "Ente Photos";
network = "internet";
};
fileSystems."/storage".neededForBoot = true;
environment.persistence."/storage".directories = [
{
directory = "/var/lib/minio";
user = "minio";
group = "minio";
mode = "0750";
}
];
environment.persistence."/persist".directories = [
{
directory = "/var/lib/ente";
user = "ente";
group = "ente";
mode = "0750";
}
];
# NOTE: don't use the root user for access. In this case it doesn't matter
# since the whole minio server is only for ente anyway, but it would be a
# good practice.
age.secrets.minio-access-key = {
generator.script = "alnum";
mode = "440";
group = "ente";
};
age.secrets.minio-secret-key = {
generator.script = "alnum";
mode = "440";
group = "ente";
};
age.secrets.minio-root-credentials = {
generator.dependencies = [
config.age.secrets.minio-access-key
config.age.secrets.minio-secret-key
];
generator.script =
{
lib,
decrypt,
deps,
...
}:
''
echo -n "MINIO_ROOT_USER="
${decrypt} ${lib.escapeShellArg (builtins.elemAt deps 0).file}
echo -n "MINIO_ROOT_PASSWORD="
${decrypt} ${lib.escapeShellArg (builtins.elemAt deps 1).file}
'';
mode = "440";
group = "minio";
};
# base64 (url)
age.secrets.ente-jwt = {
generator.script =
{ pkgs, ... }: "${pkgs.openssl}/bin/openssl rand -base64 32 | tr -d '\n' | tr '/+' '_-'";
mode = "440";
group = "ente";
};
# base64 (standard)
age.secrets.ente-encryption-key = {
generator.script = "base64";
mode = "440";
group = "ente";
};
# base64 (standard)
age.secrets.ente-hash-key = {
generator.script = { pkgs, ... }: "${pkgs.openssl}/bin/openssl rand -base64 64 | tr -d '\n'";
mode = "440";
group = "ente";
};
services.minio = {
enable = true;
rootCredentialsFile = config.age.secrets.minio-root-credentials.path;
};
systemd.services.minio = {
environment.MINIO_SERVER_URL = "https://${s3Domain}";
postStart = ''
# Wait until minio is up
${lib.getExe pkgs.curl} --retry 5 --retry-connrefused --fail --no-progress-meter -o /dev/null "http://localhost:9000/minio/health/live"
# Make sure bucket exists
mkdir -p ${lib.escapeShellArg config.services.minio.dataDir}/data/ente
'';
};
systemd.services.ente.after = [ "minio.service" ];
services.ente.api = {
enable = true;
enableLocalDB = true;
domain = enteApiDomain;
settings = {
apps = {
accounts = "https://${enteAccountsDomain}";
cast = "https://${enteCastDomain}";
public-albums = "https://${enteAlbumsDomain}";
};
webauthn = {
rpid = enteAccountsDomain;
rporigins = [ "https://${enteAccountsDomain}" ];
};
s3 = {
use_path_style_urls = true;
b2-eu-cen = {
endpoint = "https://${s3Domain}";
region = "us-east-1";
bucket = "ente";
key._secret = config.age.secrets.minio-access-key.path;
secret._secret = config.age.secrets.minio-secret-key.path;
};
};
jwt.secret._secret = config.age.secrets.ente-jwt.path;
key = {
encryption._secret = config.age.secrets.ente-encryption-key.path;
hash._secret = config.age.secrets.ente-hash-key.path;
};
};
};
# NOTE: services.ente.web is configured separately on both proxy servers!
nodes.sentinel.services.nginx = proxyConfig config.wireguard.proxy-sentinel.ipv4 "";
nodes.ward-web-prox.services.nginxy = proxyConfig config.wireguard.proxy-home.ipv4 ''
allow ${globals.net.home-lan.vlans.home.cidrv4};
allow ${globals.net.home-lan.vlans.home.cidrv6};
# Firezone traffic
allow ${globals.net.home-lan.vlans.services.hosts.ward.ipv4};
allow ${globals.net.home-lan.vlans.services.hosts.ward.ipv6};
deny all;
'';
}

View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILPHm23XtSiwueXpmqJqFWIxYVWU/eq+dQ0PcwMrsN+c

View file

@ -13,7 +13,13 @@ let
# FIXME: new entry here? make new firezone entry too.
homeDomains = [
globals.services.grafana.domain
globals.services.ente.domain
# TODO: allow multiple domains per global service.
"accounts.photos.${globals.domains.me}"
"albums.photos.${globals.domains.me}"
"api.photos.${globals.domains.me}"
"cast.photos.${globals.domains.me}"
"photos.${globals.domains.me}"
"s3.photos.${globals.domains.me}"
globals.services.immich.domain
globals.services.influxdb.domain
globals.services.loki.domain

View file

@ -112,7 +112,12 @@ in
# FIXME: new entry here? make new firezone entry too.
# FIXME: new entry here? make new firezone gateway on ward entry too.
globals.services.grafana.domain
globals.services.ente.domain
"accounts.photos.${globals.domains.me}"
"albums.photos.${globals.domains.me}"
"api.photos.${globals.domains.me}"
"cast.photos.${globals.domains.me}"
"photos.${globals.domains.me}"
"s3.photos.${globals.domains.me}"
globals.services.immich.domain
globals.services.influxdb.domain
globals.services.loki.domain

View file

@ -85,4 +85,15 @@ in
users.groups.acme.members = [ "nginx" ];
services.nginx.enable = true;
services.nginx.recommendedSetup = true;
services.ente.web = {
enable = true;
domains = {
api = "api.photos.${globals.domains.me}";
accounts = "accounts.photos.${globals.domains.me}";
albums = "albums.photos.${globals.domains.me}";
cast = "cast.photos.${globals.domains.me}";
photos = "photos.${globals.domains.me}";
};
};
}