Compare commits

...

6 Commits

Author SHA1 Message Date
Lennart J. Kurzweg (Nx2)
dbccb827ad games also on ACE 2024-11-20 01:25:42 +01:00
Lennart J. Kurzweg (Nx2)
f5af726382 nx2site gitea and vaultwarden (working) 2024-11-20 01:24:57 +01:00
Lennart J. Kurzweg (Nx2)
e95332cedb Merge branch 'master' of ssh://ssh.nx2.site:20022/nx2/dotfiles 2024-11-15 14:10:50 +01:00
Lennart J. Kurzweg (Nx2)
6a7d8fe4be nx2site002 (unready) 2024-11-15 14:10:48 +01:00
Lennart J. Kurzweg (Nx2)
37eb70db63 Merge branch 'master' of ssh://ssh.nx2.site:20022/nx2/dotfiles 2024-11-14 01:21:42 +01:00
Lennart J. Kurzweg (Nx2)
77e9aa4ddd games 2024-11-14 01:21:34 +01:00
13 changed files with 254 additions and 125 deletions

View File

@@ -6,6 +6,7 @@
./system-modules/auto-mount.nix ./system-modules/auto-mount.nix
./system-modules/hardware-configuration.nix ./system-modules/hardware-configuration.nix
./system-modules/fuse.nix ./system-modules/fuse.nix
./system-modules/games.nix
./system-modules/nvidia.nix ./system-modules/nvidia.nix
./system-modules/users.nix ./system-modules/users.nix
./system-modules/sound.nix ./system-modules/sound.nix
@@ -41,8 +42,8 @@
./system-modules/nx2site.nix ./system-modules/nx2site.nix
./system-modules/postgres.nix ./system-modules/postgres.nix
./system-modules/nx2site/proxy.nix ./system-modules/nx2site/proxy.nix
# ./system-modules/nx2site/gitea.nix ./system-modules/nx2site/gitea.nix
# ./system-modules/nx2site/vaultwarden.nix ./system-modules/nx2site/vaultwarden.nix
] else []); ] else []);
# Set your time zone. # Set your time zone.
@@ -99,6 +100,7 @@
blueman blueman
dmidecode dmidecode
file file
cowsay
# ]) ++ (with pkgs-unstable; [ # ]) ++ (with pkgs-unstable; [
# # sendme # # sendme
]); ]);

Binary file not shown.

View File

@@ -1,7 +1,18 @@
{ pkgs, lib, host, ... }: { pkgs-unstable, pkgs, lib, host, ... }:
lib.mkIf (host != "NxACE") lib.mkIf (host == "NxNORTH" || host == "NxACE")
{ {
home.packages = with pkgs; [ home = {
packages = (with pkgs-unstable; [
protonup
mangohud
# heroic
mindustry-wayland mindustry-wayland
]; ]) ++ [ pkgs.heroic ];
sessionVariables = {
STEAM_EXTRA_COMPAT_TOOLS_PATHS = "\${HOME}/.steam/root/compatibilitytools.d";
};
};
} }

View File

@@ -21,7 +21,7 @@
HOST nxgit HOST nxgit
HostName ssh.${domain} HostName ssh.${domain}
User git User git
Port 20022 Port 50022
''; '';
}; };
} }

File diff suppressed because one or more lines are too long

12
system-modules/games.nix Normal file
View File

@@ -0,0 +1,12 @@
{ lib, host, ... }:
lib.mkIf (host == "NxNORTH" || host == "NxACE")
{
programs = {
gamemode = {
enable = true;
};
gamescope = {
enable = true;
};
};
}

View File

