at 23.11-pre 8.6 kB view raw
1{ pkgs, config, lib, ... }: 2 3let 4 cfg = config.services.cockpit; 5 inherit (lib) types mkEnableOption mkOption mkIf mdDoc literalMD mkPackageOptionMD; 6 settingsFormat = pkgs.formats.ini {}; 7in { 8 options = { 9 services.cockpit = { 10 enable = mkEnableOption (mdDoc "Cockpit"); 11 12 package = mkPackageOptionMD pkgs "Cockpit" { 13 default = [ "cockpit" ]; 14 }; 15 16 settings = lib.mkOption { 17 type = settingsFormat.type; 18 19 default = {}; 20 21 description = mdDoc '' 22 Settings for cockpit that will be saved in /etc/cockpit/cockpit.conf. 23 24 See the [documentation](https://cockpit-project.org/guide/latest/cockpit.conf.5.html), that is also available with `man cockpit.conf.5` for details. 25 ''; 26 }; 27 28 port = mkOption { 29 description = mdDoc "Port where cockpit will listen."; 30 type = types.port; 31 default = 9090; 32 }; 33 34 openFirewall = mkOption { 35 description = mdDoc "Open port for cockpit."; 36 type = types.bool; 37 default = false; 38 }; 39 }; 40 }; 41 config = mkIf cfg.enable { 42 43 # expose cockpit-bridge system-wide 44 environment.systemPackages = [ cfg.package ]; 45 46 # allow cockpit to find its plugins 47 environment.pathsToLink = [ "/share/cockpit" ]; 48 49 # generate cockpit settings 50 environment.etc."cockpit/cockpit.conf".source = settingsFormat.generate "cockpit.conf" cfg.settings; 51 52 security.pam.services.cockpit = {}; 53 54 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; 55 56 # units are in reverse sort order if you ls $out/lib/systemd/system 57 # all these units are basically verbatim translated from upstream 58 59 # Translation from $out/lib/systemd/system/systemd-cockpithttps.slice 60 systemd.slices.system-cockpithttps = { 61 description = "Resource limits for all cockpit-ws-https@.service instances"; 62 sliceConfig = { 63 TasksMax = 200; 64 MemoryHigh = "75%"; 65 MemoryMax = "90%"; 66 }; 67 }; 68 69 # Translation from $out/lib/systemd/system/cockpit-wsinstance-https@.socket 70 systemd.sockets."cockpit-wsinstance-https@" = { 71 unitConfig = { 72 Description = "Socket for Cockpit Web Service https instance %I"; 73 BindsTo = [ "cockpit.service" "cockpit-wsinstance-https@%i.service" ]; 74 # clean up the socket after the service exits, to prevent fd leak 75 # this also effectively prevents a DoS by starting arbitrarily many sockets, as 76 # the services are resource-limited by system-cockpithttps.slice 77 Documentation = "man:cockpit-ws(8)"; 78 }; 79 socketConfig = { 80 ListenStream = "/run/cockpit/wsinstance/https@%i.sock"; 81 SocketUser = "root"; 82 SocketMode = "0600"; 83 }; 84 }; 85 86 # Translation from $out/lib/systemd/system/cockpit-wsinstance-https@.service 87 systemd.services."cockpit-wsinstance-https@" = { 88 description = "Cockpit Web Service https instance %I"; 89 bindsTo = [ "cockpit.service"]; 90 path = [ cfg.package ]; 91 documentation = [ "man:cockpit-ws(8)" ]; 92 serviceConfig = { 93 Slice = "system-cockpithttps.slice"; 94 ExecStart = "${cfg.package}/libexec/cockpit-ws --for-tls-proxy --port=0"; 95 User = "root"; 96 Group = ""; 97 }; 98 }; 99 100 # Translation from $out/lib/systemd/system/cockpit-wsinstance-http.socket 101 systemd.sockets.cockpit-wsinstance-http = { 102 unitConfig = { 103 Description = "Socket for Cockpit Web Service http instance"; 104 BindsTo = "cockpit.service"; 105 Documentation = "man:cockpit-ws(8)"; 106 }; 107 socketConfig = { 108 ListenStream = "/run/cockpit/wsinstance/http.sock"; 109 SocketUser = "root"; 110 SocketMode = "0600"; 111 }; 112 }; 113 114 # Translation from $out/lib/systemd/system/cockpit-wsinstance-https-factory.socket 115 systemd.sockets.cockpit-wsinstance-https-factory = { 116 unitConfig = { 117 Description = "Socket for Cockpit Web Service https instance factory"; 118 BindsTo = "cockpit.service"; 119 Documentation = "man:cockpit-ws(8)"; 120 }; 121 socketConfig = { 122 ListenStream = "/run/cockpit/wsinstance/https-factory.sock"; 123 Accept = true; 124 SocketUser = "root"; 125 SocketMode = "0600"; 126 }; 127 }; 128 129 # Translation from $out/lib/systemd/system/cockpit-wsinstance-https-factory@.service 130 systemd.services."cockpit-wsinstance-https-factory@" = { 131 description = "Cockpit Web Service https instance factory"; 132 documentation = [ "man:cockpit-ws(8)" ]; 133 path = [ cfg.package ]; 134 serviceConfig = { 135 ExecStart = "${cfg.package}/libexec/cockpit-wsinstance-factory"; 136 User = "root"; 137 }; 138 }; 139 140 # Translation from $out/lib/systemd/system/cockpit-wsinstance-http.service 141 systemd.services."cockpit-wsinstance-http" = { 142 description = "Cockpit Web Service http instance"; 143 bindsTo = [ "cockpit.service" ]; 144 path = [ cfg.package ]; 145 documentation = [ "man:cockpit-ws(8)" ]; 146 serviceConfig = { 147 ExecStart = "${cfg.package}/libexec/cockpit-ws --no-tls --port=0"; 148 User = "root"; 149 Group = ""; 150 }; 151 }; 152 153 # Translation from $out/lib/systemd/system/cockpit.socket 154 systemd.sockets."cockpit" = { 155 unitConfig = { 156 Description = "Cockpit Web Service Socket"; 157 Documentation = "man:cockpit-ws(8)"; 158 Wants = "cockpit-motd.service"; 159 }; 160 socketConfig = { 161 ListenStream = cfg.port; 162 ExecStartPost = [ 163 "-${cfg.package}/share/cockpit/motd/update-motd \"\" localhost" 164 "-${pkgs.coreutils}/bin/ln -snf active.motd /run/cockpit/motd" 165 ]; 166 ExecStopPost = "-${pkgs.coreutils}/bin/ln -snf inactive.motd /run/cockpit/motd"; 167 }; 168 wantedBy = [ "sockets.target" ]; 169 }; 170 171 # Translation from $out/lib/systemd/system/cockpit.service 172 systemd.services."cockpit" = { 173 description = "Cockpit Web Service"; 174 documentation = [ "man:cockpit-ws(8)" ]; 175 restartIfChanged = true; 176 path = with pkgs; [ coreutils cfg.package ]; 177 requires = [ "cockpit.socket" "cockpit-wsinstance-http.socket" "cockpit-wsinstance-https-factory.socket" ]; 178 after = [ "cockpit-wsinstance-http.socket" "cockpit-wsinstance-https-factory.socket" ]; 179 environment = { 180 G_MESSAGES_DEBUG = "cockpit-ws,cockpit-bridge"; 181 }; 182 serviceConfig = { 183 RuntimeDirectory="cockpit/tls"; 184 ExecStartPre = [ 185 # cockpit-tls runs in a more constrained environment, these + means that these commands 186 # will run with full privilege instead of inside that constrained environment 187 # See https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= for details 188 "+${cfg.package}/libexec/cockpit-certificate-ensure --for-cockpit-tls" 189 ]; 190 ExecStart = "${cfg.package}/libexec/cockpit-tls"; 191 User = "root"; 192 Group = ""; 193 NoNewPrivileges = true; 194 ProtectSystem = "strict"; 195 ProtectHome = true; 196 PrivateTmp = true; 197 PrivateDevices = true; 198 ProtectKernelTunables = true; 199 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; 200 MemoryDenyWriteExecute = true; 201 }; 202 }; 203 204 # Translation from $out/lib/systemd/system/cockpit-motd.service 205 # This part basically implements a motd state machine: 206 # - If cockpit.socket is enabled then /run/cockpit/motd points to /run/cockpit/active.motd 207 # - If cockpit.socket is disabled then /run/cockpit/motd points to /run/cockpit/inactive.motd 208 # - As cockpit.socket is disabled by default, /run/cockpit/motd points to /run/cockpit/inactive.motd 209 # /run/cockpit/active.motd is generated dynamically by cockpit-motd.service 210 systemd.services."cockpit-motd" = { 211 path = with pkgs; [ nettools ]; 212 serviceConfig = { 213 Type = "oneshot"; 214 ExecStart = "${cfg.package}/share/cockpit/motd/update-motd"; 215 }; 216 description = "Cockpit motd updater service"; 217 documentation = [ "man:cockpit-ws(8)" ]; 218 wants = [ "network.target" ]; 219 after = [ "network.target" "cockpit.socket" ]; 220 }; 221 222 systemd.tmpfiles.rules = [ # From $out/lib/tmpfiles.d/cockpit-tmpfiles.conf 223 "C /run/cockpit/inactive.motd 0640 root root - ${cfg.package}/share/cockpit/motd/inactive.motd" 224 "f /run/cockpit/active.motd 0640 root root -" 225 "L+ /run/cockpit/motd - - - - inactive.motd" 226 "d /etc/cockpit/ws-certs.d 0600 root root 0" 227 ]; 228 }; 229 230 meta.maintainers = pkgs.cockpit.meta.maintainers; 231}