From 897d5670a3da4166ad87b115770ad7d587d7238d Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Tue, 23 Jan 2024 22:36:52 +0100 Subject: [PATCH 1/2] livebook: Use `mix release` to build instead of escript The current build of livebook does not work with the new [Livebook Teams](https://livebook.dev/teams/) features. The problem can be observed by running the current version of livebook, adding a new team and going to the team page. The process will crash and the team page will show a 500 error. The base of the problem is that the escript build method is not officially supported. This commit changes the livebook package to use the `mix release` workflow, which is also the one used to build the official Docker container. Unfortunately, the binary built with `mix release` does not support command line arguments like the `escript` binary does. Instead, users need to pass in most of the configuration as environment variables, as documented [here](https://hexdocs.pm/livebook/readme.html#environment-variables). As a result, this commit also changes the Livebook service to reflect this new way of configuring Livebook. Finally, the Livebook release configuration specifically excludes the ERTS (Erlang Runtime System), which means that the resulting release cannot run without Erlang installed. I have tested the results (both of the package and the service) locally. --- .../manual/release-notes/rl-2405.section.md | 7 ++ .../modules/services/development/livebook.md | 24 ++-- .../modules/services/development/livebook.nix | 106 +++++++++--------- nixos/tests/livebook-service.nix | 10 +- pkgs/servers/web-apps/livebook/default.nix | 10 +- 5 files changed, 84 insertions(+), 73 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index 27dd9a3d8cb2..c034abf28174 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -202,6 +202,13 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - `nomad_1_4` has been removed, as it is now unsupported upstream. +- The `livebook` package is now built as a `mix release` instead of an `escript`. + This means that configuration now has to be done using [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) instead of command line arguments. + This has the further implication that the `livebook` service configuration has changed: + + - The `erlang_node_short_name`, `erlang_node_name`, `port` and `options` configuration parameters are gone, and have been replaced with an `environment` parameter. + Use the appropriate [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) inside `environment` to configure the service instead. + ## Other Notable Changes {#sec-release-24.05-notable-changes} diff --git a/nixos/modules/services/development/livebook.md b/nixos/modules/services/development/livebook.md index 5012e977a4f7..5315f2c2755a 100644 --- a/nixos/modules/services/development/livebook.md +++ b/nixos/modules/services/development/livebook.md @@ -15,11 +15,12 @@ which runs the server. { services.livebook = { enableUserService = true; - port = 20123; + environment = { + LIVEBOOK_PORT = 20123; + LIVEBOOK_PASSWORD = "mypassword"; + }; # See note below about security - environmentFile = pkgs.writeText "livebook.env" '' - LIVEBOOK_PASSWORD = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ''; + environmentFile = "/var/lib/livebook.env"; }; } ``` @@ -30,14 +31,19 @@ The Livebook server has the ability to run any command as the user it is running under, so securing access to it with a password is highly recommended. -Putting the password in the Nix configuration like above is an easy -way to get started but it is not recommended in the real world because -the `livebook.env` file will be added to the world-readable Nix store. -A better approach would be to put the password in some secure -user-readable location and set `environmentFile = /home/user/secure/livebook.env`. +Putting the password in the Nix configuration like above is an easy way to get +started but it is not recommended in the real world because the resulting +environment variables can be read by unprivileged users. A better approach +would be to put the password in some secure user-readable location and set +`environmentFile = /home/user/secure/livebook.env`. ::: +The [Livebook +documentation](https://hexdocs.pm/livebook/readme.html#environment-variables) +lists all the applicable environment variables. It is recommended to at least +set `LIVEBOOK_PASSWORD` or `LIVEBOOK_TOKEN_ENABLED=false`. + ### Extra dependencies {#module-services-livebook-extra-dependencies} By default, the Livebook service is run with minimum dependencies, but diff --git a/nixos/modules/services/development/livebook.nix b/nixos/modules/services/development/livebook.nix index 75729ff28efa..30ab7ae3f128 100644 --- a/nixos/modules/services/development/livebook.nix +++ b/nixos/modules/services/development/livebook.nix @@ -14,58 +14,64 @@ in package = mkPackageOption pkgs "livebook" { }; - environmentFile = mkOption { - type = types.path; - description = lib.mdDoc '' - Environment file as defined in {manpage}`systemd.exec(5)` passed to the service. - - This must contain at least `LIVEBOOK_PASSWORD` or - `LIVEBOOK_TOKEN_ENABLED=false`. See `livebook server --help` - for other options.''; - }; - - erlang_node_short_name = mkOption { - type = with types; nullOr str; - default = null; - example = "livebook"; - description = "A short name for the distributed node."; - }; - - erlang_node_name = mkOption { - type = with types; nullOr str; - default = null; - example = "livebook@127.0.0.1"; - description = "The name for the app distributed node."; - }; - - port = mkOption { - type = types.port; - default = 8080; - description = "The port to start the web application on."; - }; - - address = mkOption { - type = types.str; - default = "127.0.0.1"; - description = lib.mdDoc '' - The address to start the web application on. Must be a valid IPv4 or - IPv6 address. - ''; - }; - - options = mkOption { - type = with types; attrsOf str; + environment = mkOption { + type = with types; attrsOf (nullOr (oneOf [ bool int str ])); default = { }; description = lib.mdDoc '' - Additional options to pass as command-line arguments to the server. + Environment variables to set. + + Livebook is configured through the use of environment variables. The + available configuration options can be found in the [Livebook + documentation](https://hexdocs.pm/livebook/readme.html#environment-variables). + + Note that all environment variables set through this configuration + parameter will be readable by anyone with access to the host + machine. Therefore, sensitive information like {env}`LIVEBOOK_PASSWORD` + or {env}`LIVEBOOK_COOKIE` should never be set using this configuration + option, but should instead use + [](#opt-services.livebook.environmentFile). See the documentation for + that option for more information. + + Any environment variables specified in the + [](#opt-services.livebook.environmentFile) will supersede environment + variables specified in this option. ''; + example = literalExpression '' { - cookie = "a value shared by all nodes in this cluster"; + LIVEBOOK_PORT = 8080; } ''; }; + environmentFile = mkOption { + type = with types; nullOr types.path; + default = null; + description = lib.mdDoc '' + Additional dnvironment file as defined in {manpage}`systemd.exec(5)`. + + Secrets like {env}`LIVEBOOK_PASSWORD` (which is used to specify the + password needed to access the livebook site) or {env}`LIVEBOOK_COOKIE` + (which is used to specify the + [cookie](https://www.erlang.org/doc/reference_manual/distributed.html#security) + used to connect to the running Elixir system) may be passed to the + service without making them readable to everyone with access to + systemctl by using this configuration parameter. + + Note that this file needs to be available on the host on which + `livebook` is running. + + For security purposes, this file should contain at least + {env}`LIVEBOOK_PASSWORD` or {env}`LIVEBOOK_TOKEN_ENABLED=false`. + + See the [Livebook + documentation](https://hexdocs.pm/livebook/readme.html#environment-variables) + and the [](#opt-services.livebook.environment) configuration parameter + for further options. + ''; + example = "/var/lib/livebook.env"; + }; + extraPackages = mkOption { type = with types; listOf package; default = [ ]; @@ -81,17 +87,11 @@ in serviceConfig = { Restart = "always"; EnvironmentFile = cfg.environmentFile; - ExecStart = - let - args = lib.cli.toGNUCommandLineShell { } ({ - inherit (cfg) port; - ip = cfg.address; - name = cfg.erlang_node_name; - sname = cfg.erlang_node_short_name; - } // cfg.options); - in - "${cfg.package}/bin/livebook server ${args}"; + ExecStart = "${cfg.package}/bin/livebook start"; }; + environment = mapAttrs (name: value: + if isBool value then boolToString value else toString value) + cfg.environment; path = [ pkgs.bash ] ++ cfg.extraPackages; wantedBy = [ "default.target" ]; }; diff --git a/nixos/tests/livebook-service.nix b/nixos/tests/livebook-service.nix index 56b4eb932f34..f428412e1644 100644 --- a/nixos/tests/livebook-service.nix +++ b/nixos/tests/livebook-service.nix @@ -9,13 +9,15 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { services.livebook = { enableUserService = true; - port = 20123; + environment = { + LIVEBOOK_PORT = 20123; + LIVEBOOK_COOKIE = "chocolate chip"; + LIVEBOOK_TOKEN_ENABLED = true; + + }; environmentFile = pkgs.writeText "livebook.env" '' LIVEBOOK_PASSWORD = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ''; - options = { - cookie = "chocolate chip"; - }; }; }; }; diff --git a/pkgs/servers/web-apps/livebook/default.nix b/pkgs/servers/web-apps/livebook/default.nix index f0eee2eed605..e1815a819bbf 100644 --- a/pkgs/servers/web-apps/livebook/default.nix +++ b/pkgs/servers/web-apps/livebook/default.nix @@ -22,15 +22,11 @@ beamPackages.mixRelease rec { hash = "sha256-dyKhrbb7vazBV6LFERtGHLQXEx29vTgn074mY4fsHy4="; }; - installPhase = '' - mix escript.build - mkdir -p $out/bin - mv ./livebook $out/bin - + postInstall = '' wrapProgram $out/bin/livebook \ - --prefix PATH : ${lib.makeBinPath [ elixir ]} \ + --prefix PATH : ${lib.makeBinPath [ elixir erlang ]} \ --set MIX_REBAR3 ${rebar3}/bin/rebar3 - ''; + ''; passthru.tests = { livebook-service = nixosTests.livebook-service; From 1ee8e72834880c4859666c501dbbeef896c0d127 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 24 Jan 2024 10:26:00 +0100 Subject: [PATCH 2/2] livebook: Set KillMode=mixed This will gracefully shut down the service instead of resulting in errors like this: ``` Jan 24 10:11:11 foo livebook[981676]: 10:11:11.922 [error] GenServer :disksup terminating Jan 24 10:11:11 foo livebook[981676]: ** (stop) {:port_died, :normal} Jan 24 10:11:11 foo livebook[981676]: Last message: {:EXIT, #Port<0.8>, :normal} Jan 24 10:11:11 foo livebook[981676]: 10:11:11.922 [error] GenServer :memsup terminating Jan 24 10:11:11 foo livebook[981676]: ** (stop) {:port_died, :normal} ``` --- nixos/modules/services/development/livebook.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/modules/services/development/livebook.nix b/nixos/modules/services/development/livebook.nix index 30ab7ae3f128..df0e6e01e97c 100644 --- a/nixos/modules/services/development/livebook.nix +++ b/nixos/modules/services/development/livebook.nix @@ -88,6 +88,7 @@ in Restart = "always"; EnvironmentFile = cfg.environmentFile; ExecStart = "${cfg.package}/bin/livebook start"; + KillMode = "mixed"; }; environment = mapAttrs (name: value: if isBool value then boolToString value else toString value)