@@ -1,9 +1,23 @@
{ config, pkgs, domain, secrets, ... }: { config, pkgs, user, domain, secrets, ... }:
let dns-user = "cloudflare"; in
{ {
sops.secrets = { sops.secrets = {
"nx2site/namecheap.pw" = { }; # "nx2site/namecheap.pw" = { };
# "nx2site/cloudflare/api-token-dns-edit" = { }; # "nx2site/cloudflare/api-token-dns-edit" = { };
"nx2site/cloudflare/global-api-key" = { }; "nx2site/cloudflare/global-api-key" = {
owner = dns-user;
};
};
users = {
users = {
"${dns-user}" = {
isSystemUser = true;
group = dns-user;
};
"${user}".extraGroups = [ dns-user ];
};
groups."${dns-user}" = {};
}; };
systemd = { systemd = {
@@ -19,22 +33,24 @@
u = let u = let
account_id = secrets.email.gmail-online.mail; account_id = secrets.email.gmail-online.mail;
zone_id = "33fecab36e060f49d492127345ea95a0"; zone_id = "33fecab36e060f49d492127345ea95a0";
record_id = { record_id = { # curl --request GET --url https://api.cloudflare.com/client/v4/zones/33fecab36e060f49d492127345ea95a0/dns_records --header 'Content-Type: application/json' --header 'X-Auth-Email: <hidden>@gmail.com' --header "X-Auth-Key: <hiddenreadinsops>" -s | jq
base = "58d3412e8d88889d1a611b3669f0700f"; base = "58d3412e8d88889d1a611b3669f0700f";
sub = "fc861353142bc05d5dbad1799178e6a1";
base6 = "d1b90e21d2d747dcb30448bd65312927"; base6 = "d1b90e21d2d747dcb30448bd65312927";
sub = "fc861353142bc05d5dbad1799178e6a1";
sub6 = "b8082b7afe9e80971fc9f9dda16ec284"; sub6 = "b8082b7afe9e80971fc9f9dda16ec284";
ssh = "c0f14f17f32d6595c202f041dd836eb3";
ssh6 = "f1ecb2d9d0522d4eec06437688ca76da";
}; };
passord-file-path = config.sops.secrets."nx2site/cloudflare/global-api-key".path; passord-file-path = config.sops.secrets."nx2site/cloudflare/global-api-key".path;
log-file-path = "/var/log/couldflare.log"; log-file-path = "/var/log/couldflare.log";
count-file-path = "/var/log/cloudflare-count.txt"; count-file-path = "/var/log/cloudflare-count.txt";
in pkgs.writers.writePython3Bin "dyn_dns" { in pkgs.writers.writePython3Bin "dyn_dns" {
libraries = with pkgs.python311Packages; [ requests ]; libraries = with pkgs.python311Packages; [ requests ];
flakeIgnore = [ "E501" "E305" "E701" "E704" "E302" "E114" "F841" "E121" "E261" "E303"]; flakeIgnore = [ "E501" "E305" "E701" "E704" "E302" "E114" "F841" "E121" "E261" "E303" ];
} /* python */ '' } /* python */ ''
import requests import requests
import subprocess import subprocess
from datetime import datetime # from datetime import datetime
def get_public_ip(ipv6=False): def get_public_ip(ipv6=False):
return subprocess.run(['${pkgs.curl}/bin/curl', '-s', '-6' if ipv6 else '-4', 'https://ifconfig.me'], capture_output=True, text=True).stdout.strip() return subprocess.run(['${pkgs.curl}/bin/curl', '-s', '-6' if ipv6 else '-4', 'https://ifconfig.me'], capture_output=True, text=True).stdout.strip()
@@ -43,13 +59,13 @@
my_ip = get_public_ip() my_ip = get_public_ip()
my_ip6 = get_public_ip(ipv6=True) my_ip6 = get_public_ip(ipv6=True)
with open("${count-file-path}", "r") as f: # with open("${count-file-path}", "r") as f:
content = f.read() # content = f.read()
if content == "": count = 0 # if content == "": count = 0
else: count = int(content) # else: count = int(content)
count += 1 # count += 1
with open("${count-file-path}", "w") as f: # with open("${count-file-path}", "w") as f:
f.write(str(count)) # f.write(str(count))
# 4 # 4
with open("${passord-file-path}", 'r') as pw_file: with open("${passord-file-path}", 'r') as pw_file:
@@ -85,7 +101,7 @@
}, },
json={ json={
"comment": "Domain verification record", "comment": "Domain verification record",
"name": "${domain}", "name": "*.${domain}",
"proxied": True, "proxied": True,
"settings": {}, "settings": {},
"tags": [], "tags": [],
@@ -95,15 +111,34 @@
} }
) )
resp_sshd = requests.patch(
'https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${record_id.ssh}',
headers={
'Content-Type': 'application/json',
'X-Auth-Email': '${account_id}',
'X-Auth-Key': pw
},
json={
"comment": "Domain verification record",
"name": "ssh.${domain}",
"proxied": False,
"settings": {},
"tags": [],
"ttl": 1, # automatic
"content": my_ip,
"type": "A"
}
)
if resp_base.status_code != 200: if resp_base.status_code != 200:
print(resp_base.text) print(resp_base.text)
now_str = datetime.now().strftime('%Y/%m/%d-%R') # now_str = datetime.now().strftime('%Y/%m/%d-%R')
log_entry = f"At {now_str} - to {my_ip} - Response {resp_base.status_code}\n" # log_entry = f"At {now_str} - to {my_ip} - Response {resp_base.status_code}\n"
print(log_entry, end="") # print(log_entry, end="")
with open("${log-file-path}", 'a') as log_file: # with open("${log-file-path}", 'a') as log_file:
log_file.write(log_entry) # log_file.write(log_entry)
# Perform DNS updates # Perform DNS updates
# https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-update-dns-record # https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-update-dns-record
@@ -135,7 +170,7 @@
}, },
json={ json={
"comment": "Domain verification record", "comment": "Domain verification record",
"name": "${domain}", "name": "*.${domain}",
"proxied": True, "proxied": True,
"settings": {}, "settings": {},
"tags": [], "tags": [],
@@ -145,14 +180,32 @@
} }
) )
resp_sshd = requests.patch(
'https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${record_id.ssh6}',
headers={
'Content-Type': 'application/json',
'X-Auth-Email': '${account_id}',
'X-Auth-Key': pw
},
json={
"comment": "Domain verification record",
"name": "ssh.${domain}",
"proxied": False,
"settings": {},
"tags": [],
"ttl": 1, # automatic
"content": my_ip6,
"type": "AAAA"
}
)
if resp_base.status_code != 200: if resp_base.status_code != 200:
print(resp_base.text) print(resp_base.text)
# now_str = datetime.now().strftime('%Y/%m/%d-%R')
now_str = datetime.now().strftime('%Y/%m/%d-%R') # log_entry = f"At {now_str} - to {my_ip6} - Response {resp_base.status_code}\n"
log_entry = f"At {now_str} - to {my_ip6} - Response {resp_base.status_code}\n" # print(log_entry, end="")
print(log_entry, end="") # with open("${log-file-path}", 'a') as log_file: log_file.write(log_entry)
with open("${log-file-path}", 'a') as log_file: log_file.write(log_entry)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
@@ -164,7 +217,7 @@
''; '';
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
User = "root"; User = dns-user;
}; };
}; };
}; };
@@ -174,7 +227,7 @@
# "172.1.0.9" = [ "matrixdb.docker" ]; # "172.1.0.9" = [ "matrixdb.docker" ];
# "172.1.4.1" = [ "matrix-ss.docker" ]; # "172.1.4.1" = [ "matrix-ss.docker" ];
# "172.1.0.7" = [ "matrix-ssdb.docker" ]; # "172.1.0.7" = [ "matrix-ssdb.docker" ];
"172.1.5.1" = [ "pw.docker" ]; # "172.1.5.1" = [ "pw.docker" ];
"172.1.6.1" = [ "git.docker" ]; "172.1.6.1" = [ "git.docker" ];
# "172.1.0.10" = [ "gitdb.docker" ]; # "172.1.0.10" = [ "gitdb.docker" ];
# "172.1.7.1" = [ "nn.docker" ]; # "172.1.7.1" = [ "nn.docker" ];

