diff --git a/nixos/modules/services/monitoring/cockpit.nix b/nixos/modules/services/monitoring/cockpit.nix index 64e26ce4e127da..31620b31eb315e 100644 --- a/nixos/modules/services/monitoring/cockpit.nix +++ b/nixos/modules/services/monitoring/cockpit.nix @@ -53,171 +53,8 @@ in { networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; - # units are in reverse sort order if you ls $out/lib/systemd/system - # all these units are basically verbatim translated from upstream - - # Translation from $out/lib/systemd/system/systemd-cockpithttps.slice - systemd.slices.system-cockpithttps = { - description = "Resource limits for all cockpit-ws-https@.service instances"; - sliceConfig = { - TasksMax = 200; - MemoryHigh = "75%"; - MemoryMax = "90%"; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit-wsinstance-https@.socket - systemd.sockets."cockpit-wsinstance-https@" = { - unitConfig = { - Description = "Socket for Cockpit Web Service https instance %I"; - BindsTo = [ "cockpit.service" "cockpit-wsinstance-https@%i.service" ]; - # clean up the socket after the service exits, to prevent fd leak - # this also effectively prevents a DoS by starting arbitrarily many sockets, as - # the services are resource-limited by system-cockpithttps.slice - Documentation = "man:cockpit-ws(8)"; - }; - socketConfig = { - ListenStream = "/run/cockpit/wsinstance/https@%i.sock"; - SocketUser = "root"; - SocketMode = "0600"; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit-wsinstance-https@.service - systemd.services."cockpit-wsinstance-https@" = { - description = "Cockpit Web Service https instance %I"; - bindsTo = [ "cockpit.service"]; - path = [ cfg.package ]; - documentation = [ "man:cockpit-ws(8)" ]; - serviceConfig = { - Slice = "system-cockpithttps.slice"; - ExecStart = "${cfg.package}/libexec/cockpit-ws --for-tls-proxy --port=0"; - User = "root"; - Group = ""; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit-wsinstance-http.socket - systemd.sockets.cockpit-wsinstance-http = { - unitConfig = { - Description = "Socket for Cockpit Web Service http instance"; - BindsTo = "cockpit.service"; - Documentation = "man:cockpit-ws(8)"; - }; - socketConfig = { - ListenStream = "/run/cockpit/wsinstance/http.sock"; - SocketUser = "root"; - SocketMode = "0600"; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit-wsinstance-https-factory.socket - systemd.sockets.cockpit-wsinstance-https-factory = { - unitConfig = { - Description = "Socket for Cockpit Web Service https instance factory"; - BindsTo = "cockpit.service"; - Documentation = "man:cockpit-ws(8)"; - }; - socketConfig = { - ListenStream = "/run/cockpit/wsinstance/https-factory.sock"; - Accept = true; - SocketUser = "root"; - SocketMode = "0600"; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit-wsinstance-https-factory@.service - systemd.services."cockpit-wsinstance-https-factory@" = { - description = "Cockpit Web Service https instance factory"; - documentation = [ "man:cockpit-ws(8)" ]; - path = [ cfg.package ]; - serviceConfig = { - ExecStart = "${cfg.package}/libexec/cockpit-wsinstance-factory"; - User = "root"; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit-wsinstance-http.service - systemd.services."cockpit-wsinstance-http" = { - description = "Cockpit Web Service http instance"; - bindsTo = [ "cockpit.service" ]; - path = [ cfg.package ]; - documentation = [ "man:cockpit-ws(8)" ]; - serviceConfig = { - ExecStart = "${cfg.package}/libexec/cockpit-ws --no-tls --port=0"; - User = "root"; - Group = ""; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit.socket - systemd.sockets."cockpit" = { - unitConfig = { - Description = "Cockpit Web Service Socket"; - Documentation = "man:cockpit-ws(8)"; - Wants = "cockpit-motd.service"; - }; - socketConfig = { - ListenStream = cfg.port; - ExecStartPost = [ - "-${cfg.package}/share/cockpit/motd/update-motd \"\" localhost" - "-${pkgs.coreutils}/bin/ln -snf active.motd /run/cockpit/motd" - ]; - ExecStopPost = "-${pkgs.coreutils}/bin/ln -snf inactive.motd /run/cockpit/motd"; - }; - wantedBy = [ "sockets.target" ]; - }; - - # Translation from $out/lib/systemd/system/cockpit.service - systemd.services."cockpit" = { - description = "Cockpit Web Service"; - documentation = [ "man:cockpit-ws(8)" ]; - restartIfChanged = true; - path = with pkgs; [ coreutils cfg.package ]; - requires = [ "cockpit.socket" "cockpit-wsinstance-http.socket" "cockpit-wsinstance-https-factory.socket" ]; - after = [ "cockpit-wsinstance-http.socket" "cockpit-wsinstance-https-factory.socket" ]; - environment = { - G_MESSAGES_DEBUG = "cockpit-ws,cockpit-bridge"; - }; - serviceConfig = { - RuntimeDirectory="cockpit/tls"; - ExecStartPre = [ - # cockpit-tls runs in a more constrained environment, these + means that these commands - # will run with full privilege instead of inside that constrained environment - # See https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= for details - "+${cfg.package}/libexec/cockpit-certificate-ensure --for-cockpit-tls" - ]; - ExecStart = "${cfg.package}/libexec/cockpit-tls"; - User = "root"; - Group = ""; - NoNewPrivileges = true; - ProtectSystem = "strict"; - ProtectHome = true; - PrivateTmp = true; - PrivateDevices = true; - ProtectKernelTunables = true; - RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; - MemoryDenyWriteExecute = true; - }; - }; - - # Translation from $out/lib/systemd/system/cockpit-motd.service - # This part basically implements a motd state machine: - # - If cockpit.socket is enabled then /run/cockpit/motd points to /run/cockpit/active.motd - # - If cockpit.socket is disabled then /run/cockpit/motd points to /run/cockpit/inactive.motd - # - As cockpit.socket is disabled by default, /run/cockpit/motd points to /run/cockpit/inactive.motd - # /run/cockpit/active.motd is generated dynamically by cockpit-motd.service - systemd.services."cockpit-motd" = { - path = with pkgs; [ nettools ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${cfg.package}/share/cockpit/motd/update-motd"; - }; - description = "Cockpit motd updater service"; - documentation = [ "man:cockpit-ws(8)" ]; - wants = [ "network.target" ]; - after = [ "network.target" "cockpit.socket" ]; - }; + systemd.packages = [ cfg.package ]; + systemd.sockets.cockpit.wantedBy = [ "multi-user.target" ]; systemd.tmpfiles.rules = [ # From $out/lib/tmpfiles.d/cockpit-tmpfiles.conf "C /run/cockpit/inactive.motd 0640 root root - ${cfg.package}/share/cockpit/motd/inactive.motd" diff --git a/nixos/tests/cockpit.nix b/nixos/tests/cockpit.nix index 6c90cccade72c2..5236f5311f021e 100644 --- a/nixos/tests/cockpit.nix +++ b/nixos/tests/cockpit.nix @@ -67,14 +67,14 @@ import ./make-test-python.nix ( log("Opening homepage") driver.get("https://server:9090") - wait = WebDriverWait(driver, 60) - - def wait_elem(by, query): + def wait_elem(by, query, timeout=10): + wait = WebDriverWait(driver, timeout) wait.until(EC.presence_of_element_located((by, query))) - def wait_title_contains(title): + def wait_title_contains(title, timeout=10): + wait = WebDriverWait(driver, timeout) wait.until(EC.title_contains(title)) @@ -121,6 +121,12 @@ import ./make-test-python.nix ( assert "Web console is running in limited access mode" in driver.page_source + log("Clicking the sudo button") + driver.switch_to.default_content() + driver.find_element(By.CSS_SELECTOR, 'button.ct-locked').click() + log("Checking that /nonexistent is not a thing") + assert '/nonexistent' not in driver.page_source + driver.close() ''; in @@ -136,12 +142,8 @@ import ./make-test-python.nix ( testScript = '' start_all() + server.wait_for_unit("sockets.target") server.wait_for_open_port(9090) - server.wait_for_unit("network.target") - server.wait_for_unit("multi-user.target") - server.systemctl("start", "polkit") - - client.wait_for_unit("multi-user.target") client.succeed("curl -k https://server:9090 -o /dev/stderr") print(client.succeed("whoami")) diff --git a/pkgs/by-name/co/cockpit/package.nix b/pkgs/by-name/co/cockpit/package.nix index 716d8ac3027539..c7932f23e6c84e 100644 --- a/pkgs/by-name/co/cockpit/package.nix +++ b/pkgs/by-name/co/cockpit/package.nix @@ -14,7 +14,6 @@ git, glib, glib-networking, - gnused, gnutls, json-glib, krb5, @@ -31,21 +30,20 @@ pkg-config, polkit, python3Packages, - runtimeShell, systemd, udev, xmlto, }: -stdenv.mkDerivation rec { +stdenv.mkDerivation (finalAttrs: { pname = "cockpit"; - version = "330"; + version = "331"; src = fetchFromGitHub { owner = "cockpit-project"; repo = "cockpit"; - rev = "refs/tags/${version}"; - hash = "sha256-tPoFa9/22WUO+N3Pc/7jdWIOsoImeru6/F+2yUi92iI="; + tag = finalAttrs.version; + hash = "sha256-G0L1ZcvjUCSNkDvYoyConymZ4bsEye03t5K15EyI008="; fetchSubmodules = true; }; @@ -87,29 +85,30 @@ stdenv.mkDerivation rec { echo "#!/bin/sh" > test/node_modules substituteInPlace src/tls/cockpit-certificate-helper.in \ - --replace 'COCKPIT_CONFIG="@sysconfdir@/cockpit"' 'COCKPIT_CONFIG=/etc/cockpit' + --replace-fail 'COCKPIT_CONFIG="@sysconfdir@/cockpit"' 'COCKPIT_CONFIG=/etc/cockpit' substituteInPlace src/tls/cockpit-certificate-ensure.c \ - --replace '#define COCKPIT_SELFSIGNED_PATH PACKAGE_SYSCONF_DIR COCKPIT_SELFSIGNED_FILENAME' '#define COCKPIT_SELFSIGNED_PATH "/etc" COCKPIT_SELFSIGNED_FILENAME' + --replace-fail '#define COCKPIT_SELFSIGNED_PATH PACKAGE_SYSCONF_DIR COCKPIT_SELFSIGNED_FILENAME' '#define COCKPIT_SELFSIGNED_PATH "/etc" COCKPIT_SELFSIGNED_FILENAME' substituteInPlace src/common/cockpitconf.c \ - --replace 'const char *cockpit_config_dirs[] = { PACKAGE_SYSCONF_DIR' 'const char *cockpit_config_dirs[] = { "/etc"' + --replace-fail 'const char *cockpit_config_dirs[] = { PACKAGE_SYSCONF_DIR' 'const char *cockpit_config_dirs[] = { "/etc"' # instruct users with problems to create a nixpkgs issue instead of nagging upstream directly substituteInPlace configure.ac \ - --replace 'devel@lists.cockpit-project.org' 'https://github.com/NixOS/nixpkgs/issues/new?assignees=&labels=0.kind%3A+bug&template=bug_report.md&title=cockpit%25' + --replace-fail 'devel@lists.cockpit-project.org' 'https://github.com/NixOS/nixpkgs/issues/new?assignees=&labels=0.kind%3A+bug&template=bug_report.md&title=cockpit%25' patchShebangs \ build.js \ test/common/pixel-tests \ test/common/run-tests \ test/common/tap-cdp \ - test/static-code \ tools/escape-to-c \ tools/make-compile-commands \ tools/node-modules \ tools/termschutz \ - tools/webpack-make.js + tools/webpack-make.js \ + tools/test-driver \ + test/common/static-code for f in node_modules/.bin/*; do patchShebangs $(realpath $f) @@ -122,17 +121,23 @@ stdenv.mkDerivation rec { for f in pkg/**/*.js pkg/**/*.jsx test/**/* src/**/*; do # some files substituteInPlace report as missing and it's safe to ignore them substituteInPlace "$(realpath "$f")" \ - --replace '"/usr/bin/' '"' \ - --replace '"/bin/' '"' || true + --replace-quiet '"/usr/bin/' '"' \ + --replace-quiet '"/bin/' '"' || true done substituteInPlace src/common/Makefile-common.am \ - --replace 'TEST_PROGRAM += test-pipe' "" # skip test-pipe because it hangs the build + --replace-warn 'TEST_PROGRAM += test-pipe' "" # skip test-pipe because it hangs the build + + substituteInPlace src/ws/Makefile-ws.am \ + --replace-warn 'TEST_PROGRAM += test-compat' "" substituteInPlace test/pytest/*.py \ - --replace "'bash" "'${bashInteractive}/bin/bash" + --replace-quiet "'bash" "'${bashInteractive}/bin/bash" + + echo "m4_define(VERSION_NUMBER, [${finalAttrs.version}])" > version.m4 - echo "m4_define(VERSION_NUMBER, [${version}])" > version.m4 + # hardcode libexecdir, I am assuming that cockpit only use it to find it's binaries + printf 'def get_libexecdir() -> str:\n\treturn "%s"' "$out/libexec" >> src/cockpit/packages.py ''; configureFlags = [ @@ -140,34 +145,15 @@ stdenv.mkDerivation rec { "--disable-pcp" # TODO: figure out how to package its dependency "--with-default-session-path=/run/wrappers/bin:/run/current-system/sw/bin" "--with-admin-group=root" # TODO: really? Maybe "wheel"? - "--enable-old-bridge=yes" ]; enableParallelBuilding = true; - preBuild = '' - patchShebangs \ - tools/test-driver - ''; - - postBuild = '' - chmod +x \ - src/systemd/update-motd \ - src/tls/cockpit-certificate-helper \ - src/ws/cockpit-desktop - - patchShebangs \ - src/systemd/update-motd \ - src/tls/cockpit-certificate-helper \ - src/ws/cockpit-desktop - - substituteInPlace src/ws/cockpit-desktop \ - --replace ' /bin/bash' ' ${runtimeShell}' - ''; - fixupPhase = '' runHook preFixup + patchShebangs $out/libexec/* + wrapProgram $out/libexec/cockpit-certificate-helper \ --prefix PATH : ${ lib.makeBinPath [ @@ -177,41 +163,40 @@ stdenv.mkDerivation rec { } \ --run 'cd $(mktemp -d)' - wrapProgram $out/share/cockpit/motd/update-motd \ - --prefix PATH : ${lib.makeBinPath [ gnused ]} - wrapProgram $out/bin/cockpit-bridge \ --prefix PYTHONPATH : $out/${python3Packages.python.sitePackages} substituteInPlace $out/${python3Packages.python.sitePackages}/cockpit/_vendor/systemd_ctypes/libsystemd.py \ - --replace-fail libsystemd.so.0 ${systemd}/lib/libsystemd.so.0 + --replace-warn libsystemd.so.0 ${systemd}/lib/libsystemd.so.0 substituteInPlace $out/share/polkit-1/actions/org.cockpit-project.cockpit-bridge.policy \ --replace-fail /usr $out + substituteInPlace $out/lib/systemd/*/* \ + --replace-warn /bin /run/current-system/sw/bin + runHook postFixup ''; - doCheck = true; + nativeCheckInputs = [ python3Packages.pytestCheckHook ]; + checkInputs = [ bashInteractive cacert dbus glib-networking openssh - python3Packages.pytest ]; - checkPhase = '' + + preCheck = '' export GIO_EXTRA_MODULES=$GIO_EXTRA_MODULES:${glib-networking}/lib/gio/modules export G_DEBUG=fatal-criticals export G_MESSAGES_DEBUG=cockpit-ws,cockpit-wrapper,cockpit-bridge export PATH=$PATH:$(pwd) - make pytest -j$NIX_BUILD_CORES || true make check -j$NIX_BUILD_CORES || true - test/static-code npm run eslint - npm run stylelint || true + npm run stylelint ''; passthru = { @@ -219,11 +204,12 @@ stdenv.mkDerivation rec { updateScript = nix-update-script { }; }; - meta = with lib; { + meta = { description = "Web-based graphical interface for servers"; mainProgram = "cockpit-bridge"; homepage = "https://cockpit-project.org/"; - license = licenses.lgpl21; - maintainers = with maintainers; [ lucasew ]; + changelog = "https://cockpit-project.org/blog/cockpit-${finalAttrs.version}.html"; + license = lib.licenses.lgpl21; + maintainers = [ lib.maintainers.lucasew ]; }; -} +})