279 lines
8.2 KiB
Nix
279 lines
8.2 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
let
|
|
cfg = config.fudo.services.gitea-container;
|
|
|
|
hostname = config.instance.hostname;
|
|
domainName = config.fudo.hosts."${hostname}".domain;
|
|
zoneName = config.fudo.domains."${domainName}".zone;
|
|
siteName = config.fudo.hosts."${hostname}".site;
|
|
|
|
getSiteGatewayV4 = pkgs.lib.getSiteGatewayV4;
|
|
|
|
keyPaths = {
|
|
ed25519 = "/state/ssh/ed25519.key";
|
|
ecdsa = "/state/ssh/ecdsa.key";
|
|
};
|
|
|
|
in {
|
|
options.fudo.services.gitea-container = with types; {
|
|
enable = mkEnableOption "Enable Gitea running in a container.";
|
|
|
|
site-name = mkOption {
|
|
type = str;
|
|
description = "Name of this Gitea instance.";
|
|
};
|
|
|
|
hostname = mkOption {
|
|
type = str;
|
|
description = "Hostname at which the server is reachable.";
|
|
};
|
|
|
|
state-directory = mkOption {
|
|
type = str;
|
|
description = "Path at which to store Gitea state.";
|
|
};
|
|
|
|
trusted-networks = mkOption {
|
|
type = listOf str;
|
|
description =
|
|
"List of networks to be considered trusted (for metrics access).";
|
|
default = [ ];
|
|
};
|
|
|
|
networking = {
|
|
interface = mkOption {
|
|
type = str;
|
|
description = "Parent host interface on which to listen.";
|
|
};
|
|
|
|
ipv4 = mkOption {
|
|
type = nullOr (submodule {
|
|
options = {
|
|
address = mkOption {
|
|
type = str;
|
|
description = "IP address.";
|
|
};
|
|
prefixLength = mkOption {
|
|
type = int;
|
|
description = "Significant bits in the address.";
|
|
};
|
|
};
|
|
});
|
|
default = null;
|
|
};
|
|
|
|
ipv6 = mkOption {
|
|
type = nullOr (submodule {
|
|
options = {
|
|
address = mkOption {
|
|
type = str;
|
|
description = "IP address.";
|
|
};
|
|
prefixLength = mkOption {
|
|
type = int;
|
|
description = "Significant bits in the address.";
|
|
};
|
|
};
|
|
});
|
|
default = null;
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
systemd.tmpfiles.rules = [
|
|
"d ${cfg.state-directory}/gitea 755 root root - -"
|
|
"d ${cfg.state-directory}/acme 755 root root - -"
|
|
];
|
|
|
|
containers.gitea = {
|
|
autoStart = true;
|
|
additionalCapabilities = [ "CAP_NET_ADMIN" ];
|
|
macvlans = [ cfg.networking.interface ];
|
|
bindMounts = {
|
|
"/state" = {
|
|
hostPath = "${cfg.state-directory}/gitea";
|
|
isReadOnly = false;
|
|
};
|
|
"/var/lib/acme" = {
|
|
hostPath = "${cfg.state-directory}/acme";
|
|
isReadOnly = false;
|
|
};
|
|
};
|
|
config = {
|
|
nixpkgs.pkgs = pkgs;
|
|
|
|
systemd = {
|
|
tmpfiles.rules = [ "d /state 0755 root root - -" ];
|
|
services = {
|
|
gitea-chown = {
|
|
requiredBy = [ "gitea.service" ];
|
|
before = [ "gitea.service" ];
|
|
serviceConfig.Type = "oneshot";
|
|
script = "chown -R gitea:gitea /state";
|
|
};
|
|
|
|
gitea-keygen = {
|
|
requiredBy = [ "gitea.service" ];
|
|
before = [ "gitea.service" ];
|
|
serviceConfig.Type = "oneshot";
|
|
script = let
|
|
keygenScripts = mapAttrsToList (type: path:
|
|
let dir = dirOf path;
|
|
in ''
|
|
if [ ! -f ${path} ]; then
|
|
mkdir -p ${dir}
|
|
${pkgs.openssh}/bin/ssh-keygen -q -N "" -t ${type} -f ${path}
|
|
chown -R gitea:gitea ${dir}
|
|
chmod 0750 ${dir}
|
|
chmod 0440 ${path}
|
|
fi
|
|
'') keyPaths;
|
|
in concatStringsSep "\n" keygenScripts;
|
|
};
|
|
};
|
|
};
|
|
|
|
environment.systemPackages = let
|
|
giteaCli = pkgs.writeShellApplication {
|
|
name = "gitea-cli";
|
|
runtimeInputs = with pkgs; [ gitea ];
|
|
text = ''gitea --config /state/gitea/custom/conf/app.ini "$@"'';
|
|
};
|
|
in [ giteaCli ];
|
|
|
|
networking = {
|
|
defaultGateway = {
|
|
address = getSiteGatewayV4 siteName;
|
|
interface = "mv-${cfg.networking.interface}";
|
|
};
|
|
enableIPv6 = !isNull cfg.networking.ipv6;
|
|
nameservers = config.networking.nameservers;
|
|
firewall = {
|
|
enable = true;
|
|
allowedTCPPorts = [ 22 80 443 ];
|
|
};
|
|
interfaces."mv-${cfg.networking.interface}" = {
|
|
ipv4.addresses = optional (!isNull cfg.networking.ipv4) {
|
|
address = cfg.networking.ipv4.address;
|
|
prefixLength = cfg.networking.ipv4.prefixLength;
|
|
};
|
|
ipv6.addresses = optional (!isNull cfg.networking.ipv6) {
|
|
address = cfg.networking.ipv6.address;
|
|
prefixLength = cfg.networking.ipv6.prefixLength;
|
|
};
|
|
};
|
|
};
|
|
|
|
security.acme = {
|
|
acceptTerms = true;
|
|
defaults.email = "admin@${cfg.hostname}";
|
|
};
|
|
|
|
services = {
|
|
gitea = {
|
|
enable = true;
|
|
appName = cfg.site-name;
|
|
database = {
|
|
createDatabase = true;
|
|
type = "sqlite3";
|
|
};
|
|
repositoryRoot = "/state/repositories";
|
|
stateDir = "/state/gitea";
|
|
settings = {
|
|
service = {
|
|
#DISABLE_REGISTRATION = true;
|
|
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
|
|
};
|
|
security = {
|
|
INSTALL_LOCK = true;
|
|
LOGIN_REMEMBER_DAYS = 30;
|
|
};
|
|
metrics.ENABLED = cfg.trusted-networks != [ ];
|
|
server = {
|
|
START_SSH_SERVER = true;
|
|
|
|
# Host & port to display in the clone URL
|
|
SSH_DOMAIN = cfg.hostname;
|
|
# SSH_LISTEN_HOST = "0.0.0.0";
|
|
SSH_PORT = 22;
|
|
SSH_LISTEN_PORT = 2222;
|
|
DOMAIN = cfg.hostname;
|
|
ROOT_URL = "https://${cfg.hostname}";
|
|
|
|
SSH_SERVER_HOST_KEYS =
|
|
concatStringsSep "," (attrValues keyPaths);
|
|
|
|
HTTP_ADDR = "127.0.0.1";
|
|
HTTP_PORT = 8080;
|
|
};
|
|
oauth2_client = {
|
|
REGISTER_EMAIL_CONFIRM = false;
|
|
OPENID_CONNECT_SCOPES =
|
|
concatStringsSep "," [ "email" "profile" ];
|
|
#ENABLE_AUTO_REGISTRATION = true;
|
|
USERNAME = "email";
|
|
UPDATE_AVATAR = true;
|
|
ACCOUNT_LINKING = "login";
|
|
};
|
|
"git.timeout" = {
|
|
DEFAULT = 720;
|
|
MIGRATE = 3600;
|
|
MIRROR = 3600;
|
|
CLONE = 1200;
|
|
PULL = 1200;
|
|
GC = 600;
|
|
};
|
|
};
|
|
};
|
|
|
|
xinetd = {
|
|
enable = true;
|
|
services = [{
|
|
name = "ssh";
|
|
port = 22;
|
|
protocol = "tcp";
|
|
extraConfig = ''
|
|
redirect = localhost 2222
|
|
wait = no
|
|
socket_type = stream
|
|
'';
|
|
user = "nobody";
|
|
# Must be defined, but not used
|
|
server = "/usr/bin/env";
|
|
# unlisted = true;
|
|
}];
|
|
};
|
|
|
|
nginx = {
|
|
enable = true;
|
|
recommendedOptimisation = true;
|
|
recommendedProxySettings = true;
|
|
recommendedTlsSettings = true;
|
|
recommendedGzipSettings = true;
|
|
virtualHosts."${cfg.hostname}" = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
locations."/" = {
|
|
proxyPass = "http://127.0.0.1:8080";
|
|
proxyWebsockets = true;
|
|
recommendedProxySettings = true;
|
|
};
|
|
locations."/metrics" = mkIf (cfg.trusted-networks != [ ]) {
|
|
proxyPass = "http://127.0.0.1:8080/metrics";
|
|
extraConfig = let
|
|
networkAllowClauses =
|
|
map (net: "allow ${net};") cfg.trusted-networks;
|
|
in concatStringsSep "\n"
|
|
(networkAllowClauses ++ [ "deny all;" ]);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|