View File

@@ -1,26 +1,41 @@
{ config, pkgs-unstable, domain, ... }: { config, pkgs, secrets, user, domain, ... }:
let git-user = "git"; in
{ {
sops.secrets = { sops.secrets = {
"postgres-pw" = { owner = "gitea"; }; "postgres-pw" = { owner = config.services.gitea.user; };
};
environment.systemPackages = with pkgs; [
gitea
];
users = {
users = {
"${user}".extraGroups = [ git-user ];
"${git-user}" = {
isSystemUser = true;
group = git-user;
useDefaultShell = true;
home = config.services.gitea.stateDir;
openssh.authorizedKeys.keys = config.users.users."${user}".openssh.authorizedKeys.keys;
};
};
groups."${git-user}" = {};
}; };
services.gitea = { services.gitea = {
enable = true; enable = true;
package = pkgs-unstable.gitea; package = pkgs.gitea;
group = "gitea"; # default group = git-user;
user = "gitea"; # default user = git-user;
appName = "NxGit"; appName = "NxGit";
stateDir = "/var/lib/gitea"; # default stateDir = "/var/lib/gitea"; # default
useWizard = false; # default useWizard = false; # default
# camoHmacKeyFile = ; # camoHmacKeyFile = ;
customDir = "${config.services.gitea.stateDir}/custom"; # default
database = { database = {
createDatabase = false; # default createDatabase = false; # default
host = "127.0.0.1"; # default host = "127.0.0.1"; # default
port = 5432; port = 5432;
passwordFile = config.sops.secrets."postgres-pw".path; passwordFile = config.sops.secrets."postgres-pw".path;
# path = "${config.services.gitea.stateDir}/data/gitea.db"; # default
# socket = "/run/postgresql";
socket = null; socket = null;
type = "postgres"; type = "postgres";
name = "gitea"; # default name = "gitea"; # default
@@ -28,37 +43,35 @@
}; };
dump = { dump = {
enable = true; enable = true;
backupDir = "${config.services.gitea.stateDir}/dump"; # default backupDir = "/var/backup/gitea";
file = null; # default file = null; # default = chosen by gitea
interval = "daily"; interval = "daily";
type = "zip"; # default type = "zip"; # default
}; };
extraConfig = null; # default # extraConfig = null; # default
lfs = { # lfs = {
enable = false; # default # enable = false; # default
contentDir = "${config.services.gitea.stateDir}/data/lfs"; # default # contentDir = "${config.services.gitea.stateDir}/data/lfs"; # default
}; # };
mailerPasswordFile = null; # default # mailerPasswordFile = null; # default
metricsTokenFile = null; # default # metricsTokenFile = null; # default
repositoryRoot = "${config.services.gitea.stateDir}/repositories"; # default # repositoryRoot = "${config.services.gitea.stateDir}/repositories"; # default
settings = { settings = {
log = { log = {
LEVEL = "Info"; LEVEL = "Info";
# LEVEL = "Error"; # LEVEL = "Error";
ROOT_PATH = "${config.services.gitea.stateDir}/log"; # default
};
i18n = {
LANGS = "en-US";
}; };
server = { server = {
DISABLE_SSH = false; # default DISABLE_SSH = false; # default
SSH_PORT = 20022; START_SSH_SERVER = false; # default
DOMAIN = "pw2.${domain}"; SSH_LISTEN_HOST = "0.0.0.0";
HTTP_ADDR = "http://${config.services.gitea.settings.server.DOMAIN}:${toString config.services.gitea.settings.server.HTTP_PORT}/"; SSH_PORT = secrets.ssh.port;
HTTP_PORT = 3000; # default DOMAIN = "pw.${domain}";
PROTOCOL = "http"; # default SSH_DOMAIN = "ssh.${domain}";
ROOT_URL = "https:pw2.${domain}/"; # default # HTTP_ADDR = "${config.services.gitea.settings.server.DOMAIN}";
STATIC_ROOT_PATH = "${config.services.gitea.stateDir}/static"; # HTTP_PORT = 3000; # default
# PROTOCOL = "http"; # default
# ROOT_URL = "https:pw.${domain}/"; # default
}; };
session = { session = {
COOKIE_SECURE = true; COOKIE_SECURE = true;

View File

@@ -14,7 +14,7 @@
}; };
certs = { certs = {
"${domain}" = { "${domain}" = {
extraDomainNames = builtins.map (subd: "${subd}.${domain}") [ "git" "git2" "pw" "pw2" "sync" ]; extraDomainNames = builtins.map (subd: "${subd}.${domain}") [ "git" "pw" "sync" ];
}; };
}; };
}; };
@@ -99,18 +99,20 @@
listen = dl; listen = dl;
locations = { "~.*" = { return = "502"; }; }; locations = { "~.*" = { return = "502"; }; };
}; };
# "pw.${domain}" = vh // {
# listen = dl;
# locations = let d = "pw.docker:80"; in {
# "/" = { proxyPass = "http://${d}"; };
# "/admin" = { proxyPass = "http://${d}"; };
# "/notifications/hub" = { proxyPass = "http://${d}"; };
# "/notifications/hub/negotiate" = { proxyPass = "http://${d}"; };
# };
# };
"pw.${domain}" = vh // { "pw.${domain}" = vh // {
listen = dl; listen = dl;
locations = let d = "pw.docker:80"; in { locations = let
"/" = { proxyPass = "http://${d}"; }; d = with config.services.vaultwarden.config; "${ROCKET_ADDRESS}:${builtins.toString ROCKET_PORT}";
"/admin" = { proxyPass = "http://${d}"; }; in {
"/notifications/hub" = { proxyPass = "http://${d}"; };
"/notifications/hub/negotiate" = { proxyPass = "http://${d}"; };
};
};
"pw2.${domain}" = vh // {
listen = dl;
locations = let d = "127.0.0.1:3000"; in {
"/" = { proxyPass = "http://${d}"; }; "/" = { proxyPass = "http://${d}"; };
"/admin" = { proxyPass = "http://${d}"; }; "/admin" = { proxyPass = "http://${d}"; };
"/notifications/hub" = { proxyPass = "http://${d}"; }; "/notifications/hub" = { proxyPass = "http://${d}"; };
@@ -121,13 +123,14 @@
listen = dl; listen = dl;
locations = { "/" = { proxyPass = "http://127.0.0.1:11434"; }; }; locations = { "/" = { proxyPass = "http://127.0.0.1:11434"; }; };
}; };
# "git.${domain}" = vh // {
# listen = dl;
# locations = { "/" = { proxyPass = "http://git.docker:3000"; }; };
# };
"git.${domain}" = vh // { "git.${domain}" = vh // {
http2 = false;
listen = dl; listen = dl;
locations = { "/" = { proxyPass = "http://git.docker:3000"; }; }; locations = { "/" = { proxyPass = "http://127.0.0.1:3000"; }; };
};
"git2.${domain}" = vh // {
listen = dl;
locations = { "/" = { proxyPass = "http://127.0.0.1:8222"; }; };
}; };
"~^(.*).${domain}$" = { "~^(.*).${domain}$" = {
listen = dl; listen = dl;

View File

@@ -0,0 +1,43 @@
{ config, pkgs, secrets, domain, ... }:
{
sops.secrets = {
"nx2site/vaultwarden.env" = {
owner = "vaultwarden";
};
};
services.vaultwarden = {
enable = true;
package = pkgs.vaultwarden;
webVaultPackage = pkgs.vaultwarden.webvault;
dbBackend = "postgresql";
# backupDir = "/var/backup/vaultwarden";
environmentFile = config.sops.secrets."nx2site/vaultwarden.env".path;
config = {
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
DATABASE_URL = "@DATABASE_URL@";
# DATABASE_URL = "postgresql://vaultwarden:fakepw123@127.0.0.1:5432/vaultwarden";
SMTP_HOST = "smtp.gmail.com";
SMTP_FROM = secrets.email.gmail-online.mail;
SMTP_PORT = 587;
SMTP_SECURITY = "starttls";
SMTP_USERNAME = secrets.email.gmail-online.mail;
SMTP_PASSWORD = "@SMTP_PASSWORD@";
LOGIN_RATELIMIT_MAX_BURST = 10;
LOGIN_RATELIMIT_SECONDS = 60;
DOMAIN = "https://pw.${domain}";
INVITATION_ORG_NAME = "NxPW";
INVITATIONS_ALLOWED = true;
ADMIN_TOKEN = "@ADMIN_TOKEN@";
SIGNUPS_ALLOWED = false;
SIGNUPS_VERIFY = true;
SIGNUPS_VERIFY_RESEND_TIME = 3600;
SIGNUPS_VERIFY_RESEND_LIMIT = 6;
EMERGENCY_ACCESS_ALLOWED = true;
SENDS_ALLOWED = true;
WEB_VAULT_ENABLED = true;
};
};
}

View File

@@ -23,7 +23,7 @@
# recoveryConfig = null; # recoveryConfig = null;
ensureDatabases = [ ensureDatabases = [
"gitea" "gitea"
# "vaultwarden" "vaultwarden"
]; ];
settings = { settings = {
port = 5432; # default port = 5432; # default
@@ -32,49 +32,40 @@
shared_preload_libraries = [ ]; # default shared_preload_libraries = [ ]; # default
}; };
ensureUsers = [ ensureUsers = [
# {
# name = "${user}";
# ensureDBOwnership = false;
# ensureClauses = {
# login = true;
# # inherit
# createdb = true;
# bypassrls = true;
# superuser = true;
# createrole = true;
# replication = true;
# };
# }
{
# as liong as there is no declarative user management you gotta set a pw by hand # as liong as there is no declarative user management you gotta set a pw by hand
# sudo -u postgres psql -c "ALTER USER gitea PASSWORD 'new-passwd';" # sudo -u postgres psql -c "ALTER USER gitea PASSWORD 'new-passwd';"
{
name = "gitea"; name = "gitea";
ensureDBOwnership = true; ensureDBOwnership = true;
} }
{
name = "vaultwarden";
ensureDBOwnership = true;
}
]; ];
}; };
# postgresqlBackup = { postgresqlBackup = {
# enable enable = true;
# startAt # startAt = "*-*-* 01:15:00";
# location # location = "/var/backup/postgresql";
# databases databases = config.services.postgresql.ensureDatabases;
# backupAll backupAll = false;
# compression # compression = "gzip";
# } # pgdumpOptions = "-C";
# compressionLevel = 6;
};
# postgresqlWalReceiver.receivers."main" = { # postgresqlWalReceiver.receivers."main" = {
# postgresqlPackage = pkgs.postgresql_15; # postgresqlPackage = pkgs.postgresql_15;
# directory = /mnt/pg_wal/main/; # directory = /mnt/pg_wal/main/;
# slot = "main_wal_receiver"; # slot = "main_wal_receiver";
# connection = "postgresql://user@somehost"; # connection = "postgresql://user@somehost";
# compress # compress
# extraArgs # extraArgs
# synchronous # synchronous
# environment # environment
# statusInterval # statusInterval
# }; # };
# } # };
}; };
} }

