liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

configuration.rst (9034B)


      1 Configuration
      2 #############
      3 
      4 Liminix uses the Nix language to provide congruent configuration
      5 management.  This means that to change anything about the way in
      6 which a Liminix system works, you make that change in
      7 your :file:`configuration.nix` (or one of the other files it references),
      8 and rerun :command:`nix-build` or :command:`liminix-rebuild` to action
      9 the change. It is not possible (at least, without shenanigans) to make
     10 changes by logging into the device and running imperative commands
     11 whose effects may later be overridden: :file:`configuration.nix`
     12 always describes the entire system and can be used to recreate that
     13 system at any time.  You can usefully keep it under version control.
     14 
     15 If you are familiar with NixOS, you will notice some similarities
     16 between NixOS and Liminix configuration, and also some
     17 differences. Sometimes the differences are due to the
     18 resource-constrained devices we deploy onto, sometimes due to
     19 differences in the uses these devices are put to.
     20 
     21 
     22 Configuration taxonomy
     23 **********************
     24 
     25 There are many things you can specify in a configuration, but these
     26 are the ones you most commonly need to change:
     27 
     28 * which services (processes) to run
     29 * what packages to install
     30 * permitted users and groups
     31 * Linux kernel configuration options
     32 * Busybox applets
     33 * filesystem layout
     34 
     35 
     36 Modules
     37 *******
     38 
     39 **Modules** are a means of abstraction which allow "bundling"
     40 of configuration options related to a common purpose or theme. For
     41 example, the ``dnsmasq`` module defines a template for a dnsmasq
     42 service, ensures that the dnsmasq package is installed, and provides a
     43 dnsmasq user and group for the service to run as. The ``ppp`` module
     44 defines a service template and also enables various PPP-related kernel
     45 configuration.
     46 
     47 Not all modules are included in the configuration by default, because
     48 that would mean that the kernel (and the Busybox binary providing
     49 common CLI tools) was compiled with many unnecessary bells and whistles
     50 and therefore be bigger than needed. (This is not purely an academic concern
     51 if your device has little flash storage).  Therefore, specifying a
     52 service is usually a two-step process.  For example, to add an NTP
     53 service you first add :file:`modules/ntp` to your ``imports`` list,
     54 then you create a service by calling
     55 :code:`config.system.service.ntp.build { .... }` with the appropriate
     56 service-dependent configuration parameters.
     57 
     58 .. code-block:: nix
     59 
     60   let svc = config.system.service;
     61   in {
     62     # ...
     63     imports = [
     64       ./modules/ntp
     65       # ....
     66     ];
     67     config.services.ntp = svc.ntp.build {
     68       pools = { "pool.ntp.org" = ["iburst"]; };
     69       makestep = { threshold = 1.0; limit = 3; };
     70     };
     71 
     72 Merely including the module won't define the service on its own: it
     73 only creates the template in ``config.system.service.foo`` and you
     74 have to create an actual service using the template. This is an
     75 intentional choice to allow the creation of multiple
     76 differently-configured services based on the same template - perhaps
     77 e.g. when you have multiple networks (VPNs etc) in different trust
     78 domains, or you want to run two SSH daemons on different ports.
     79 (For the background to this, please refer to the :doc:`architecture decision record <adr/module-system>`)
     80 
     81 .. tip:: Liminix modules should be quite familiar (but also different)
     82 	 if you already know how to use NixOS modules. We use the
     83 	 NixOS module infrastructure code, meaning that you should
     84 	 recognise the syntax, the type system, the rules for
     85 	 combining configuration values from different sources. We
     86 	 don't use the NixOS modules themselves, because the
     87 	 underlying system is not similar enough for them to work.
     88 
     89 
     90 Services
     91 ********
     92 
     93 We use the `s6-rc service manager <https://www.skarnet.org/software/s6-rc/overview.html>`_  to start/stop/restart services and handle
     94 service dependencies. Any attribute in `config.services` will become
     95 part of the default set of services that s6-rc will try to bring up on
     96 boot.
     97 
     98 For the most part, for common use cases, hopefully the services you
     99 need will be defined by modules and you will only have to pass the
    100 right parameters to ``build``.
    101 
    102 Should you need to create a custom service of your own devising, use
    103 the `oneshot` or `longrun` functions:
    104 
    105 * a "longrun" service is the "normal" service concept: it has a
    106   ``run`` action which describes the process to start, and it watches
    107   that process to restart it if it exits. The process should not
    108   attempt to daemonize or "background" itself, otherwise s6-rc will think
    109   it died. Whatever it prints to standard output/standard error
    110   will be logged.
    111 
    112 .. code-block:: nix
    113 
    114     config.services.cowsayd = pkgs.liminix.services.longrun {
    115       name = "cowsayd";
    116       run = "${pkgs.cowsayd}/bin/cowsayd --port 3001 --breed hereford";
    117       # don't start this until the lan interface is ready
    118       dependencies = [ config.services.lan ];
    119     }
    120 
    121 
    122 * a "oneshot" service doesn't have a process attached. It consists of
    123   ``up`` and ``down`` actions which are bits of shell script that
    124   are run at the appropriate points in the service lifecycle
    125 
    126 .. code-block:: nix
    127 
    128     config.services.greenled = pkgs.liminix.services.oneshot {
    129       name = "greenled";
    130       up = ''
    131 	echo 17 > /sys/class/gpio/export
    132 	echo out > /sys/class/gpio/gpio17/direction
    133 	echo 0   > /sys/class/gpio/gpio17/value
    134       '';
    135       down = ''
    136 	echo 0   > /sys/class/gpio/gpio17/value
    137       '';
    138     }
    139 
    140 Services may have dependencies: as you see above in the ``cowsayd``
    141 example, it depends on some service called ``config.services.lan``,
    142 meaning that it won't be started until that other service is up.
    143 
    144 ..
    145 	TODO: explain service outputs
    146 
    147 ..
    148 	TODO: outputs that change, and services that poll other services
    149 
    150 Module implementation
    151 *********************
    152 
    153 Modules in Liminix conventionally live in
    154 :file:`modules/somename/default.nix`. If you want or need to
    155 write your own, you may wish to refer to the
    156 examples there in conjunction with reading this section.
    157 
    158 A module is a function that accepts ``{lib, pkgs, config, ... }`` and
    159 returns an attrset with keys ``imports, options config``.
    160 
    161 * ``imports`` is a list of paths to the other modules required by this one
    162 
    163 * ``options`` is a nested set of option declarations
    164 
    165 * ``config`` is a nested set of option definitions
    166 
    167 The NixOS manual section `Writing NixOS Modules
    168 <https://nixos.org/manual/nixos/stable/#sec-writing-modules>`_ is a
    169 quite comprehensive reference to writing NixOS modules, which is also
    170 mostly applicable to Liminix except that it doesn't cover
    171 service templates.
    172 
    173 Service templates
    174 =================
    175 
    176 To expose a service template in a module, it needs the following:
    177 
    178 * an option declaration for ``system.service.myservicename`` with the
    179   type of ``liminix.lib.types.serviceDefn``
    180 
    181 .. code-block:: nix
    182 
    183     options = {
    184       system.service.cowsay = mkOption {
    185 	type = liminix.lib.types.serviceDefn;
    186       };
    187     };
    188 
    189 * an option definition for the same key, which specifies where to
    190   import the service template from (often :file:`./service.nix`)
    191   and the types of its parameters.
    192 
    193 .. code-block:: nix
    194 
    195     config.system.service.cowsay = liminix.callService ./service.nix {
    196       address = mkOption {
    197 	type = types.str;
    198 	default = "0.0.0.0";
    199 	description = "Listen on specified address";
    200 	example = "127.0.0.1";
    201       };
    202       port = mkOption {
    203 	type = types.port;
    204 	default = 22;
    205 	description = "Listen on specified TCP port";
    206       };
    207       breed = mkOption {
    208 	type = types.str;
    209 	default = "British Friesian"
    210 	description = "Breed of the cow";
    211       };
    212     };
    213 
    214 Then you need to provide the service template itself, probably in
    215 :file:`./service.nix`:
    216 
    217 .. code-block:: nix
    218 
    219     {
    220       # any nixpkgs package can be named here
    221       liminix
    222     , cowsayd
    223     , serviceFns
    224     , lib
    225     }:
    226     # these are the parameters declared in the callService invocation
    227     { address, port, breed} :
    228     let
    229       inherit (liminix.services) longrun;
    230       inherit (lib.strings) escapeShellArg;
    231     in longrun {
    232       name = "cowsayd";
    233       run = "${cowsayd}/bin/cowsayd --address ${address} --port ${builtins.toString port} --breed ${escapeShellArg breed}";
    234     }
    235 
    236 .. tip::
    237 
    238    Not relevant to module-based services specifically, but a common
    239    gotcha when specifiying services is forgetting to transform "rich"
    240    parameter values into text when composing a command for the shell
    241    to execute. Note here that the port number, an integer, is
    242    stringified with ``toString``, and the name of the breed,
    243    which may contain spaces, is
    244    escaped with ``escapeShellArg``
    245 
    246 Types
    247 =====
    248 
    249 All of the NixOS module types are available in Liminix. These
    250 Liminix-specific types also exist in ``pkgs.liminix.lib.types``:
    251 
    252 * ``service``: an s6-rc service
    253 * ``interface``: an s6-rc service which specifies a network
    254   interface
    255 * ``serviceDefn``: a service "template" definition
    256 
    257 In the future it is likely that we will extend this to include other
    258 useful types in the networking domain: for example; IP address,
    259 network prefix or netmask, protocol family and others as we find them.