206 lines
7 KiB
Org Mode
206 lines
7 KiB
Org Mode
#+title: Forgejo
|
|
|
|
* Forgejo Configuration
|
|
There is an accompanying piece of documentation created by Claude called README-forgejo-setup.md. It is located in ~/Downloads
|
|
#+begin_src nix :noweb yes :tangle ~/Development/falcon_nest/virtualization/forgejo.nix
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
domain = <<forgejo-domain>>;
|
|
acmeEmail = <<forgejo-email>>;
|
|
secretsDir = <<forgejo-secrets>>;
|
|
dockerJobImage = <<forgejo-job-image>>;
|
|
in
|
|
{
|
|
<<forgejo-setup>>
|
|
}
|
|
#+end_src
|
|
** Domain
|
|
#+begin_src nix :noweb-ref forgejo-domain
|
|
"git.onionpatch.cloud"
|
|
#+end_src
|
|
** Email
|
|
#+begin_src nix :noweb-ref forgejo-email
|
|
"shawn@shawnborton.info"
|
|
#+end_src
|
|
** Secrets Dir
|
|
#+begin_src nix :noweb-ref forgejo-secrets
|
|
"/etc/forgejo/secrets"
|
|
#+end_src
|
|
** Job Image
|
|
A general-purpose Actions job image. It ships Node.js (required by JS-based actions such as actions/checkout), git, bash and common CLI tools, without the ~18GB footprint of the fully-loaded "act-latest" image. Terraform, Python and pandoc are installed per-job via setup actions / apt-get -- see the example workflows in .forgejo/workflows/.
|
|
#+begin_src nix :noweb-ref forgejo-job-image
|
|
"docker://catthehacker/ubuntu:runner-latest"
|
|
#+end_src
|
|
** Forgejo User
|
|
services.forgejo already creates and runs the service as a user named "forgejo" by default. We declare it explicitly here so it's visible in the config, and give it an interactive shell so you can `sudo -u forgejo -i` for occasional admin/CLI work (creating the first admin account, running `forgejo admin ...`, etc).
|
|
|
|
The Actions runner (section 4) intentionally does NOT run as this user -- it's managed by its own module under a separate, dynamically allocated identity, so a compromised CI job can't directly touch the Forgejo server's state or the "docker" group.
|
|
#+begin_src nix :noweb-ref forgejo-user
|
|
users.users.forgejo = {
|
|
isSystemUser = true;
|
|
# group = "podman";
|
|
description = "Forgejo git service";
|
|
linger = true;
|
|
# createHome = true;
|
|
autoSubUidGidRange = true;
|
|
};
|
|
#+end_src
|
|
** Storage
|
|
Only repository data and LFS objects live on the network share. Forgejo's own app state (config, DB-adjacent files, queues, indexer) stays on local disk at the default /var/lib/forgejo -- changing `stateDir` is known to break the service on some nixpkgs releases (see NixOS/nixpkgs#385595), and app state doesn't need to survive on the network mount anyway.
|
|
|
|
CAVEAT: CIFS does not have the same locking/hardlink semantics as a local filesystem or NFS. Git operations are mostly fine over CIFS in practice, but if you see intermittent "unable to lock" or corrupted pack errors under concurrent load, consider switching this mount to NFS instead.
|
|
Make sure forgejo.service won't start (or will block) until the share is actually mounted.
|
|
#+begin_src nix :noweb-ref forgejo-storage
|
|
systemd.services.forgejo.serviceConfig.RequiresMountsFor = [ "/mnt/forgejo" ];
|
|
#+end_src
|
|
** Setup
|
|
And now, forgejo itself. We'll use PostgreSQL for our backend.
|
|
|
|
The system already has an SSH daemon setup and chugging along nicely. No need to reinvent the wheel. We'll disable registration and use the CLI to create the first user. Finally, we'll allow future runners to use Github style runner setups.
|
|
|
|
Forgejo's own app state (`app.ini`, secrets it generates on first boot, queues, search index) stays on local disk at the default `/var/lib/forgejo`. Redirecting that whole directory to a network mount is known to break the service outright on some nixpkgs versions.
|
|
#+begin_src nix :noweb-ref forgejo-setup
|
|
services.forgejo = {
|
|
enable = true;
|
|
user = "forgejo";
|
|
group = "forgejo";
|
|
database.type = "postgres";
|
|
repositoryRoot = "/mnt/forgejo/repositories";
|
|
|
|
settings = {
|
|
server = {
|
|
# DISABLE_SSH = true;
|
|
DOMAIN = domain;
|
|
ROOT_URL = "https://${domain}/";
|
|
# HTTP_ADDR = "127.0.0.1";
|
|
HTTP_ADDR = "0.0.0.0";
|
|
# HTTP_PORT = 3000;
|
|
# PROTOCOL = "https";
|
|
SSH_PORT = 2112;
|
|
# SSH_PORT = lib.head config.services.openssh.ports;
|
|
};
|
|
|
|
service = {
|
|
DISABLE_REGISTRATION = false;
|
|
};
|
|
|
|
lfs = {
|
|
enable = true;
|
|
contentDir = "/mnt/forgejo/lfs";
|
|
};
|
|
|
|
actions = {
|
|
ENABLED = true;
|
|
DEFAULT_ACTIONS_URL = "github";
|
|
};
|
|
};
|
|
};
|
|
#+end_src
|
|
** Reverse Proxy + TLS with Let's Encrypt and ACME
|
|
#+begin_src nix :noweb-ref forgejo-proxy
|
|
# security.acme = {
|
|
# acceptTerms = true;
|
|
# defaults.email = acmeEmail;
|
|
# };
|
|
|
|
services.nginx = {
|
|
enable = true;
|
|
recommendedProxySettings = true;
|
|
recommendedTlsSettings = true;
|
|
|
|
virtualHosts."${domain}" = {
|
|
# forceSSL = true;
|
|
# enableACME = true;
|
|
extraConfig = ''
|
|
client_max_body_size 512M;
|
|
'';
|
|
locations."/" = {
|
|
proxyPass = "http://127.0.0.1:3000";
|
|
proxyWebsockets = true;
|
|
};
|
|
};
|
|
};
|
|
#+end_src
|
|
** Runners
|
|
Coming soon...
|
|
|
|
The Actions runner is kept separate on purpose: the docker-executor instance needs membership in the `podman` group, which is effectively root-equivalent (anyone in that group can mount the host filesystem via a container). Giving that same access to the account that owns your git data and admin session would mean a compromised CI job could reach the Forgejo server directly.
|
|
#+begin_src nix :noweb-ref forgejo-runners
|
|
services.gitea-actions-runner = {
|
|
package = pkgs.forgejo-runner;
|
|
|
|
instances = {
|
|
podman = {
|
|
enable = true;
|
|
name = "podman-runner";
|
|
url = "https://${domain}";
|
|
tokenFile = "${secretsDir}/forgejo-runner-token";
|
|
labels = [ "docker:${dockerJobImage}" ];
|
|
settings = {
|
|
runner = {
|
|
capacity = 2; # up to 2 concurrent containerized jobs
|
|
timeout = "3h";
|
|
};
|
|
};
|
|
};
|
|
|
|
nix-host = {
|
|
enable = true;
|
|
name = "nix-host-runner";
|
|
url = "https://${domain}";
|
|
tokenFile = "${secretsDir}/forgejo-runner-token";
|
|
labels = [ "nix-host:host" ];
|
|
hostPackages = with pkgs; [
|
|
nix
|
|
git
|
|
bash
|
|
coreutils
|
|
gnutar
|
|
gzip
|
|
nodejs_20 # needed by JS-based actions like actions/checkout
|
|
cacert
|
|
];
|
|
settings = {
|
|
runner = {
|
|
capacity = 1; # Nix builds are heavy; keep this to 1 at a time
|
|
timeout = "3h";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
#+end_src
|
|
** Secret Placeholders
|
|
These files are created with dummy content ONLY if they don't already exist, so they survive `nixos-rebuild switch` without being clobbered once you've filled them in. Fill them in BEFORE your first switch where possible -- see README-forgejo-setup.md.
|
|
#+begin_src nix :noweb-ref forgejo-placeholders
|
|
system.activationScripts.forgejoSecretPlaceholders = ''
|
|
mkdir -p ${secretsDir}
|
|
chmod 700 ${secretsDir}
|
|
|
|
if [ ! -f ${secretsDir}/forgejo-runner-token ]; then
|
|
cat > ${secretsDir}/forgejo-runner-token <<'EOF'
|
|
TOKEN=CHANGEME-generate-after-first-boot
|
|
EOF
|
|
|
|
chmod 600 ${secretsDir}/forgejo-runner-token
|
|
fi
|
|
'';
|
|
#+end_src
|
|
* Notes
|
|
** Creating users
|
|
Since public registration is disabled by default, you'll need to run this command in the terminal
|
|
#+begin_src shell
|
|
sudo forgejo-cli admin user create \
|
|
--admin --username shawnb \
|
|
--email shawn@shawnborton.info \
|
|
--password 'qwZem4CxmT9j'
|
|
#+end_src
|
|
|
|
|