View File

@@ -3,7 +3,7 @@
{ {
environment.etc."ssh/ssh_host_ed25519_key.pub".text = if (host == "NxNORTH") then environment.etc."ssh/ssh_host_ed25519_key.pub".text = if (host == "NxNORTH") then
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF1r5gUQPPS/dGB0SsvWtP6WdNWoxMwhhHRrqlO19cJt root@NxNORTH" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF1r5gUQPPS/dGB0SsvWtP6WdNWoxMwhhHRrqlO19cJt root@NxNORTH"
else if ( host == "NxXPS") then else if ( host == "NxXPS" ) then
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPf+08+t8a0lY2+nR1mhIU3vuksStiJOlojJjzCwFk7r root@NxXPS" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPf+08+t8a0lY2+nR1mhIU3vuksStiJOlojJjzCwFk7r root@NxXPS"
else else
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBFfZpWVPlujsz3FklSVAM+tuYn4pzDSijhp5CeYNOZk root@NxACE"; "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBFfZpWVPlujsz3FklSVAM+tuYn4pzDSijhp5CeYNOZk root@NxACE";
@@ -13,7 +13,7 @@
}; };
services.openssh = { services.openssh = {
enable = true; enable = true;
ports = secrets.ssh.ports; ports = [ secrets.ssh.port ];
settings = { settings = {
PasswordAuthentication = false; PasswordAuthentication = false;
}; };

View File

@@ -6,6 +6,7 @@
users.users."${user}" = { users.users."${user}" = {
isNormalUser = true; isNormalUser = true;
extraGroups = [ extraGroups = [
# TODO: actually put the groups into the relevant files
"networkmanager" "networkmanager"
"wheel" "wheel"
"audio" "audio"
@@ -18,7 +19,6 @@
"acme" "acme"
"nginx" "nginx"
"adbusers" "adbusers"
"gitea"
"postgres" "postgres"
]; ];
useDefaultShell = true; useDefaultShell = true;