at 23.11-pre 4.1 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.agate; 7in 8{ 9 options = { 10 services.agate = { 11 enable = mkEnableOption (lib.mdDoc "Agate Server"); 12 13 package = mkOption { 14 type = types.package; 15 default = pkgs.agate; 16 defaultText = literalExpression "pkgs.agate"; 17 description = lib.mdDoc "The package to use"; 18 }; 19 20 addresses = mkOption { 21 type = types.listOf types.str; 22 default = [ "0.0.0.0:1965" ]; 23 description = lib.mdDoc '' 24 Addresses to listen on, IP:PORT, if you haven't disabled forwarding 25 only set IPv4. 26 ''; 27 }; 28 29 contentDir = mkOption { 30 default = "/var/lib/agate/content"; 31 type = types.path; 32 description = lib.mdDoc "Root of the content directory."; 33 }; 34 35 certificatesDir = mkOption { 36 default = "/var/lib/agate/certificates"; 37 type = types.path; 38 description = lib.mdDoc "Root of the certificate directory."; 39 }; 40 41 hostnames = mkOption { 42 default = [ ]; 43 type = types.listOf types.str; 44 description = lib.mdDoc '' 45 Domain name of this Gemini server, enables checking hostname and port 46 in requests. (multiple occurrences means basic vhosts) 47 ''; 48 }; 49 50 language = mkOption { 51 default = null; 52 type = types.nullOr types.str; 53 description = lib.mdDoc "RFC 4646 Language code for text/gemini documents."; 54 }; 55 56 onlyTls_1_3 = mkOption { 57 default = false; 58 type = types.bool; 59 description = lib.mdDoc "Only use TLSv1.3 (default also allows TLSv1.2)."; 60 }; 61 62 extraArgs = mkOption { 63 type = types.listOf types.str; 64 default = [ "" ]; 65 example = [ "--log-ip" ]; 66 description = lib.mdDoc "Extra arguments to use running agate."; 67 }; 68 }; 69 }; 70 71 config = mkIf cfg.enable { 72 # available for generating certs by hand 73 # it can be a bit arduous with openssl 74 environment.systemPackages = [ cfg.package ]; 75 76 systemd.services.agate = { 77 description = "Agate"; 78 wantedBy = [ "multi-user.target" ]; 79 after = [ "network.target" "network-online.target" ]; 80 81 script = 82 let 83 prefixKeyList = key: list: concatMap (v: [ key v ]) list; 84 addresses = prefixKeyList "--addr" cfg.addresses; 85 hostnames = prefixKeyList "--hostname" cfg.hostnames; 86 in 87 '' 88 exec ${cfg.package}/bin/agate ${ 89 escapeShellArgs ( 90 [ 91 "--content" "${cfg.contentDir}" 92 "--certs" "${cfg.certificatesDir}" 93 ] ++ 94 addresses ++ 95 (optionals (cfg.hostnames != []) hostnames) ++ 96 (optionals (cfg.language != null) [ "--lang" cfg.language ]) ++ 97 (optionals cfg.onlyTls_1_3 [ "--only-tls13" ]) ++ 98 (optionals (cfg.extraArgs != []) cfg.extraArgs) 99 ) 100 } 101 ''; 102 103 serviceConfig = { 104 Restart = "always"; 105 RestartSec = "5s"; 106 DynamicUser = true; 107 StateDirectory = "agate"; 108 109 # Security options: 110 AmbientCapabilities = ""; 111 CapabilityBoundingSet = ""; 112 113 # ProtectClock= adds DeviceAllow=char-rtc r 114 DeviceAllow = ""; 115 116 LockPersonality = true; 117 118 PrivateTmp = true; 119 PrivateDevices = true; 120 PrivateUsers = true; 121 122 ProtectClock = true; 123 ProtectControlGroups = true; 124 ProtectHostname = true; 125 ProtectKernelLogs = true; 126 ProtectKernelModules = true; 127 ProtectKernelTunables = true; 128 129 RestrictNamespaces = true; 130 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; 131 RestrictRealtime = true; 132 133 SystemCallArchitectures = "native"; 134 SystemCallErrorNumber = "EPERM"; 135 SystemCallFilter = [ 136 "@system-service" 137 "~@cpu-emulation" 138 "~@debug" 139 "~@keyring" 140 "~@memlock" 141 "~@obsolete" 142 "~@privileged" 143 "~@setuid" 144 ]; 145 }; 146 }; 147 }; 148}