prev. article next article

system services on nixos

10 Oct 2011

#how to integrate a daemon into nixos

in this short article i want to show how services can be added/used in nixos. i personally find the nixos way a quite easy and ‘clean’ approach!

as i integrated cntlm my goals were:

it helped a lot to look at similar scripts like the sshd integration but i also used “NixOS: A Purely functional Linux Distribution” [1] which is describing most aspects which are needed to get a service up and running.

seen as a developer one has to write two nix expressions:

seen as a user one has to modify only one nix expression:

/etc/nixos/nixpkgs/pkgs/tools/networking/cntlm/default.nix

this nix expression describes the cntlm software and is quite simple as it basically fetches the software/compiles it/installs it into the system. however, a user does not have to install this software using:

# nix-env -i cntlm

still this would be possible, and a normal user/root user could use the software in his profile this way.

anyway here is the script:

     1  { stdenv, fetchurl, which}:
     2
     3  stdenv.mkDerivation {
     4    name = "cntlm-0.35.1";
     5
     6    src = fetchurl {
     7      url = mirror://sourceforge/cntlm/cntlm-0.35.1.tar.gz;
     8      sha256 = "7b3fb7184e72cc3f1743bb8e503a5305e96458bc630a7e1ebfc9f3c07ffa6c5e";
     9    };
    10
    11    buildInputs = [ which ];
    12
    13    installPhase = ''
    14      ensureDir $out/bin; cp cntlm $out/bin/;
    15      ensureDir $out/share/; cp COPYRIGHT README VERSION doc/cntlm.conf $out/share/;
    16      ensureDir $out/man/; cp doc/cntlm.1 $out/man/;
    17    '';
    18
    19    meta = {
    20      description = "Cntlm is an NTLM/NTLMv2 authenticating HTTP proxy";
    21      homepage = http://cntlm.sourceforge.net/;
    22      license = stdenv.lib.licenses.gpl2;
    23      maintainers = [ stdenv.lib.maintainers.qknight ];
    24    };
    25  }

the only point of interest might be the buildInputs (line 11) which includes which. in this build script ‘which gcc’ is used to test if gcc is installed.

/etc/nixos/nixos/modules/services/networking/cntlm.nix

this expression is used to integrate cntlm as a system service.

     1  { config, pkgs, ... }:
     2
     3  with pkgs.lib;
     4
     5  let
     6
     7    cfg = config.services.cntlm;
     8    uid = config.ids.uids.cntlm;
     9
    10  in
    11
    12  {
    13
    14    options = {
    15
    16      services.cntlm= {
    17
    18        enable = mkOption {
    19          default = false;
    20          description = ''
    21            Whether to enable the cntlm, which start a local proxy.
    22          '';
    23        };
    24
    25        username = mkOption {
    26          description = ''
    27            Proxy account name, without the possibility to include domain name ('at' sign is interpreted literally).
    28          '';
    29        };
    30
    31        domain = mkOption {
    32          description = ''Proxy account domain/workgroup name.'';
    33        };
    34
    35        password = mkOption {
    36          default = "/etc/cntlm.password";
    37          type = with pkgs.lib.types; string;
    38          description = ''Proxy account password. Note: use chmod 0600 on /etc/cntlm.password for security.'';
    39        };
    40
    41        netbios_hostname = mkOption {
    42          default = config.networking.hostName;
    43          description = ''
    44            The hostname of your workstation.
    45          '';
    46        };
    47
    48        proxy = mkOption {
    49          description = ''
    50            A list of NTLM/NTLMv2 authenticating HTTP proxies.
    51
    52            Parent proxy, which requires authentication. The same as proxy on the command-line, can be used more than  once  to  specify  unlimited
    53            number  of  proxies.  Should  one proxy fail, cntlm automatically moves on to the next one. The connect request fails only if the whole
    54            list of proxies is scanned and (for each request) and found to be invalid. Command-line takes precedence over the configuration file.
    55          '';
    56        };
    57
    58        port = mkOption {
    59          default = [3128];
    60          description = "Specifies on which ports the cntlm daemon listens.";
    61        };
    62
    63       extraConfig = mkOption {
    64          default = "";
    65          description = "Verbatim contents of cntlm.conf.";
    66       };
    67
    68      };
    69
    70    };
    71
    72
    73    ###### implementation
    74
    75    config = mkIf config.services.cntlm.enable {
    76      users.extraUsers = singleton {
    77          name = "cntlm";
    78          description = "cntlm system-wide daemon";
    79          home = "/var/empty";
    80      };
    81
    82      jobs.cntlm = {
    83          description = "cntlm is an NTLM / NTLM Session Response / NTLMv2 authenticating HTTP proxy.";
    84          startOn = "started network-interfaces";
    85          environment = {
    86          };
    87
    88      preStart = '' '';
    89
    90      daemonType = "fork";
    91
    92      exec =
    93        ''
    94          ${pkgs.cntlm}/bin/cntlm -U cntlm \
    95          -c ${pkgs.writeText "cntlm_config" cfg.extraConfig}
    96        '';
    97      };
    98
    99      services.cntlm.extraConfig =
   100        ''
   101          # Cntlm Authentication Proxy Configuration
   102          Username        ${cfg.username}
   103          Domain          ${cfg.domain}
   104          Password        ${cfg.password}
   105          Workstation     ${cfg.netbios_hostname}
   106          ${concatMapStrings (entry: "Proxy ${entry}\n") cfg.proxy}
   107
   108          ${concatMapStrings (port: ''
   109            Listen ${toString port}
   110          '') cfg.port}
   111        '';
   112    };
   113  }

notable parts are:

cfg.proxy = [ “foo” “bar” “baz” ];

is transformed into:

how to make use of the above expressions

a user has to append this configuration into /etc/nixos/configuration.nix and cntlm will be installed/configured and started

  services.cntlm = {
    enable=true;
    username="myusername";
    domain="mydomain";
    proxy=[ "192.168.3.5:1234" ];
  };

summary

in contrast to most other distributions nixos makes not only packaging subject to a ‘clean’ package management but also configuration management (/etc stuff) and runtime management. this is a very clean design helping to avoid lots of pitfalls.

links