Giving systemd service, access to the shell commands

I’m trying to use a webhook server on NixOS. It’s an HTTP endpoint handler. You define an endpoint and when there’s GET or POST request arrives to this endpoint, webhook executes command(or script). I created systemd service, which has to run webhook as a service. Here’s a configuration:


{ pkgs, ... }:
{
  environment.etc."webhook.conf".text = ''
  [
    {
      "id": "update",
      "execute-command": "/var/webhook/update.sh",
      "command-working-directory": "/tmp"
    },

    {
      "id": "rollback",
      "execute-command": "nixos-rebuild switch --rollback",
      "command-working-directory": "/tmp"
    },
  ]
  '';
  users.users.webhook = {
    isNormalUser = true;
  };
  systemd.services.webhook = {
    enable = true;
    serviceConfig = {
      User = "webhook";
      ExecStart = "${pkgs.webhook}/bin/webhook -hooks /etc/webhook.conf -secure -cert /var/lib/acme/example.com/fullchain.pem -key /var/lib/acme/example.com/key.pem -verbose";
    };
  };
}

But when I trigger the command execution, webhook fails to execute any command I ask it because “command not found”


● webhook.service
   Loaded: loaded (/nix/store/7gc38iaqm789xgw741ha5ry6mvfs0zza-unit-webhook.service/webhook.service; linked; vendor preset: enabled)
   Active: active (running) since Thu 2020-09-24 12:16:18 UTC; 11min ago
 Main PID: 30867 (webhook)
       IP: 1.4K in, 5.0K out
    Tasks: 5 (limit: 1166)
   Memory: 8.4M
      CPU: 42ms
   CGroup: /system.slice/webhook.service
           └─30867 /nix/store/a3513plsphspb0fa55yyhznkbk2fv4mr-webhook-2.6.8-bin/bin/webhook -hooks /etc/webhook.conf -secure -cert /var/lib/acme/ilchub.net/fullchain.pem -key /var/lib/acme/ilchub.net/key.pem -ve>

Sep 24 12:16:18 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:18 serving hooks on https://0.0.0.0:9000/hooks/{id}
Sep 24 12:16:18 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:18 os signal watcher ready
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 [5c4246] incoming HTTP request from 93.77.150.2:49634
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 [5c4246] update got matched
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 [5c4246] update hook triggered successfully
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 200 | 1.238232ms | example.com:9000 | GET /hooks/update
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 [5c4246] executing /var/webhook/update.sh (/var/webhook/update.sh) with arguments ["/var/webhook/update.sh"] and environment [] using /tmp as c>
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 [5c4246] command output: /var/webhook/update.sh: line 3: nixos-rebuild: command not found
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 [5c4246] error occurred: exit status 127
Sep 24 12:16:35 ilchub webhook[30867]: [webhook] 2020/09/24 12:16:35 [5c4246] finished handling update

Here’s a content of /var/webhook/update.sh

#!/run/current-system/sw/bin/bash

nixos-rebuild switch --upgrade

So, I’m wondering, how to make this script, running in the shell with all commands available

You have to replace nixos-rebuild with something such as ${config.system.build.nixos-rebuild}/bin/nixos-rebuild. You can look at the auto-upgrade module.

Also, instead of writing -hooks /etc/webhook.conf you should write something such as -hooks ${myWebhookFile} (where myWebhookFile = pkgs.writeText "filename" "file content"), otherwise, nixos-rebuild won’t be able to restart your service on configuration file changes. You could also use the “writers” to create scripts on the fly :wink:

I followed your advice and rewritten my webhook.nix, this way:

{ pkgs, config, ... }:
{
  nixpkgs.overlays = [(self: super: {
    updateScript = pkgs.writeScriptBin "updateScript" ''
      #!${pkgs.stdenv.shell}

      ${config.system.build.nixos-rebuild}/bin/nixos-rebuild --upgrade
    '';
  })];
  environment.etc."webhook.conf".text = ''
  [
    {
      "id": "update",
      "execute-command": "${pkgs.updateScript}/bin/updateScript",
      "command-working-directory": "/tmp"
    },

    {
      "id": "rollback",
      "execute-command": "nixos-rebuild switch --rollback",
      "command-working-directory": "/tmp"
    },
  ]
  '';
  users.users.webhook = {
    isNormalUser = false;
  };
  systemd.services.webhook = {
    enable = true;
    serviceConfig = {
      User = "webhook";
      ExecStart = "${pkgs.webhook}/bin/webhook -hooks /etc/webhook.conf -secure -cert /var/lib/acme/example.com/fullchain.pem -key /var/lib/acme/example.com/key.pem -verbose";
    };
  };
}

but the problem seems to be in the nixos-rebuild script, itself

● webhook.service
   Loaded: loaded (/nix/store/7gc38iaqm789xgw741ha5ry6mvfs0zza-unit-webhook.service/webhook.service; linked; vendor preset: enabled)
   Active: active (running) since Thu 2020-09-24 14:13:00 UTC; 14s ago
 Main PID: 32647 (webhook)
       IP: 1.5K in, 3.6K out
    Tasks: 6 (limit: 1166)
   Memory: 1.8M
      CPU: 42ms
   CGroup: /system.slice/webhook.service
           └─32647 /nix/store/a3513plsphspb0fa55yyhznkbk2fv4mr-webhook-2.6.8-bin/bin/webhook -hooks /etc/webhook.conf -secure -cert /var/lib/acme/example.com/fullchain.pem -key /var/lib/acme/example.com/key.pem -ve>

Sep 24 14:13:00 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:00 serving hooks on https://0.0.0.0:9000/hooks/{id}
Sep 24 14:13:00 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:00 os signal watcher ready
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 [faa855] incoming HTTP request from 93.77.150.2:52462
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 [faa855] update got matched
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 [faa855] update hook triggered successfully
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 200 | 1.622591ms | example.com:9000 | GET /hooks/update
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 [faa855] executing /nix/store/8whbihdvcz7k84rm0qip1nclzq9zpw89-updateScript/bin/updateScript (/nix/store/8whbihdvcz7k84rm0qip1nclzq9zpw89-updat>
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 [faa855] command output: /nix/store/85vha2v87a5gbqdy1kvwh4c11h1av0rg-nixos-rebuild/bin/nixos-rebuild: line 11: exec: man: not found
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 [faa855] error occurred: exit status 127
Sep 24 14:13:13 ilchub webhook[32647]: [webhook] 2020/09/24 14:13:13 [faa855] finished handling update

nixos-rebuild calls other binaries directly, instead of using Nixpkg variables

Then a systemd unit can define it’s path:

systemd.services.<name>.path where you could put the different packages your service may require

1 Like

Could you, please, provide more detailed usage examples of this option?

Here for instance.

2 Likes

Yeah. Seems like a plan. But I tried adding nix-channel command, this way:

systemd.services.webhook = {
    path = with pkgs; [
      man
      nix-channel
    ];
    enable = true;
    serviceConfig = {
      User = "webhook";
      ExecStart = "${pkgs.webhook}/bin/webhook -hooks /etc/webhook.conf -secure -cert /var/lib/acme/ilchub.net/fullchain.pem -key /var/lib/acme/ilchub.net/key.pem -verbose";
    };

and got configuration switch error:

building Nix...
building the system configuration...
error: undefined variable 'nix-channel' at /etc/nixos/webhook.nix:31:7
(use '--show-trace' to show detailed location information)

How do I include this package?

try config.nix.package.out instead of nix-channel

1 Like

What about source command? I thought it would work without adding it to systemd path, but it doesn’t. So I added source, but it says error: undefined variable 'source' How to use it in systemd services?