Self-host your own digital island
1{ pkgs, config, lib, ... }:
2
3with lib;
4let
5 cfg = config.eilean;
6 domain = config.networking.domain;
7 subdomain = "git.${domain}";
8in {
9 options.eilean.gitea = {
10 enable = mkEnableOption "gitea";
11 sshPort = mkOption {
12 type = types.int;
13 default = 3001;
14 };
15 databasePasswordFile = mkOption {
16 type = types.nullOr types.path;
17 default = null;
18 };
19 };
20
21 config = mkIf cfg.gitea.enable {
22 security.acme-eon.nginxCerts = [ subdomain ];
23
24 services.nginx = {
25 enable = true;
26 recommendedProxySettings = true;
27 virtualHosts."${subdomain}" = {
28 enableACME = lib.mkIf (!cfg.acme-eon) true;
29 forceSSL = true;
30 locations."/" = {
31 proxyPass = "http://localhost:${
32 builtins.toString config.services.gitea.settings.server.HTTP_PORT
33 }/";
34 };
35 };
36 };
37
38 users.users.git = {
39 description = "Git Service";
40 home = config.services.gitea.stateDir;
41 useDefaultShell = true;
42 group = "gitea";
43 isSystemUser = true;
44 };
45
46 services.gitea = {
47 enable = true;
48 user = "git";
49 appName = "git | ${domain}";
50 mailerPasswordFile = cfg.mailserver.systemAccountPasswordFile;
51 settings = {
52 server = {
53 ROOT_URL = "https://${subdomain}/";
54 DOMAIN = subdomain;
55 };
56 mailer = {
57 ENABLED = true;
58 FROM = "git@${domain}";
59 MAILER_TYPE = "smtp";
60 HOST = "mail.${domain}:465";
61 USER = "system@${domain}";
62 IS_TLS_ENABLED = true;
63 };
64 repository.DEFAULT_BRANCH = "main";
65 service.DISABLE_REGISTRATION = true;
66 #server.HTTP_PORT = 3000;
67 };
68 database = {
69 type = "postgres";
70 passwordFile = cfg.gitea.databasePasswordFile;
71 user = "git";
72 name = "git";
73 #createDatabase = true;
74 #socket = "/run/postgresql";
75 };
76 #stateDir = "/var/lib/gitea";
77 };
78
79 # https://github.com/NixOS/nixpkgs/issues/103446
80 systemd.services.gitea.serviceConfig = {
81 ReadWritePaths = [ "/var/lib/postfix/queue/maildrop" ];
82 NoNewPrivileges = mkForce false;
83 PrivateDevices = mkForce false;
84 PrivateUsers = mkForce false;
85 ProtectHostname = mkForce false;
86 ProtectClock = mkForce false;
87 ProtectKernelTunables = mkForce false;
88 ProtectKernelModules = mkForce false;
89 ProtectKernelLogs = mkForce false;
90 RestrictAddressFamilies = mkForce [ ];
91 LockPersonality = mkForce false;
92 MemoryDenyWriteExecute = mkForce false;
93 RestrictRealtime = mkForce false;
94 RestrictSUIDSGID = mkForce false;
95 SystemCallArchitectures = mkForce "";
96 SystemCallFilter = mkForce [ ];
97 };
98
99 eilean.dns.enable = true;
100 eilean.services.dns.zones.${config.networking.domain}.records = [{
101 name = "git";
102 type = "CNAME";
103 value = cfg.domainName;
104 }];
105
106 # proxy port 22 on ethernet interface to internal gitea ssh server
107 # openssh server remains accessible on port 22 via vpn(s)
108
109 # allow forwarding
110 boot.kernel.sysctl = {
111 "net.ipv4.ip_forward" = 1;
112 "net.ipv6.conf.all.forwarding" = 1;
113 };
114
115 networking.firewall = {
116 allowedTCPPorts = [ 22 cfg.gitea.sshPort ];
117 extraCommands = ''
118 # proxy all traffic on public interface to the gitea SSH server
119 iptables -A PREROUTING -t nat -i ${config.eilean.publicInterface} -p tcp --dport 22 -j REDIRECT --to-port ${
120 builtins.toString cfg.gitea.sshPort
121 }
122 ip6tables -A PREROUTING -t nat -i ${config.eilean.publicInterface} -p tcp --dport 22 -j REDIRECT --to-port ${
123 builtins.toString cfg.gitea.sshPort
124 }
125
126 # proxy locally originating outgoing packets
127 iptables -A OUTPUT -d ${config.eilean.serverIpv4} -t nat -p tcp --dport 22 -j REDIRECT --to-port ${
128 builtins.toString cfg.gitea.sshPort
129 }
130 ip6tables -A OUTPUT -d ${config.eilean.serverIpv6} -t nat -p tcp --dport 22 -j REDIRECT --to-port ${
131 builtins.toString cfg.gitea.sshPort
132 }
133 '';
134 };
135
136 services.gitea.settings.server = {
137 START_SSH_SERVER = true;
138 SSH_LISTEN_PORT = cfg.gitea.sshPort;
139 };
140 };
141}