configuration.adoc (8835B)
1 == Configuration 2 3 There are many things you can specify in a configuration, but most 4 commonly you need to change: 5 6 * which services (processes) to run 7 * what packages to install 8 * permitted users and groups 9 * Linux kernel configuration options 10 * Busybox applets 11 * filesystem layout 12 13 === Modules 14 15 *Modules* are a means of abstraction which allow "bundling" of 16 configuration options related to a common purpose or theme. For example, 17 the `+dnsmasq+` module defines a template for a dnsmasq service, ensures 18 that the dnsmasq package is installed, and provides a dnsmasq user and 19 group for the service to run as. The `+ppp+` module defines a service 20 template and also enables various PPP-related kernel configuration. 21 22 Not all modules are included in the configuration by default, because 23 that would mean that the kernel (and the Busybox binary providing common 24 CLI tools) was compiled with many unnecessary bells and whistles and 25 therefore be bigger than needed. (This is not purely an academic concern 26 if your device has little flash storage). Therefore, specifying a 27 service is usually a two-step process. For example, to add an NTP 28 service you first add `+modules/ntp+` to your `+imports+` list, then you 29 create a service by calling `+config.system.service.ntp.build { .... }+` 30 with the appropriate service-dependent configuration parameters. 31 32 [source,nix] 33 ---- 34 let svc = config.system.service; 35 in { 36 # ... 37 imports = [ 38 ./modules/ntp 39 # .... 40 ]; 41 config.services.ntp = svc.ntp.build { 42 pools = { "pool.ntp.org" = ["iburst"]; }; 43 makestep = { threshold = 1.0; limit = 3; }; 44 }; 45 ---- 46 47 Merely including the module won't define the service on its own: it only 48 creates the template in `+config.system.service.foo+` and you have to 49 create an actual service using the template. This is an intentional 50 choice to allow the creation of multiple differently-configured services 51 based on the same template - perhaps e.g. when you have multiple 52 networks (VPNs etc) in different trust domains, or you want to run two 53 SSH daemons on different ports. (For the background to this, please 54 refer to the `+architecture decision record <adr/module-system>+`) 55 56 [TIP] 57 ==== 58 Liminix modules should be quite familiar (but also different) if you 59 already know how to use NixOS modules. We use the NixOS module 60 infrastructure code, meaning that you should recognise the syntax, the 61 type system, the rules for combining configuration values from different 62 sources. We don't use the NixOS modules themselves, because the 63 underlying system is not similar enough for them to work. 64 ==== 65 66 [[configuration-services]] 67 === Services 68 69 In Liminix a service is any kind of long-running task or process on the 70 system, that is managed (started, stopped, and monitored) by a service 71 supervisor. A typical SOHO router might have services to 72 73 * answer DHCP and DNS requests from the LAN 74 * provide a wireless access point 75 * connect using PPPoE or L2TP to an upstream network 76 * start/stop the firewall 77 * enable/disable IP packet forwarding 78 * mount filesystems 79 80 (Some of these might not be considered services using other definitions 81 of the term: for example, this L2TP process would be a "client" in the 82 client/server classification; and enabling packet forwarding doesn't 83 require any long-lived process - just a setting to be toggled. However, 84 there is value in being able to use the same abstractions for all the 85 things to manage them and specify their dependency relationships - so in 86 Liminix "everything is a service") 87 88 The service supervision system enables service health monitoring, 89 restart of unhealthy services, and failover to "backup" services when a 90 primary service fails or its dependencies are unavailable. The intention 91 is that you have a framework in which you can specify policy 92 requirements like "ethernet wan dhcp-client should be restarted if it 93 crashes, but if it can't start because the hardware link is down, then 94 4G ppp service should be started instead". 95 96 Any attribute in [.title-ref]#config.services# will become part of the 97 default set of services that s6-rc will try to bring up. Services are 98 usually started at boot time, but *controlled services* are those that 99 are required only in particular contexts. For example, a service to 100 mount a USB backup drive should run only when the drive is attached to 101 the system. Liminix currently implements three kinds of controlled 102 service: 103 104 * "uevent-rule" service controllers use sysfs/uevent to identify when 105 particular hardware devices are present, and start/stop a controlled 106 service appropriately. 107 * the "round-robin" service controller is used for service failover: it 108 allows you to specify a list of services and runs each of them in turn 109 until it exits, then runs the next. 110 * the "health-check" service wraps another service, and runs a "health 111 check" command at regular intervals. When the health check fails, 112 indicating that the wrapped service is not working, it is terminated and 113 allowed to restart. 114 115 === Runtime secrets (external vault) 116 117 Secrets (such as wifi passphrases, PPP username/password, SSH keys, etc) 118 that you provide as literal values in `+configuration.nix+` are 119 processed into into config files and scripts at build time, and 120 eventually end up in various files in the (world-readable) 121 `+/nix/store+` before being baked into a flashable image. To change a 122 secret - whether due to a compromise, or just as part of to a routine 123 key rotation - you need to rebuild the configuration and potentially 124 reflash the affected devices. 125 126 To avoid this, you may instead use a "secrets service", which is a 127 mechanism for your device to fetch secrets from a source external to the 128 Nix store, and create at runtime the configuration files and scripts 129 that start the services which require them. 130 131 Not every possible parameter to every possible service is configurable 132 using a secrets service. Parameters which can be configured this way are 133 those with the type `+liminix.lib.types.replacable+`. At the time this 134 document was written, these include: 135 136 * ppp (pppoe and l2tp): `+username+`, `+password+` 137 * ssh: `+authorizedKeys+` 138 * hostapd: all parameters (most likely to be useful for 139 `+wpa_passphrase+`) 140 141 To use a runtime secret for any of these parameters: 142 143 * create a secrets service to specify the source of truth for secrets 144 * use the `+outputRef+` function in the service parameter to specify the 145 secrets service and path 146 147 For example, given you had an HTTPS server hosting a JSON file with the 148 structure 149 150 [source,json] 151 ---- 152 "ssh": { 153 "authorizedKeys": { 154 "root": [ "ssh-rsa ....", "ssh-rsa ....", ... ] 155 "guest": [ "ssh-rsa ....", "ssh-rsa ....", ... ] 156 } 157 } 158 ---- 159 160 you could use a `+configuration.nix+` fragment something like this to 161 make those keys visible to ssh: 162 163 [source,nix] 164 ---- 165 services.secrets = svc.secrets.outboard.build { 166 name = "secret-service"; 167 url = "http://10.0.0.1/secrets.json"; 168 username = "secrets"; 169 password = "liminix"; 170 interval = 30; # minutes 171 dependencies = [ config.services.lan ]; 172 }; 173 services.sshd = svc.ssh.build { 174 authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys"; 175 }; 176 ---- 177 178 There are presently two implementations of a secrets service: 179 180 ===== Outboard secrets (HTTPS) 181 182 This service expects a URL to a JSON file containing all the secrets. 183 184 You may specify a username and password along with the URL, which are 185 used if the file is password-protected (HTTP Basic authentication). Note 186 that this is not a protection against a malicious local user: the 187 username and password are normal build-time parameters so will be 188 readable in the Nix store. This is a mitigation against the URL being 189 accidentally discovered due to e.g. a log file or error message on the 190 server leaking. 191 192 ===== Tang secrets (encrypted local file) 193 194 Aternatively, secrets may be stored locally on the device, in a file 195 that has been encrypted using https://github.com/latchset/tang[Tang]. 196 197 ____ 198 Tang is a server for binding data to network presence. 199 200 This sounds fancy, but the concept is simple. You have some data, but 201 you only want it to be available when the system containing the data is 202 on a certain, usually secure, network. 203 ____ 204 205 [source,nix] 206 ---- 207 services.secrets = svc.secrets.tang.build { 208 name = "secret-service"; 209 path = "/run/mnt/usbstick/secrets.json.jwe"; 210 interval = 30; # minutes 211 dependencies = [ config.services.mount-usbstick ]; 212 }; 213 ---- 214 215 The encryption uses the same scheme/algorithm as 216 https://github.com/latchset/clevis[Clevis] : you may use the 217 https://github.com/latchset/clevis?tab=readme-ov-file#pin-tang[Clevis 218 instructions] to encrypt the file on another host and then copy it to 219 your Liminix device, or you can use `+tangc encrypt+` to encrypt 220 directly on the device. (That latter approach may pose a chicken/egg 221 problem if the device needs secrets to boot up and run the services you 222 are relying on in order to login). 223