diff options
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/cobalt/configuration.nix | 145 | ||||
-rw-r--r-- | nixos/cobalt/hardware-configuration.nix | 65 | ||||
-rw-r--r-- | nixos/cobalt/modules/git-daemon.nix | 137 | ||||
-rw-r--r-- | nixos/cobalt/modules/soju.nix | 132 | ||||
-rw-r--r-- | nixos/cobalt/services/README.md | 5 | ||||
-rw-r--r-- | nixos/cobalt/services/acme.nix | 26 | ||||
-rw-r--r-- | nixos/cobalt/services/cgit.nix | 105 | ||||
-rw-r--r-- | nixos/cobalt/services/fail2ban.nix | 5 | ||||
-rw-r--r-- | nixos/cobalt/services/git-daemon.nix | 15 | ||||
-rw-r--r-- | nixos/cobalt/services/gitolite-noncore/fix-refs | 9 | ||||
-rw-r--r-- | nixos/cobalt/services/gitolite-noncore/rename | 62 | ||||
-rw-r--r-- | nixos/cobalt/services/gitolite.nix | 109 | ||||
-rw-r--r-- | nixos/cobalt/services/nginx.nix | 15 | ||||
-rw-r--r-- | nixos/cobalt/services/soju.nix | 26 | ||||
-rw-r--r-- | nixos/colmena.nix | 28 |
15 files changed, 884 insertions, 0 deletions
diff --git a/nixos/cobalt/configuration.nix b/nixos/cobalt/configuration.nix new file mode 100644 index 0000000..c596536 --- /dev/null +++ b/nixos/cobalt/configuration.nix @@ -0,0 +1,145 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). + +{ config, pkgs, lib, ... }: +let + ipv4 = { + address = "95.216.74.104"; + gateway = "95.216.74.65"; + netmask = "255.255.255.192"; + prefixLength = 26; # https://www.pawprint.net/designresources/netmask-converter.php + }; + ipv6 = { + address = "2a01:4f9:2b:a98::"; + gateway = "fe80::1"; + prefixLength = 64; + }; + networkInterface = "eth0"; + hostName = "cobalt"; + hostId = "712ae82a"; +in +{ + imports = + [ + # Include the results of the hardware scan. + ./hardware-configuration.nix + + ./services/acme.nix + ./services/nginx.nix + ./services/fail2ban.nix + ./services/soju.nix + ./services/gitolite.nix + ./services/git-daemon.nix + ./services/cgit.nix + ]; + + boot.supportedFilesystems = [ "zfs" ]; + networking.hostId = hostId; + + boot.loader.grub.enable = true; + # boot.loader.grub.version = 2; + boot.loader.grub.efiSupport = false; + # boot.loader.grub.device = "nodev"; + + # This should be done automatically, but explicitly declare it just in case. + boot.loader.grub.copyKernels = true; + # Make sure that you've listed all of the boot partitions here. + boot.loader.grub.mirroredBoots = [ + { path = "/boot"; devices = [ "/dev/disk/by-id/ata-ST4000NM0245-1Z2107_ZC17GW7G" ]; } + { path = "/boot-fallback"; devices = [ "/dev/disk/by-id/ata-ST4000NM0245-1Z2107_ZC17GWB2" ]; } + ]; + + # Boot normally when one of the boot partitions are missing + fileSystems."/boot".options = [ "nofail" ]; + fileSystems."/boot-fallback".options = [ "nofail" ]; + + # Erase your darlings + boot.initrd.postDeviceCommands = lib.mkAfter '' + zfs rollback -r rpool/local/root@blank + ''; + + # NOTE: replace these to boot.initrd.availableKernelModules? + boot.kernelModules = [ "e1000e" ]; + boot.initrd.kernelModules = [ "e1000e" ]; + + boot.kernelParams = [ + # See <https:#www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt> for documentation. + # ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip> + # The server ip refers to the NFS server -- not needed in this case. + "ip=${ipv4.address}::${ipv4.gateway}:${ipv4.netmask}:${hostName}-initrd:${networkInterface}:off:8.8.8.8" + ]; + + boot.initrd.network.enable = true; + boot.initrd.network.ssh = { + enable = true; + + # Using the same port as the actual SSH will cause clients to throw errors + # related to host key mismatch. + port = 2222; + + # This takes 'path's, not 'string's. + hostKeys = [ + /boot/initrd-ssh-key + /boot-fallback/initrd-ssh-key + ]; + + # Public ssh key to log into the initrd ssh + authorizedKeys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDi7GGOGVj1Y5Sc1EW6zEdrp78dS6hvmS348pqu9dUsB openpgp:0x6BE7BD6F" ]; + }; + boot.initrd.network.postCommands = '' + cat <<EOF > /root/.profile + if pgrep -x "zfs" > /dev/null + then + zfs load-key -a + killall zfs + else + echo "ZFS is not running -- this could be a sign of failure." + fi + EOF + ''; + + + networking.hostName = hostName; # Define your hostname. + + networking.useDHCP = false; + networking.interfaces.${networkInterface} = { + ipv4 = { addresses = [{ address = ipv4.address; prefixLength = ipv4.prefixLength; }]; }; + ipv6 = { addresses = [{ address = ipv6.address; prefixLength = ipv6.prefixLength; }]; }; + }; + networking.defaultGateway = ipv4.gateway; + networking.defaultGateway6 = { address = ipv6.gateway; interface = networkInterface; }; + networking.nameservers = [ "8.8.8.8" ]; + + # Set your time zone. + time.timeZone = "UTC"; + + users.users.root.initialHashedPassword = ""; + users.users.root.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDi7GGOGVj1Y5Sc1EW6zEdrp78dS6hvmS348pqu9dUsB openpgp:0x6BE7BD6F" ]; + services.openssh.enable = true; + services.openssh.permitRootLogin = "prohibit-password"; + # mkdir -p /persist/etc/ssh + services.openssh.hostKeys = [ + { + path = "/persist/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + } + { + path = "/persist/ssh/ssh_host_rsa_key"; + type = "rsa"; + bits = 4096; + } + ]; + + # impermanence requirement + fileSystems."/persist".neededForBoot = true; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? +} + diff --git a/nixos/cobalt/hardware-configuration.nix b/nixos/cobalt/hardware-configuration.nix new file mode 100644 index 0000000..95ecb96 --- /dev/null +++ b/nixos/cobalt/hardware-configuration.nix @@ -0,0 +1,65 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { + device = "rpool/local/root"; + fsType = "zfs"; + }; + + fileSystems."/boot" = + { + device = "/dev/disk/by-uuid/445A-0C55"; + fsType = "vfat"; + }; + + fileSystems."/boot-fallback" = + { + device = "/dev/disk/by-uuid/445C-198F"; + fsType = "vfat"; + }; + + fileSystems."/nix" = + { + device = "rpool/local/nix"; + fsType = "zfs"; + }; + + fileSystems."/home" = + { + device = "rpool/safe/home"; + fsType = "zfs"; + }; + + fileSystems."/persist" = + { + device = "rpool/safe/persist"; + fsType = "zfs"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`. + networking.useDHCP = lib.mkDefault false; + # networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/nixos/cobalt/modules/git-daemon.nix b/nixos/cobalt/modules/git-daemon.nix new file mode 100644 index 0000000..76b395e --- /dev/null +++ b/nixos/cobalt/modules/git-daemon.nix @@ -0,0 +1,137 @@ +{ config, lib, pkgs, ... }: +with lib; +let + + cfg = config.services.gitDaemon; + +in +{ + + ###### interface + + options = { + services.gitDaemon = { + + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable Git daemon, which allows public hosting of git repositories + without any access controls. This is mostly intended for read-only access. + + You can allow write access by setting daemon.receivepack configuration + item of the repository to true. This is solely meant for a closed LAN setting + where everybody is friendly. + + If you need any access controls, use something else. + ''; + }; + + basePath = mkOption { + type = types.str; + default = ""; + example = "/srv/git/"; + description = lib.mdDoc '' + Remap all the path requests as relative to the given path. For example, + if you set base-path to /srv/git, then if you later try to pull + git://example.com/hello.git, Git daemon will interpret the path as /srv/git/hello.git. + ''; + }; + + exportAll = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Publish all directories that look like Git repositories (have the objects + and refs subdirectories), even if they do not have the git-daemon-export-ok file. + + If disabled, you need to touch .git/git-daemon-export-ok in each repository + you want the daemon to publish. + + Warning: enabling this without a repository whitelist or basePath + publishes every git repository you have. + ''; + }; + + repositories = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "/srv/git" "/home/user/git/repo2" ]; + description = lib.mdDoc '' + A whitelist of paths of git repositories, or directories containing repositories + all of which would be published. Paths must not end in "/". + + Warning: leaving this empty and enabling exportAll publishes all + repositories in your filesystem or basePath if specified. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = ""; + example = "example.com"; + description = lib.mdDoc "Listen on a specific IP address or hostname."; + }; + + port = mkOption { + type = types.port; + default = 9418; + description = lib.mdDoc "Port to listen on."; + }; + + options = mkOption { + type = types.str; + default = ""; + description = lib.mdDoc "Extra configuration options to be passed to Git daemon."; + }; + + user = mkOption { + type = types.str; + default = "git"; + description = lib.mdDoc "User under which Git daemon would be running."; + }; + + group = mkOption { + type = types.str; + default = "git"; + description = lib.mdDoc "Group under which Git daemon would be running."; + }; + + createUserAndGroup = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Create the specified group and user. + Disable this option if you want to use the existing user + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + users.users.${cfg.user} = optionalAttrs (cfg.createUserAndGroup == true) { + uid = config.ids.uids.git; + group = cfg.group; + description = "Git daemon user"; + }; + + users.groups.${cfg.group} = optionalAttrs (cfg.createUserAndGroup == true) { + gid = config.ids.gids.git; + }; + + systemd.services.git-daemon = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.git}/bin/git daemon --reuseaddr " + + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ") + + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ") + + "--port=${toString cfg.port} --user=${cfg.user} --group=${cfg.group} ${cfg.options} " + + "--verbose " + (optionalString cfg.exportAll "--export-all ") + concatStringsSep " " cfg.repositories; + }; + + }; + +} diff --git a/nixos/cobalt/modules/soju.nix b/nixos/cobalt/modules/soju.nix new file mode 100644 index 0000000..d14082c --- /dev/null +++ b/nixos/cobalt/modules/soju.nix @@ -0,0 +1,132 @@ +# Not an overlay, module replacement +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.soju; + stateDir = "/var/lib/soju"; + listenCfg = concatMapStringsSep "\n" (l: "listen ${l}") cfg.listen; + tlsCfg = optionalString (cfg.tlsCertificate != null) + "tls ${cfg.tlsCertificate} ${cfg.tlsCertificateKey}"; + logCfg = optionalString cfg.enableMessageLogging + "log fs ${stateDir}/logs"; + + configFile = pkgs.writeText "soju.conf" '' + ${listenCfg} + hostname ${cfg.hostName} + ${tlsCfg} + db sqlite3 ${stateDir}/soju.db + ${logCfg} + http-origin ${concatStringsSep " " cfg.httpOrigins} + accept-proxy-ip ${concatStringsSep " " cfg.acceptProxyIP} + + ${cfg.extraConfig} + ''; +in +{ + ###### interface + + options.services.soju = { + enable = mkEnableOption (lib.mdDoc "soju"); + + listen = mkOption { + type = types.listOf types.str; + default = [ ":6697" ]; + description = lib.mdDoc '' + Where soju should listen for incoming connections. See the + `listen` directive in + {manpage}`soju(1)`. + ''; + }; + + hostName = mkOption { + type = types.str; + default = config.networking.hostName; + defaultText = literalExpression "config.networking.hostName"; + description = lib.mdDoc "Server hostname."; + }; + + tlsCertificate = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/host.cert"; + description = lib.mdDoc "Path to server TLS certificate."; + }; + + tlsCertificateKey = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/host.key"; + description = lib.mdDoc "Path to server TLS certificate key."; + }; + + enableMessageLogging = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc "Whether to enable message logging."; + }; + + httpOrigins = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + List of allowed HTTP origins for WebSocket listeners. The parameters are + interpreted as shell patterns, see + {manpage}`glob(7)`. + ''; + }; + + acceptProxyIP = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + Allow the specified IPs to act as a proxy. Proxys have the ability to + overwrite the remote and local connection addresses (via the X-Forwarded-\* + HTTP header fields). The special name "localhost" accepts the loopback + addresses 127.0.0.0/8 and ::1/128. By default, all IPs are rejected. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = lib.mdDoc "Lines added verbatim to the configuration file."; + }; + + extraGroups = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc "Extra groups for the dynamic user."; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + assertions = [ + { + assertion = (cfg.tlsCertificate != null) == (cfg.tlsCertificateKey != null); + message = '' + services.soju.tlsCertificate and services.soju.tlsCertificateKey + must both be specified to enable TLS. + ''; + } + ]; + + systemd.services.soju = { + description = "soju IRC bouncer"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + serviceConfig = { + DynamicUser = true; + SupplementaryGroups = cfg.extraGroups; + Restart = "always"; + ExecStart = "${pkgs.soju}/bin/soju -config ${configFile}"; + StateDirectory = "soju"; + }; + }; + }; + + meta.maintainers = with maintainers; [ malvo ]; +} diff --git a/nixos/cobalt/services/README.md b/nixos/cobalt/services/README.md new file mode 100644 index 0000000..89d9ca5 --- /dev/null +++ b/nixos/cobalt/services/README.md @@ -0,0 +1,5 @@ +# colmena/cobalt/services + +A list of 'pluggable' services. +TODO: this should be moved to /modules/ and +converted to modules. diff --git a/nixos/cobalt/services/acme.nix b/nixos/cobalt/services/acme.nix new file mode 100644 index 0000000..b41ae1c --- /dev/null +++ b/nixos/cobalt/services/acme.nix @@ -0,0 +1,26 @@ +let + poorObfuscation = y: x: "${x}@${y}"; +in +{ + security.acme = { + acceptTerms = true; + defaults.email = poorObfuscation "sefidel.com" "postmaster"; + certs = { + "sefidel.com" = { + domain = "*.sefidel.com"; + dnsProvider = "hetzner"; + dnsPropagationCheck = true; + credentialsFile = "/persist/secrets/hetzner.key"; + }; + }; + }; + + environment.persistence."/persist".directories = [ + "/var/lib/acme" + ]; + + deployment.keys."hetzner.key" = { + keyCommand = [ "pass" "show" "server/hetzner-dns" ]; + destDir = "/persist/secrets"; + }; +} diff --git a/nixos/cobalt/services/cgit.nix b/nixos/cobalt/services/cgit.nix new file mode 100644 index 0000000..4e030c8 --- /dev/null +++ b/nixos/cobalt/services/cgit.nix @@ -0,0 +1,105 @@ +{ pkgs, ... }: + +{ + services.uwsgi = { + enable = true; + user = "nginx"; + group = "nginx"; + plugins = [ "cgi" ]; + + instance = { + type = "emperor"; + vassals = { + cgit = { + type = "normal"; + master = true; + socket = "/run/uwsgi/cgit.sock"; + procname-master = "uwsgi cgit"; + plugins = [ "cgi" ]; + cgi = "${pkgs.cgit-pink}/cgit/cgit.cgi"; + }; + }; + }; + }; + + users.extraUsers.nginx.extraGroups = [ "git" ]; + + services.nginx.virtualHosts."git.sefidel.com" = { + addSSL = true; + useACMEHost = "sefidel.com"; + root = "${pkgs.cgit-pink}/cgit"; + locations = { + "/" = { + extraConfig = '' + try_files $uri @cgit; + ''; + }; + "@cgit" = { + extraConfig = '' + uwsgi_pass unix:/run/uwsgi/cgit.sock; + include ${pkgs.nginx}/conf/uwsgi_params; + uwsgi_modifier1 9; + ''; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + systemd.services.create-cgit-cache = { + description = "Create cache directory for cgit"; + enable = true; + + script = '' + mkdir -p /run/cgit + chown -R nginx:nginx /run/cgit + ''; + + wantedBy = [ "uwsgi.service" ]; + serviceConfig = { + Type = "oneshot"; + }; + }; + + environment.etc."cgitrc".text = '' + virtual-root=/ + + cache-size=1000 + cache-root=/run/cgit + + root-title=sefidel git + root-desc=Exotic place. + + snapshots=tar.gz zip + + enable-git-config=1 + remove-suffix=1 + + enable-git-clone=1 + enable-index-links=1 + enable-commit-graph=1 + enable-log-filecount=1 + enable-log-linecount=1 + + branch-sort=age + + readme=:README + readme=:readme + readme=:README.md + readme=:readme.md + readme=:README.org + readme=:readme.org + + source-filter=${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py + about-filter=${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh + + section-from-path=2 + + project-list=/var/lib/gitolite/projects.list + scan-path=/var/lib/gitolite/repositories + ''; + + imports = [ + ./nginx.nix + ]; +} diff --git a/nixos/cobalt/services/fail2ban.nix b/nixos/cobalt/services/fail2ban.nix new file mode 100644 index 0000000..9731ef6 --- /dev/null +++ b/nixos/cobalt/services/fail2ban.nix @@ -0,0 +1,5 @@ +{ + services.fail2ban = { + enable = true; + }; +} diff --git a/nixos/cobalt/services/git-daemon.nix b/nixos/cobalt/services/git-daemon.nix new file mode 100644 index 0000000..21e957e --- /dev/null +++ b/nixos/cobalt/services/git-daemon.nix @@ -0,0 +1,15 @@ +{ + services.gitDaemon = { + enable = true; + createUserAndGroup = false; + basePath = "/var/lib/gitolite/repositories"; + }; + + networking.firewall.allowedTCPPorts = [ 9418 ]; + + disabledModules = [ "services/networking/git-daemon.nix" ]; + + imports = [ + ../modules/git-daemon.nix + ]; +} diff --git a/nixos/cobalt/services/gitolite-noncore/fix-refs b/nixos/cobalt/services/gitolite-noncore/fix-refs new file mode 100644 index 0000000..8ffec9e --- /dev/null +++ b/nixos/cobalt/services/gitolite-noncore/fix-refs @@ -0,0 +1,9 @@ +[[ $4 == W ]] || exit 0 + +cd $GL_REPO_BASE/$2.git + +head=`git symbolic-ref HEAD` +[[ -f $head ]] || { + set -- refs/heads/* + git symbolic-ref HEAD $1 +} diff --git a/nixos/cobalt/services/gitolite-noncore/rename b/nixos/cobalt/services/gitolite-noncore/rename new file mode 100644 index 0000000..00aa5ca --- /dev/null +++ b/nixos/cobalt/services/gitolite-noncore/rename @@ -0,0 +1,62 @@ + +# Usage: ssh git@host rename [-c] <repo1> <repo2> +# +# Renames repo1 to repo2. You must be the creator of repo1, and have +# create ("C") permissions for repo2, which of course must not exist. +# Alternatively you must be an account admin, that is, you must have +# write access to the gitolite-admin repository. If you have "C" +# permissions for repo2 then you can use the -c option to take over +# as creator of the repository. + +die() { echo "$@" >&2; exit 1; } +usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; } +[ -z "$1" ] && usage +[ "$1" = "-h" ] && usage +[ -z "$GL_USER" ] && die GL_USER not set + +# ---------------------------------------------------------------------- + +if [ "$1" = "-c" ] +then shift + takeover=true +else takeover=false +fi + +from="$1"; shift +to="$1"; shift +[ -z "$to" ] && usage + +topath=$GL_REPO_BASE/$to.git + +checkto() { + gitolite access -q "$to" $GL_USER ^C any || + die "'$to' already exists or you are not allowed to create it" +} + +if gitolite access -q gitolite-admin $GL_USER +then + # the user is an admin so we can avoid most permission checks + if $takeover + then checkto + elif [ -e $topath ] + then die "'$to' already exists" + fi +else + # the user isn't an admin, so do all the checks + checkto + gitolite creator "$from" $GL_USER || + die "'$from' does not exist or you are not allowed to delete it" +fi + +# ---------------------------------------------------------------------- + +mv $GL_REPO_BASE/$from.git $topath +[ $? -ne 0 ] && exit 1 + +$takeover && echo $GL_USER > $topath/gl-creator + +[ -f "$HOME/projects.list" ] && sed "s:$from.git$:$to.git:g" -i "$HOME/projects.list" + +echo "$from renamed to $to" >&2 + +exit diff --git a/nixos/cobalt/services/gitolite.nix b/nixos/cobalt/services/gitolite.nix new file mode 100644 index 0000000..94c7ac9 --- /dev/null +++ b/nixos/cobalt/services/gitolite.nix @@ -0,0 +1,109 @@ +{ pkgs, ... }: + +let + # https://groups.google.com/g/gitolite/c/NwZ1-hq9-9E/m/mDbiKyAvDwAJ + fixRefsTrigger = pkgs.writeText "fix-refs" '' + [[ $4 == W ]] || exit 0 + + cd $GL_REPO_BASE/$2.git + + head=`git symbolic-ref HEAD` + [[ -f $head ]] || { + set -- refs/heads/* + git symbolic-ref HEAD $1 + } + ''; +in +{ + services.gitolite = { + enable = true; + user = "git"; + group = "git"; + adminPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDi7GGOGVj1Y5Sc1EW6zEdrp78dS6hvmS348pqu9dUsB openpgp:0x6BE7BD6F"; + extraGitoliteRc = '' + $RC{UMASK} = 0027; + $RC{GIT_CONFIG_KEYS} = '.*'; + $RC{ROLES}{OWNERS} = 1; + $RC{OWNER_ROLENAME} = 'OWNERS'; + # For some unknown reason, $ENV{HOME} doesn't get resolved to the correct + # directory. + # $RC{LOCAL_CODE} = '$ENV{HOME}/local'; + $RC{LOCAL_CODE} = '/var/lib/gitolite/local'; + push(@{$RC{ENABLE}}, 'D'); + push(@{$RC{ENABLE}}, 'symbolic-ref'); + push(@{$RC{ENABLE}}, 'rename'); + push(@{$RC{POST_GIT}}, 'fix-refs'); + # push(@{$RC{ENABLE}}, 'set-default-roles'); + # push(@{$RC{ENABLE}}, 'create'); + # push(@{$RC{ENABLE}}, 'fork'); + + ''; + }; + + environment.persistence."/persist".directories = [ + "/var/lib/gitolite" + ]; + + system.activationScripts.gitolite-create-local = '' + mkdir -p /var/lib/gitolite/local/triggers + mkdir -p /var/lib/gitolite/local/commands + chown -R git:git /var/lib/gitolite/local + ''; + + systemd.tmpfiles.rules = [ + "C /var/lib/gitolite/local/triggers/fix-refs 755 - - - ${./gitolite-noncore/fix-refs}" + "C /var/lib/gitolite/local/commands/rename 755 - - - ${./gitolite-noncore/rename}" + ]; + + + systemd.timers."gitolite-trash-cleanup" = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "*-*-* 00:00:00"; + Unit = "gitolite-trash-cleanup.service"; + }; + }; + + systemd.services."gitolite-trash-cleanup" = { + script = '' + set -euo pipefail + if [ ! -d "Trash" ] ; then + echo Trash directory is nonexistent! + echo No operations to perform. Exiting. + exit 0 + fi + + match=$(find Trash -type d -regextype posix-extended -regex ".*/[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2}$") + processed_entry=0 + removed_entry=0 + + for dir in $match + do + system_timestamp=$(date +%s) + trash_timestamp=$(basename $dir | sed -e "s/_/ /g" | date -f - +%s) + age=$(( $system_timestamp - $trash_timestamp )) + # Wipe trashes older than 2w + if [[ age -gt 1209600 ]] ; then + echo "Removing '$dir' (age $age)" + rm -rf $dir + ((removed_entry+=1)) + fi + ((processed_entry+=1)) + done + + echo "Directories that needs cleanup:" + find Trash -type d -empty -print -delete + echo "Cleaned empty directories." + + echo "Done! Removed $removed_entry/$processed_entry" + ''; + + path = with pkgs; [ bash util-linux coreutils ]; + + serviceConfig = { + Type = "oneshot"; + User = "git"; + WorkingDirectory = "/var/lib/gitolite/repositories"; + }; + }; +} diff --git a/nixos/cobalt/services/nginx.nix b/nixos/cobalt/services/nginx.nix new file mode 100644 index 0000000..cb5ced3 --- /dev/null +++ b/nixos/cobalt/services/nginx.nix @@ -0,0 +1,15 @@ +{ + services.nginx = { + enable = true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedTlsSettings = true; + }; + + users.extraUsers.nginx.extraGroups = [ "acme" ]; + + imports = [ + ./acme.nix + ]; +} diff --git a/nixos/cobalt/services/soju.nix b/nixos/cobalt/services/soju.nix new file mode 100644 index 0000000..c150879 --- /dev/null +++ b/nixos/cobalt/services/soju.nix @@ -0,0 +1,26 @@ +{ + services.soju = { + enable = true; + extraGroups = [ "acme" ]; + hostName = "cobalt.sefidel.com"; + listen = [ + ":6697" + ]; + tlsCertificate = "/var/lib/acme/sefidel.com/cert.pem"; + tlsCertificateKey = "/var/lib/acme/sefidel.com/key.pem"; + }; + + networking.firewall.allowedTCPPorts = [ 6697 ]; + + environment.persistence."/persist".directories = [ + "/var/lib/private/soju" + ]; + + # TODO: remove this once merged + disabledModules = [ "services/networking/soju.nix" ]; + + imports = [ + ./acme.nix + ../modules/soju.nix + ]; +} diff --git a/nixos/colmena.nix b/nixos/colmena.nix new file mode 100644 index 0000000..99945e6 --- /dev/null +++ b/nixos/colmena.nix @@ -0,0 +1,28 @@ +{ self, unstable, unstable-small, ... } @ inputs: + +{ + meta = { + nixpkgs = import unstable { + system = "x86_64-linux"; + }; + }; + + defaults = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ + curl + vim + ]; + }; + + cobalt = self.lib.mkColmena { + name = "cobalt"; + system = "x86_64-linux"; + inputs = inputs; + deployment = { + targetHost = "cobalt.sefidel.com"; + targetPort = 22; + targetUser = "root"; + }; + time.timeZone = "UTC"; + }; +} |