summaryrefslogtreecommitdiffstats
path: root/nixos
diff options
context:
space:
mode:
authortomberek <tomberek@users.noreply.github.com>2021-12-28 22:29:36 -0500
committerGitHub <noreply@github.com>2021-12-28 22:29:36 -0500
commit94cb489156f0c49d300e0250fd8d6f81c1a33d2f (patch)
treeda3a574f278ebd430f1713138b420ec2b88969c0 /nixos
parent6aab02a1de1a67e61451325824391aa89e9480ae (diff)
parent4663df5c4ad5360d82f1e2aa7d799ea43638d7d4 (diff)
Merge pull request #133984 from ju1m/sourcehut
nixos/sourcehut: updates, fixes, hardening
Diffstat (limited to 'nixos')
-rw-r--r--nixos/lib/make-options-doc/options-to-docbook.xsl2
-rw-r--r--nixos/modules/services/misc/sourcehut/default.nix1455
-rw-r--r--nixos/modules/services/misc/sourcehut/service.nix431
-rw-r--r--nixos/modules/services/misc/sourcehut/sourcehut.xml24
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/sourcehut.nix188
6 files changed, 1883 insertions, 218 deletions
diff --git a/nixos/lib/make-options-doc/options-to-docbook.xsl b/nixos/lib/make-options-doc/options-to-docbook.xsl
index b9ac26450514..b286f7b5e2c0 100644
--- a/nixos/lib/make-options-doc/options-to-docbook.xsl
+++ b/nixos/lib/make-options-doc/options-to-docbook.xsl
@@ -20,7 +20,7 @@
<title>Configuration Options</title>
<variablelist xml:id="configuration-variable-list">
<xsl:for-each select="attrs">
- <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '&lt;', '_'), '>', '_'))" />
+ <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '&lt;', '_'), '>', '_'), ':', '_'))" />
<varlistentry>
<term xlink:href="#{$id}">
<xsl:attribute name="xml:id"><xsl:value-of select="$id"/></xsl:attribute>
diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix
index c84a75b0ca02..1bd21c278e00 100644
--- a/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixos/modules/services/misc/sourcehut/default.nix
@@ -1,14 +1,90 @@
{ config, pkgs, lib, ... }:
-
with lib;
let
+ inherit (config.services) nginx postfix postgresql redis;
+ inherit (config.users) users groups;
cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- settingsFormat = pkgs.formats.ini { };
+ domain = cfg.settings."sr.ht".global-domain;
+ settingsFormat = pkgs.formats.ini {
+ listToValue = concatMapStringsSep "," (generators.mkValueStringDefault {});
+ mkKeyValue = k: v:
+ if v == null then ""
+ else generators.mkKeyValueDefault {
+ mkValueString = v:
+ if v == true then "yes"
+ else if v == false then "no"
+ else generators.mkValueStringDefault {} v;
+ } "=" k v;
+ };
+ configIniOfService = srv: settingsFormat.generate "sourcehut-${srv}-config.ini"
+ # Each service needs access to only a subset of sections (and secrets).
+ (filterAttrs (k: v: v != null)
+ (mapAttrs (section: v:
+ let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht(::.*)?$" section; in
+ if srvMatch == null # Include sections shared by all services
+ || head srvMatch == srv # Include sections for the service being configured
+ then v
+ # Enable Web links and integrations between services.
+ else if tail srvMatch == [ null ] && elem (head srvMatch) cfg.services
+ then {
+ inherit (v) origin;
+ # mansrht crashes without it
+ oauth-client-id = v.oauth-client-id or null;
+ }
+ # Drop sub-sections of other services
+ else null)
+ (recursiveUpdate cfg.settings {
+ # Those paths are mounted using BindPaths= or BindReadOnlyPaths=
+ # for services needing access to them.
+ "builds.sr.ht::worker".buildlogs = "/var/log/sourcehut/buildsrht-worker";
+ "git.sr.ht".post-update-script = "/usr/bin/gitsrht-update-hook";
+ "git.sr.ht".repos = "/var/lib/sourcehut/gitsrht/repos";
+ "hg.sr.ht".changegroup-script = "/usr/bin/hgsrht-hook-changegroup";
+ "hg.sr.ht".repos = "/var/lib/sourcehut/hgsrht/repos";
+ # Making this a per service option despite being in a global section,
+ # so that it uses the redis-server used by the service.
+ "sr.ht".redis-host = cfg.${srv}.redis.host;
+ })));
+ commonServiceSettings = srv: {
+ origin = mkOption {
+ description = "URL ${srv}.sr.ht is being served at (protocol://domain)";
+ type = types.str;
+ default = "https://${srv}.${domain}";
+ defaultText = "https://${srv}.example.com";
+ };
+ debug-host = mkOption {
+ description = "Address to bind the debug server to.";
+ type = with types; nullOr str;
+ default = null;
+ };
+ debug-port = mkOption {
+ description = "Port to bind the debug server to.";
+ type = with types; nullOr str;
+ default = null;
+ };
+ connection-string = mkOption {
+ description = "SQLAlchemy connection string for the database.";
+ type = types.str;
+ default = "postgresql:///localhost?user=${srv}srht&host=/run/postgresql";
+ };
+ migrate-on-upgrade = mkEnableOption "automatic migrations on package upgrade" // { default = true; };
+ oauth-client-id = mkOption {
+ description = "${srv}.sr.ht's OAuth client id for meta.sr.ht.";
+ type = types.str;
+ };
+ oauth-client-secret = mkOption {
+ description = "${srv}.sr.ht's OAuth client secret for meta.sr.ht.";
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ };
# Specialized python containing all the modules
python = pkgs.sourcehut.python.withPackages (ps: with ps; [
gunicorn
+ eventlet
+ # For monitoring Celery: sudo -u listssrht celery --app listssrht.process -b redis+socket:///run/redis-sourcehut/redis.sock?virtual_host=5 flower
+ flower
# Sourcehut services
srht
buildsrht
@@ -19,72 +95,37 @@ let
listssrht
mansrht
metasrht
+ # Not a python package
+ #pagessrht
pastesrht
todosrht
]);
+ mkOptionNullOrStr = description: mkOption {
+ inherit description;
+ type = with types; nullOr str;
+ default = null;
+ };
in
{
- imports =
- [
- ./git.nix
- ./hg.nix
- ./hub.nix
- ./todo.nix
- ./man.nix
- ./meta.nix
- ./paste.nix
- ./builds.nix
- ./lists.nix
- ./dispatch.nix
- (mkRemovedOptionModule [ "services" "sourcehut" "nginx" "enable" ] ''
- The sourcehut module supports `nginx` as a local reverse-proxy by default and doesn't
- support other reverse-proxies officially.
-
- However it's possible to use an alternative reverse-proxy by
-
- * disabling nginx
- * adjusting the relevant settings for server addresses and ports directly
-
- Further details about this can be found in the `Sourcehut`-section of the NixOS-manual.
- '')
- ];
-
options.services.sourcehut = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Enable sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
- task dispatching, wiki and account management services
- '';
- };
+ enable = mkEnableOption ''
+ sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
+ task dispatching, wiki and account management services
+ '';
services = mkOption {
- type = types.nonEmptyListOf (types.enum [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ]);
- default = [ "man" "meta" "paste" ];
- example = [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ];
+ type = with types; listOf (enum
+ [ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
+ defaultText = "locally enabled services";
description = ''
- Services to enable on the sourcehut network.
+ Services that may be displayed as links in the title bar of the Web interface.
'';
};
- originBase = mkOption {
+ listenAddress = mkOption {
type = types.str;
- default = with config.networking; hostName + lib.optionalString (domain != null) ".${domain}";
- defaultText = literalExpression ''
- with config.networking; hostName + optionalString (domain != null) ".''${domain}"
- '';
- description = ''
- Host name used by reverse-proxy and for default settings. Will host services at git."''${originBase}". For example: git.sr.ht
- '';
- };
-
- address = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = ''
- Address to bind to.
- '';
+ default = "localhost";
+ description = "Address to bind to.";
};
python = mkOption {
@@ -97,105 +138,1247 @@ in
'';
};
- statePath = mkOption {
- type = types.path;
- default = "/var/lib/sourcehut";
- description = ''
- Root state path for the sourcehut network. If left as the default value
- this directory will automatically be created before the sourcehut server
- starts, otherwise the sysadmin is responsible for ensuring the
- directory exists with appropriate ownership and permissions.
- '';
+ minio = {
+ enable = mkEnableOption ''local minio integration'';
+ };
+
+ nginx = {
+ enable = mkEnableOption ''local nginx integration'';
+ virtualHost = mkOption {
+ type = types.attrs;
+ default = {};
+ description = "Virtual-host configuration merged with all Sourcehut's virtual-hosts.";
+ };
+ };
+
+ postfix = {
+ enable = mkEnableOption ''local postfix integration'';
+ };
+
+ postgresql = {
+ enable = mkEnableOption ''local postgresql integration'';
+ };
+
+ redis = {
+ enable = mkEnableOption ''local redis integration in a dedicated redis-server'';
};
settings = mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
+ options."sr.ht" = {
+ global-domain = mkOption {
+ description = "Global domain name.";
+ type = types.str;
+ example = "example.com";
+ };
+ environment = mkOption {
+ description = "Values other than \"production\" adds a banner to each page.";
+ type = types.enum [ "development" "production" ];
+ default = "development";
+ };
+ network-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to a secret key to encrypt internal messages with. Use <code>srht-keygen network</code> to
+ generate this key. It must be consistent between all services and nodes.
+ '';
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ owner-email = mkOption {
+ description = "Owner's email.";
+ type = types.str;
+ default = "contact@example.com";
+ };
+ owner-name = mkOption {
+ description = "Owner's name.";
+ type = types.str;
+ default = "John Doe";
+ };
+ site-blurb = mkOption {
+ description = "Blurb for your site.";
+ type = types.str;
+ default = "the hacker's forge";
+ };
+ site-info = mkOption {
+ description = "The top-level info page for your site.";
+ type = types.str;
+ default = "https://sourcehut.org";
+ };
+ service-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to a key used for encrypting session cookies. Use <code>srht-keygen service</code> to
+ generate the service key. This must be shared between each node of the same
+ service (e.g. git1.sr.ht and git2.sr.ht), but different services may use
+ different keys. If you configure all of your services with the same
+ config.ini, you may use the same service-key for all of them.
+ '';
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ site-name = mkOption {
+ description = "The name of your network of sr.ht-based sites.";
+ type = types.str;
+ default = "sourcehut";
+ };
+ source-url = mkOption {
+ description = "The source code for your fork of sr.ht.";
+ type = types.str;
+ default = "https://git.sr.ht/~sircmpwn/srht";
+ };
+ };
+ options.mail = {
+ smtp-host = mkOptionNullOrStr "Outgoing SMTP host.";
+ smtp-port = mkOption {
+ description = "Outgoing SMTP port.";
+ type = with types; nullOr port;
+ default = null;
+ };
+ smtp-user = mkOptionNullOrStr "Outgoing SMTP user.";
+ smtp-password = mkOptionNullOrStr "Outgoing SMTP password.";
+ smtp-from = mkOptionNullOrStr "Outgoing SMTP FROM.";
+ error-to = mkOptionNullOrStr "Address receiving application exceptions";
+ error-from = mkOptionNullOrStr "Address sending application exceptions";
+ pgp-privkey = mkOptionNullOrStr ''
+ An absolute file path (which should be outside the Nix-store)
+ to an OpenPGP private key.
+
+ Your PGP key information (DO NOT mix up pub and priv here)
+ You must remove the password from your secret key, if present.
+ You can do this with <code>gpg --edit-key [key-id]</code>,
+ then use the <code>passwd</code> command and do not enter a new password.
+ '';
+ pgp-pubkey = mkOptionNullOrStr "OpenPGP public key.";
+ pgp-key-id = mkOptionNullOrStr "OpenPGP key identifier.";
+ };
+ options.objects = {
+ s3-upstream = mkOption {
+ description = "Configure the S3-compatible object storage service.";
+ type = with types; nullOr str;
+ default = null;
+ };
+ s3-access-key = mkOption {
+ description = "Access key to the S3-compatible object storage service";
+ type = with types; nullOr str;
+ default = null;
+ };
+ s3-secret-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to the secret key of the S3-compatible object storage service.
+ '';
+ type = with types; nullOr path;
+ default = null;
+ apply = mapNullable (s: "<" + toString s);
+ };
+ };
+ options.webhooks = {
+ private-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to a base64-encoded Ed25519 key for signing webhook payloads.
+ This should be consistent for all *.sr.ht sites,
+ as this key will be used to verify signatures
+ from other sites in your network.
+ Use the <code>srht-keygen webhook</code> command to generate a key.
+ '';
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ };
+
+ options."dispatch.sr.ht" = commonServiceSettings "dispatch" // {
+ };
+ options."dispatch.sr.ht::github" = {
+ oauth-client-id = mkOptionNullOrStr "OAuth client id.";
+ oauth-client-secret = mkOptionNullOrStr "OAuth client secret.";
+ };
+ options."dispatch.sr.ht::gitlab" = {
+ enabled = mkEnableOption "GitLab integration";
+ canonical-upstream = mkOption {
+ type = types.str;
+ description = "Canonical upstream.";
+ default = "gitlab.com";
+ };
+ repo-cache = mkOption {
+ type = types.str;
+ description = "Repository cache directory.";
+ default = "./repo-cache";
+ };
+ "gitlab.com" = mkOption {
+ type = with types; nullOr str;
+ description = "GitLab id and secret.";
+ default = null;
+ example = "GitLab:application id:secret";
+ };
+ };
+
+ options."builds.sr.ht" = commonServiceSettings "builds" // {
+ allow-free = mkEnableOption "nonpaying users to submit builds";
+ redis = mkOption {
+ description = "The Redis connection used for the Celery worker.";
+ type = types.str;
+ default = "redis+socket:///run/redis-sourcehut-buildsrht/redis.sock?virtual_host=2";
+ };
+ shell = mkOption {
+ description = ''
+ Scripts used to launch on SSH connection.
+ <literal>/usr/bin/master-shell</literal> on master,
+ <literal>/usr/bin/runner-shell</literal> on runner.
+ If master and worker are on the same system
+ set to <literal>/usr/bin/runner-shell</literal>.
+ '';
+ type = types.enum ["/usr/bin/master-shell" "/usr/bin/runner-shell"];
+ default = "/usr/bin/master-shell";
+ };
+ };
+ options."builds.sr.ht::worker" = {
+ bind-address = mkOption {
+ description = ''
+ HTTP bind address for serving local build information/monitoring.
+ '';
+ type = types.str;
+ default = "localhost:8080";
+ };
+ buildlogs = mkOption {
+ description = "Path to write build logs.";
+ type = types.str;
+ default = "/var/log/sourcehut/buildsrht-worker";
+ };
+ name = mkOption {
+ description = ''
+ Listening address and listening port
+ of the build runner (with HTTP port if not 80).
+ '';
+ type = types.str;
+ default = "localhost:5020";
+ };
+ timeout = mkOption {
+ description = ''
+ Max build duration.
+ See <link xlink:href="https://golang.org/pkg/time/#ParseDuration"/>.
+ '';
+ type = types.str;
+ default = "3m";
+ };
+ };
+
+ options."git.sr.ht" = commonServiceSettings "git" // {
+ outgoing-domain = mkOption {
+ description = "Outgoing domain.";
+ type = types.str;
+ default = "https://git.localhost.localdomain";
+ };
+ post-update-script = mkOption {
+ description = ''
+ A post-update script which is installed in every git repo.
+ This setting is propagated to newer and existing repositories.
+ '';
+ type = types.path;
+ default = "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
+ defaultText = "\${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
+ };
+ repos = mkOption {
+ description = ''
+ Path to git repositories on disk.
+ If changing the default, you must ensure that
+ the gitsrht's user as read and write access to it.
+ '';
+ type = types.str;
+ default = "/var/lib/sourcehut/gitsrht/repos";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "redis+socket:///run/redis-sourcehut-gitsrht/redis.sock?virtual_host=1";
+ };
+ };
+ options."git.sr.ht::api" = {
+ internal-ipnet = mkOption {
+ description = ''
+ Set of IP subnets which are permitted to utilize internal API
+ authentication. This should be limited to the subnets
+ from which your *.sr.ht services are running.
+ See <xref linkend="opt-services.sourcehut.listenAddress"/>.
+ '';
+ type = with types; listOf str;
+ default = [ "127.0.0.0/8" "::1/128" ];
+ };
+ };
+
+ options."hg.sr.ht" = commonServiceSettings "hg" // {
+ changegroup-script = mkOption {
+ description = ''
+ A changegroup script which is installed in every mercurial repo.
+ This setting is propagated to newer and existing repositories.
+ '';
+ type = types.str;
+ default = "${cfg.python}/bin/hgsrht-hook-changegroup";
+ defaultText = "\${cfg.python}/bin/hgsrht-hook-changegroup";
+ };
+ repos = mkOption {
+ description = ''
+ Path to mercurial repositories on disk.
+ If changing the default, you must ensure that
+ the hgsrht's user as read and write access to it.
+ '';
+ type = types.str;
+ default = "/var/lib/sourcehut/hgsrht/repos";
+ };
+ srhtext = mkOptionNullOrStr ''
+ Path to the srht mercurial extension
+ (defaults to where the hgsrht code is)
+ '';
+ clone_bundle_threshold = mkOption {
+ description = ".hg/store size (in MB) past which the nightly job generates clone bundles.";
+ type = types.ints.unsigned;
+ default = 50;
+ };
+ hg_ssh = mkOption {
+ description = "Path to hg-ssh (if not in $PATH).";
+ type = types.str;
+ default = "${pkgs.mercurial}/bin/hg-ssh";
+ defaultText = "\${pkgs.mercurial}/bin/hg-ssh";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "redis+socket:///run/redis-sourcehut-hgsrht/redis.sock?virtual_host=1";
+ };
+ };
+
+ options."hub.sr.ht" = commonServiceSettings "hub" // {
+ };
+
+ options."lists.sr.ht" = commonServiceSettings "lists" // {
+ allow-new-lists = mkEnableOption "Allow creation of new lists.";
+ notify-from = mkOption {
+ description = "Outgoing email for notifications generated by users.";
+ type = types.str;
+ default = "lists-notify@localhost.localdomain";
+ };
+ posting-domain = mkOption {
+ description = "Posting domain.";
+ type = types.str;
+ default = "lists.localhost.localdomain";
+ };
+ redis = mkOption {
+ description = "The Redis connection used for the Celery worker.";
+ type = types.str;
+ default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=2";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=1";
+ };
+ };
+ options."lists.sr.ht::worker" = {
+ reject-mimetypes = mkOption {
+ description = ''
+ Comma-delimited list of Content-Types to reject. Messages with Content-Types
+ included in this list are rejected. Multipart messages are always supported,
+ and each part is checked against this list.
+
+ Uses fnmatch for wildcard expansion.
+ '';
+ type = with types; listOf str;
+ default = ["text/html"];
+ };
+ reject-url = mkOption {
+ description = "Reject URL.";
+ type = types.str;
+ default = "https://man.sr.ht/lists.sr.ht/etiquette.md";
+ };
+ sock = mkOption {
+ description = ''
+ Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
+ Alternatively, specify IP:PORT and an SMTP server will be run instead.
+ '';
+ type = types.str;
+ default = "/tmp/lists.sr.ht-lmtp.sock";
+ };
+ sock-group = mkOption {
+ description = ''
+ The lmtp daemon will make the unix socket group-read/write
+ for users in this group.
+ '';
+ type = types.str;
+ default = "postfix";
+ };
+ };
+
+ options."man.sr.ht" = commonServiceSettings "man" // {
+ };
+
+ options."meta.sr.ht" =
+ removeAttrs (commonServiceSettings "meta")
+ ["oauth-client-id" "oauth-client-secret"] // {
+ api-origin = mkOption {
+ description = "Origin URL for API, 100 more than web.";
+ type = types.str;
+ default = "http://${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
+ defaultText = ''http://<xref linkend="opt-services.sourcehut.listenAddress"/>:''${toString (<xref linkend="opt-services.sourcehut.meta.port"/> + 100)}'';
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "redis+socket:///run/redis-sourcehut-metasrht/redis.sock?virtual_host=1";
+ };
+ welcome-emails = mkEnableOption "sending stock sourcehut welcome emails after signup";
+ };
+ options."meta.sr.ht::api" = {
+ internal-ipnet = mkOption {
+ description = ''
+ Set of IP subnets which are permitted to utilize internal API
+ authentication. This should be limited to the subnets
+ from which your *.sr.ht services are running.
+ See <xref linkend="opt-services.sourcehut.listenAddress"/>.
+ '';
+ type = with types; listOf str;
+ default = [ "127.0.0.0/8" "::1/128" ];
+ };
+ };
+ options."meta.sr.ht::aliases" = mkOption {
+ description = "Aliases for the client IDs of commonly used OAuth clients.";
+ type = with types; attrsOf int;
+ default = {};
+ example = { "git.sr.ht" = 12345; };
+ };
+ options."meta.sr.ht::billing" = {
+ enabled = mkEnableOption "the billing system";
+ stripe-public-key = mkOptionNullOrStr "Public key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys";
+ stripe-secret-key = mkOptionNullOrStr ''
+ An absolute file path (which should be outside the Nix-store)
+ to a secret key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys
+ '' // {
+ apply = mapNullable (s: "<" + toString s);
+ };
+ };
+ options."meta.sr.ht::settings" = {
+ registration = mkEnableOption "public registration";
+ onboarding-redirect = mkOption {
+ description = "Where to redirect new users upon registration.";
+ type = types.str;
+ default = "https://meta.localhost.localdomain";
+ };
+ user-invites = mkOption {
+ description = ''
+ How many invites each user is issued upon registration
+ (only applicable if open registration is disabled).
+ '';
+ type = types.ints.unsigned;
+ default = 5;
+ };
+ };
+
+ options."pages.sr.ht" = commonServiceSettings "pages" // {
+ gemini-certs = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to Gemini certificates.
+ '';
+ type = with types; nullOr path;
+ default = null;
+ };
+ max-site-size = mkOption {
+ description = "Maximum size of any given site (post-gunzip), in MiB.";
+ type = types.int;
+ default = 1024;
+ };
+ user-domain = mkOption {
+ description = ''
+ Configures the user domain, if enabled.
+ All users are given &lt;username&gt;.this.domain.
+ '';
+ type = with types; nullOr str;
+ default = null;
+ };
+ };
+ options."pages.sr.ht::api" = {
+ internal-ipnet = mkOption {
+ description = ''
+ Set of IP subnets which are permitted to utilize internal API
+ authentication. This should be limited to the subnets
+ from which your *.sr.ht services are running.
+ See <xref linkend="opt-services.sourcehut.listenAddress"/>.
+ '';
+ type = with types; listOf str;
+ default = [ "127.0.0.0/8" "::1/128" ];
+ };
+ };
+
+ options."paste.sr.ht" = commonServiceSettings "paste" // {
+ };
+
+ options."todo.sr.ht" = commonServiceSettings "todo" // {
+ notify-from = mkOption {
+ description = "Outgoing email for notifications generated by users.";
+ type = types.str;
+ default = "todo-notify@localhost.localdomain";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "redis+socket:///run/redis-sourcehut-todosrht/redis.sock?virtual_host=1";
+ };
+ };
+ options."todo.sr.ht::mail" = {
+ posting-domain = mkOption {
+ description = "Posting domain.";
+ type = types.str;
+ default = "todo.localhost.localdomain";
+ };
+ sock = mkOption {
+ description = ''
+ Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
+ Alternatively, specify IP:PORT and an SMTP server will be run instead.
+ '';
+ type = types.str;
+ default = "/tmp/todo.sr.ht-lmtp.sock";
+ };
+ sock-group = mkOption {
+ description = ''
+ The lmtp daemon will make the unix socket group-read/write
+ for users in this group.
+ '';
+ type = types.str;
+ default = "postfix";
+ };
+ };
};
default = { };
description = ''
The configuration for the sourcehut network.
'';
};
+
+ builds = {
+ enableWorker = mkEnableOption ''
+ worker for builds.sr.ht
+
+ <warning><para>
+ For smaller deployments, job runners can be installed alongside the master server
+ but even if you only build your own software, integration with other services
+ may cause you to run untrusted builds
+ (e.g. automatic testing of patches via listssrht).
+ See <link xlink:href="https://man.sr.ht/builds.sr.ht/configuration.md#security-model"/>.
+ </para></warning>
+ '';
+
+ images = mkOption {
+ type = with types; attrsOf (attrsOf (attrsOf package));
+ default = { };
+ example = lib.literalExpression ''(let
+ # Pinning unstable to allow usage with flakes and limit rebuilds.
+ pkgs_unstable = builtins.fetchGit {
+ url = "https://github.com/NixOS/nixpkgs";
+ rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
+ ref = "nixos-unstable";
+ };
+ image_from_nixpkgs = (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
+ pkgs = (import pkgs_unstable {});
+ });
+ in
+ {
+ nixos.unstable.x86_64 = image_from_nixpkgs;
+ }
+ )'';
+ description = ''
+ Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
+ '';
+ };
+ };
+
+ git = {
+ package = mkOption {
+ type = types.package;
+ default = pkgs.git;
+ example = literalExpression "pkgs.gitFull";
+ description = ''
+ Git package for git.sr.ht. This can help silence collisions.
+ '';
+ };
+ fcgiwrap.preforkProcess = mkOption {
+ description = "Number of fcgiwrap processes to prefork.";
+ type = types.int;
+ default = 4;
+ };
+ };
+
+ hg = {
+ package = mkOption {
+ type = types.package;
+ default = pkgs.mercurial;
+ description = ''
+ Mercurial package for hg.sr.ht. This can help silence collisions.
+ '';
+ };
+ cloneBundles = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
+ '';
+ };
+ };
+
+ lists = {
+ process = {
+ extraArgs = mkOption {
+ type = with types; listOf str;
+ default = [ "--loglevel DEBUG" "--pool eventlet" "--without-heartbeat" ];
+ description = "Extra arguments passed to the Celery responsible for processing mails.";
+ };
+ celeryConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Content of the <literal>celeryconfig.py</literal> used by the Celery of <literal>listssrht-process</literal>.";
+ };
+ };
+ };
};
- config = mkIf cfg.enable {
- assertions =
- [
+ config = mkIf cfg.enable (mkMerge [
+ {
+ environment.systemPackages = [ pkgs.sourcehut.coresrht ];
+
+ services.sourcehut.settings = {
+ "git.sr.ht".outgoing-domain = mkDefault "https://git.${domain}";
+ "lists.sr.ht".notify-from = mkDefault "lists-notify@${domain}";
+ "lists.sr.ht".posting-domain = mkDefault "lists.${domain}";
+ "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${domain}";
+ "todo.sr.ht".notify-from = mkDefault "todo-notify@${domain}";
+ "todo.sr.ht::mail".posting-domain = mkDefault "todo.${domain}";
+ };
+ }
+ (mkIf cfg.postgresql.enable {
+ assertions = [
+ { assertion = postgresql.enable;
+ message = "postgresql must be enabled and configured";
+ }
+ ];
+ })
+ (mkIf cfg.postfix.enable {
+ assertions = [
+ { assertion = postfix.enable;
+ message = "postfix must be enabled and configured";
+ }
+ ];
+ # Needed for sharing the LMTP sockets with JoinsNamespaceOf=
+ systemd.services.postfix.serviceConfig.PrivateTmp = true;
+ })
+ (mkIf cfg.redis.enable {
+ services.redis.vmOverCommit = mkDefault true;
+ })
+ (mkIf cfg.nginx.enable {
+ assertions = [
+ { assertion = nginx.enable;
+ message = "nginx must be enabled and configured";
+ }
+ ];
+ # For proxyPass= in virtual-hosts for Sourcehut services.
+ services.nginx.recommendedProxySettings = mkDefault true;
+ })
+ (mkIf (cfg.builds.enable || cfg.git.enable || cfg.hg.enable) {
+ services.openssh = {