241 lines
8.8 KiB
Nix
241 lines
8.8 KiB
Nix
{ config, lib, pkgs, homeyConfig, ... }:
|
|
|
|
# Gitea — self-hosted Git service.
|
|
#
|
|
# Auth model: LDAP authentication is configured through Gitea's admin UI
|
|
# (or CLI) after first start. Reverse proxy auth headers from Caddy/Authelia
|
|
# handle transparent login.
|
|
#
|
|
# Volume layout:
|
|
# <dataDir>/gitea/data/ → /data (repos, sqlite db, avatars, lfs, etc.)
|
|
#
|
|
# Configuration strategy: all settings are passed as GITEA__<SECTION>__<KEY>
|
|
# environment variables. Gitea writes its own app.ini into /data/gitea/conf/
|
|
# on first start; the env vars override every key at runtime without touching
|
|
# that file. This avoids the bind-mount / read-only-fs problem where Gitea
|
|
# needs to rewrite its own config file on startup.
|
|
#
|
|
# Non-secret settings go in the `environment` block (they are fine in the
|
|
# Nix store). Secret settings go into /run/gitea-secrets.env via ExecStartPre
|
|
# (never in the store).
|
|
#
|
|
# Secrets consumed from sops:
|
|
# gitea/admin_password
|
|
# gitea/lfs_jwt_secret
|
|
# gitea/oauth2_jwt_secret
|
|
# gitea/internal_token
|
|
|
|
let
|
|
cfg = config.homey.gitea;
|
|
dataDir = config.homey.storage.mountPoint;
|
|
domain = homeyConfig.domain;
|
|
in
|
|
{
|
|
options.homey.gitea = {
|
|
enable = lib.mkEnableOption "Gitea Git server";
|
|
|
|
image = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "docker.io/gitea/gitea:latest";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 3000;
|
|
description = "Host port Gitea listens on (bound to 127.0.0.1).";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# -----------------------------------------------------------------------
|
|
# Secrets
|
|
# -----------------------------------------------------------------------
|
|
sops.secrets."gitea/admin_password" = { owner = "root"; };
|
|
sops.secrets."gitea/lfs_jwt_secret" = { owner = "root"; };
|
|
sops.secrets."gitea/oauth2_jwt_secret" = { owner = "root"; };
|
|
sops.secrets."gitea/internal_token" = { owner = "root"; };
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Container
|
|
# -----------------------------------------------------------------------
|
|
virtualisation.oci-containers.containers.gitea = {
|
|
image = cfg.image;
|
|
ports = [ "127.0.0.1:${toString cfg.port}:3000" ];
|
|
|
|
# All non-secret settings via GITEA__<SECTION>__<KEY> env vars.
|
|
# These are safe to store in the Nix store.
|
|
environment = {
|
|
USER_UID = "1000";
|
|
USER_GID = "1000";
|
|
GITEA_CUSTOM = "/data/gitea";
|
|
|
|
# [DEFAULT]
|
|
GITEA____APP_NAME = homeyConfig.organization;
|
|
GITEA____RUN_MODE = "prod";
|
|
|
|
# [repository]
|
|
GITEA__repository__ROOT = "/data/git/repositories";
|
|
|
|
# [server]
|
|
GITEA__server__APP_DATA_PATH = "/data/gitea";
|
|
GITEA__server__DOMAIN = "git.${domain}";
|
|
GITEA__server__HTTP_PORT = toString cfg.port;
|
|
GITEA__server__ROOT_URL = "https://git.${domain}/";
|
|
GITEA__server__DISABLE_SSH = "true";
|
|
GITEA__server__START_SSH_SERVER = "false";
|
|
GITEA__server__SSH_PORT = "2222";
|
|
GITEA__server__SSH_LISTEN_PORT = "2222";
|
|
GITEA__server__LFS_START_SERVER = "true";
|
|
GITEA__server__OFFLINE_MODE = "false";
|
|
|
|
# [lfs]
|
|
GITEA__lfs__PATH = "/data/git/lfs";
|
|
|
|
# [database]
|
|
GITEA__database__DB_TYPE = "sqlite3";
|
|
GITEA__database__PATH = "/data/gitea/gitea.db";
|
|
GITEA__database__LOG_SQL = "false";
|
|
|
|
# [indexer]
|
|
GITEA__indexer__ISSUE_INDEXER_PATH = "/data/gitea/indexers/issues.bleve";
|
|
|
|
# [session]
|
|
GITEA__session__PROVIDER = "file";
|
|
GITEA__session__PROVIDER_CONFIG = "/data/gitea/sessions";
|
|
|
|
# [picture]
|
|
GITEA__picture__AVATAR_UPLOAD_PATH = "/data/gitea/avatars";
|
|
GITEA__picture__REPOSITORY_AVATAR_UPLOAD_PATH = "/data/gitea/repo-avatars";
|
|
GITEA__picture__DISABLE_GRAVATAR = "false";
|
|
|
|
# [attachment]
|
|
GITEA__attachment__PATH = "/data/gitea/attachments";
|
|
|
|
# [log]
|
|
GITEA__log__MODE = "console";
|
|
GITEA__log__LEVEL = "info";
|
|
GITEA__log__ROOT_PATH = "/data/gitea/log";
|
|
|
|
# [security]
|
|
GITEA__security__INSTALL_LOCK = "true";
|
|
GITEA__security__REVERSE_PROXY_LIMIT = "1";
|
|
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES = "*";
|
|
|
|
# [service]
|
|
GITEA__service__DISABLE_REGISTRATION = "true";
|
|
GITEA__service__REQUIRE_SIGNIN_VIEW = "false";
|
|
GITEA__service__REGISTER_EMAIL_CONFIRM = "false";
|
|
GITEA__service__ENABLE_NOTIFY_MAIL = "false";
|
|
GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION = "true";
|
|
GITEA__service__ENABLE_CAPTCHA = "false";
|
|
GITEA__service__DEFAULT_ALLOW_CREATE_ORGANIZATION = "true";
|
|
GITEA__service__DEFAULT_ENABLE_TIMETRACKING = "true";
|
|
GITEA__service__NO_REPLY_ADDRESS = "noreply.localhost";
|
|
GITEA__service__ENABLE_REVERSE_PROXY_AUTHENTICATION = "true";
|
|
GITEA__service__ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = "true";
|
|
|
|
# [mailer]
|
|
GITEA__mailer__ENABLED = "false";
|
|
|
|
# [openid]
|
|
GITEA__openid__ENABLE_OPENID_SIGNIN = "false";
|
|
GITEA__openid__ENABLE_OPENID_SIGNUP = "false";
|
|
|
|
# [oauth2]
|
|
GITEA__oauth2__ENABLED = "false";
|
|
|
|
# [actions]
|
|
GITEA__actions__ENABLED = "true";
|
|
};
|
|
|
|
# Secret env vars written at runtime by ExecStartPre — never in store.
|
|
environmentFiles = [ "/run/gitea-secrets.env" ];
|
|
|
|
volumes = [
|
|
"${dataDir}/gitea/data:/data"
|
|
];
|
|
|
|
extraOptions = [ "--network=homey" ];
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# ExecStartPre: write ephemeral secrets env file
|
|
# ExecStopPost: clean it up
|
|
# -----------------------------------------------------------------------
|
|
systemd.services."podman-gitea" = {
|
|
serviceConfig = {
|
|
ExecStartPre = [
|
|
(pkgs.writeShellScript "gitea-write-secrets" ''
|
|
set -euo pipefail
|
|
LFS=$(cat ${config.sops.secrets."gitea/lfs_jwt_secret".path})
|
|
OAUTH=$(cat ${config.sops.secrets."gitea/oauth2_jwt_secret".path})
|
|
TOKEN=$(cat ${config.sops.secrets."gitea/internal_token".path})
|
|
printf '%s\n' \
|
|
"GITEA__server__LFS_JWT_SECRET=$LFS" \
|
|
"GITEA__security__INTERNAL_TOKEN=$TOKEN" \
|
|
"GITEA__oauth2__JWT_SECRET=$OAUTH" \
|
|
> /run/gitea-secrets.env
|
|
chmod 600 /run/gitea-secrets.env
|
|
'')
|
|
];
|
|
ExecStopPost = [
|
|
(pkgs.writeShellScript "gitea-cleanup-secrets" ''
|
|
rm -f /run/gitea-secrets.env
|
|
'')
|
|
];
|
|
};
|
|
after = lib.mkAfter [ "mnt-data.mount" "podman-openldap.service" "podman-homey-network.service" ];
|
|
requires = lib.mkAfter [ "mnt-data.mount" "podman-homey-network.service" ];
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Uptime Kuma monitor for this service
|
|
# -----------------------------------------------------------------------
|
|
homey.monitoring.monitors = [{
|
|
name = "Gitea";
|
|
url = "https://git.${domain}";
|
|
interval = 60;
|
|
}];
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Ensure the Gitea admin user exists with the correct password after start.
|
|
# Runs as a oneshot after podman-gitea; idempotent (create or update).
|
|
# -----------------------------------------------------------------------
|
|
systemd.services."gitea-admin-setup" = {
|
|
description = "Ensure Gitea admin user exists with correct password";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "podman-gitea.service" ];
|
|
requires = [ "podman-gitea.service" ];
|
|
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
};
|
|
|
|
script = ''
|
|
set -euo pipefail
|
|
PASS=$(cat ${config.sops.secrets."gitea/admin_password".path})
|
|
|
|
# Wait until Gitea's HTTP endpoint is up (max 60 s)
|
|
for i in $(seq 1 60); do
|
|
if ${pkgs.curl}/bin/curl -sf http://127.0.0.1:${toString cfg.port}/ -o /dev/null; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# Sync password if admin exists; create if not.
|
|
if ! ${pkgs.podman}/bin/podman exec -u 1000 gitea \
|
|
gitea admin user change-password --username admin --password "$PASS" 2>/dev/null; then
|
|
${pkgs.podman}/bin/podman exec -u 1000 gitea \
|
|
gitea admin user create \
|
|
--username admin \
|
|
--password "$PASS" \
|
|
--email "admin@${domain}" \
|
|
--admin
|
|
fi
|
|
'';
|
|
};
|
|
};
|
|
}
|