THOUGHTS.txt (317599B)
1 Thu Sep 22 00:12:42 BST 2022 2 3 Making quite reasonable progress, though only running under emulation. 4 Since almost everything so far has been a recap of nixwrt, that's to 5 be expected. 6 7 The example config starts some services at boot, or at least attempts 8 to. Next we shoud 9 10 - add some network config to run-qemu 11 - implement udhcp and odhcp properly to write outputs 12 and create resolv.conf and all that 13 - write some kind of test so we can refactor the crap 14 - not let the tests write random junk everywhere 15 16 Thu Sep 22 12:46:36 BST 2022 17 18 We can store outputs in the s6 scan directory, it seems: 19 20 > There is, however, a guarantee that s6-supervise will never touch subdirectories named data or env. So if you need to store user information in the service directory with the guarantee that it will never be mistaken for a configuration file, no matter the version of s6, you should store that information in the data or env subdirectories of the service directory. 21 22 https://skarnet.org/software/s6/servicedir.html 23 24 > process 'store/pj0b27l5728cypa5mmagz0q8ibzpik0h-execline-mips-unknown-linux-musl-2.9.0.1-bin/bin/execlineb' started with executable stack 25 26 https://skarnet.org/lists/skaware/1550.html 27 28 29 Thu Sep 22 16:14:49 BST 2022 30 31 what network peers do we want to model for testing? 32 33 - wan: pppoe 34 - wan: ip over ethernet, w/ dhcp service provided 35 - wan: l2tp over (ip over ethernet, w/ dhcp service provided) 36 - lan: something with a dhcp client 37 38 https://accel-ppp.readthedocs.io/en/latest/ could use this for testing 39 pppoe and l2tp? 40 41 42 Thu Sep 22 22:57:47 BST 2022 43 44 To build a nixos vm with accel-ppp installed (not yet configured) 45 46 nix-build '<nixpkgs/nixos>' -A vm -I nixos-config=./tests/ppp-server-configuration.nix -o ppp-server 47 QEMU_OPTS="-display none -serial mon:stdio -nographic" ./ppp-server/bin/run-nixos-vm 48 49 To test it's configured I thought I'd run it against an OpenWrt qemu 50 install, so, fun with qemu networking ensues. This config in ../openwrt-qemu 51 is using two multicast socket networks - 52 53 nix-shell -p qemu --run "./run.sh ./openwrt-22.03.0-x86-64-generic-kernel.bin openwrt-22.03.0-x86-64-generic-ext4-rootfs.img " 54 55 so hopefully we can spin up other VMs connected either to its lan or 56 its wan: *however* we do first need to configure its wan to use pppoe 57 58 uci set network.wan=interface 59 uci set network.wan.device='eth1' 60 uci set network.wan.proto='pppoe' 61 uci set network.wan.username='db123@a.1' 62 uci set network.wan.password='NotReallyTheSecret' 63 64 (it's ext4 so this will probably stick) 65 66 67 Fri Sep 23 10:27:22 BST 2022 68 69 * mcast=230.0.0.1:1234 : access (interconnect between router and isp) 70 * mcast=230.0.0.1:1235 : lan 71 * mcast=230.0.0.1:1236 : world (the internet) 72 73 74 Sun Sep 25 20:56:28 BST 2022 75 76 TODO - bugs, missing bits, other infelicities as they occur to me: 77 78 DONE 1) shutdown doesn't work as its using the busybox one not s6. 79 80 2) perhaps we shouldn't have process-based services like dhcp, ppp 81 implement "address provider interface" - instead have a separate 82 service for interface address that depends on the service and uses its 83 output 84 85 * ppp is not like dhcp because dhcp finds addresses for an existing 86 interface but ppp makes a new one 87 88 3) when I killed ppp it restarted, but I don't think it reran 89 defaultroute which is supposed to depend on it. (Might be important 90 e.g. if we'd been assigned a different IP address). Investigate 91 semantics of s6-rc service dependencies 92 93 DONE 4) make the pppoe test run unattended 94 95 5) write a test for udhcp 96 97 6) squashfs size is ~ 14MB for a configuration with not much in it, 98 look for obvious wastes of space 99 100 7) some of the pppoe config should be moved into a ppp service 101 102 8) some of configuration.nix (e.g. defining routes) should be moved into 103 tools 104 105 DONE 9) split tools up instead of having it all one file 106 107 10) is it OK to depend on squashfs pseudofiles if we might want to 108 switch to ubifs? will there always be a squashfs underneath? might 109 we want to change the pseudofiles in an overlay? 110 111 11) haven't done (overlayfs) overlays at all 112 113 12) overlay.nix needs splitting up 114 115 13) upgrade ppp to something with an ipv6-up-script option 116 117 14) add ipv6 support generally 118 119 15) "ip address add" seems to magically recognise v4 vs v6 but 120 is that specified or fluke? 121 122 16) tighten up the module specs. (DONE) services.foo should be a s6-rc 123 service, (DONE) kernel config should be checked in some way 124 125 DONE 17) rename nixwrt references in kernel builder 126 127 18) maybe stop suffixing all the service names with .service 128 129 19) syslogd - use busybox or s6? 130 131 chat -s -S ogin:--ogin: root / "ip address show dev ppp0 | grep ppp0" 192.168.100.1 "/nix/store/*-s6-linux-init-*/bin/s6-linux-init-hpr -p" 132 133 134 Working towards a general goal of having a derivation we can 135 usefully run `nix path-info` on - or some other tool that will 136 tell us what's making the images big. The squashfs doesn't 137 have this information. 138 139 Towards that end (really? can't remember how ...) what would be a 140 way for packages to declare "I want to add files to /etc"? Is that 141 even a good idea? 142 143 Thinking we should turn s6-init-files back into a real derivation. 144 145 Tue Sep 27 00:31:45 BST 2022 146 147 > Thinking we should turn s6-init-files back into a real derivation. 148 149 This turns out to be Not That Simple, because it contains weird shit 150 (sticky bits and fifos). 151 152 Tue Sep 27 09:50:44 BST 2022 153 154 * allow modules to register activation scripts that are run on the 155 root filesystem once all packages are installed 156 157 - do they run on build or on host? if we're upgrading in place 158 how do we ship filesystem changes to the host? 159 160 or: 161 162 * allow modules to declare environment.*, use pseudofile on build and 163 create real files on host. will need to keep the implementation on 164 host faily simple because restricted environment 165 166 Tue Sep 27 16:14:18 BST 2022 167 168 TODO list is getting both longer and shorter, though longer on 169 average. 170 171 2) perhaps we shouldn't use process-based services like [ou]dhcp as 172 queryable endpoint for interface addresses (e.g. when adding routes). 173 Instead have a separate service for interface address that depends on 174 the *dhcp and uses its output 175 176 3) when I killed ppp it restarted, but I don't think it reran 177 defaultroute which is supposed to depend on it. (Might be important 178 e.g. if we'd been assigned a different IP address). Investigate 179 semantics of s6-rc service dependencies 180 181 4) figure out a nice way to fit ppp into this model as it actually 182 creates the interface instead of using an existing unconfigured one 183 184 5) write a test for udhcp 185 186 7) some of the pppoe config should be moved into a ppp service 187 188 11) haven't done (overlayfs) overlays at all 189 190 13) upgrade ppp to something with an ipv6-up-script option, move ppp and pppoe derivations into their own files 191 192 14) add ipv6 support generally 193 194 15) "ip address add" seems to magically recognise v4 vs v6 but 195 is that specified or fluke? 196 197 19) ship logs somehow to log collection system 198 199 21) dhcp, dns, hostap service for lan 200 201 22) support real hardware 202 203 Tue Sep 27 22:00:36 BST 2022 204 205 Found the cause of huge image size: rp-pppoe ships with scripts that 206 reference build-time packages, so we have x86-64 glibc in there 207 208 We don't need syslog just to accommodate ppp, there's an underdocumented 209 option for it to log to a file descriptor 210 211 Wed Sep 28 16:04:02 BST 2022 212 213 Based on https://unix.stackexchange.com/a/431953 if we can forge 214 ethernet packets we might be able to write tests for e.g. "is the vm 215 running a dhcp server" 216 217 Wed Sep 28 21:29:05 BST 2022 218 219 We can use Python "scapy" to generate dhcp request packets, and Python 220 'socket' model to send them encapsulated in UDP. Win 221 222 It's extremely janky python 223 224 Thu Sep 29 15:24:37 BST 2022 225 226 Two points to ponder 227 228 1) where service config depends on outputs of other services, we 229 do that rather ugly "$(cat ${output ....})" construct. Can we improve on 230 that? Maybe we could have some kind of tooling to read them as environment 231 variables ... 232 233 2) we have given no consideration yet to secrets. we want the secrets to 234 be not in the store; we want some way of refreshing them when they change 235 236 Sat Oct 1 14:24:21 BST 2022 237 238 The MAC80211_HWSIM kernel config creates virtual wlan[01] devices 239 which hostapd will work with, and a hwsim0 which we can use to monitor 240 (though not inject) trafic. Could we use this for wifi tests? How do 241 we make the guest hwsim0 visible to the host? 242 243 244 Sat Oct 1 18:41:31 BST 2022 245 246 virtual serial ports: I struggled with qemu for ages to get this to work. 247 You also need the unhelpfully named CONFIG_VIRTIO_CONSOLE option in 248 kconfig 249 250 QEMU_OPTIONS="-nodefaults -chardev socket,path=/tmp/wlan,server=on,wait=off,id=wlan -device virtio-serial-pci -device virtserialport,name=wlan,chardev=wlan" 251 252 Sun Oct 2 09:34:48 BST 2022 253 254 We could implement the secrets store as a service, then the secrets 255 are outputs. 256 257 Things we can do in qemu 258 259 1) make interface address service that depends on dhcp, instead of 260 being set by it directly 261 2) check out restart behaviour of dependent services when depended-on 262 service dies 263 3) pppd _creates_ an interface, work out how to fit it into this model 264 5) add bridge support for lan 265 8) upgrade ppp to something with an ipv6-up-script option, move ppp and pppoe derivations into their own files 266 9) get ipv6 address from pppoe 267 10) get ipv6 delegation from pppoe and add prefix to lan 268 11) support dhcp6 in dnsmasq, and advertise prefix on lan 269 12) firewalling and nat 270 - default deny or zero trust? 271 14) write secrets holder as a service with outputs 272 20) should we check that references to outputs actually correspond with 273 those provided by a service 274 275 Things we probably do on hardware 276 277 6) writable filesystem (ubifs?) 278 7) overlay with squashfs/ubifs - useful? think about workflows for 279 how this thing is installed 280 16) gl-ar750 281 17) mediatek device - gl-mt300 or whatever I have lying around 282 18) some kind of arm (banana pi router?) 283 19) should we give routeros a hardware ethernet and maybe an l2tp upstream, 284 then we could dogfood the hardware devices. we could run an l2tp service 285 at mythic-beasts, got a /48 there 286 287 288 289 https://skarnet.org/software/s6/s6-fghack.html looks like a handy thing 290 we hope we'll never have to use 291 292 Sun Oct 2 22:22:17 BST 2022 293 294 > make interface address service that depends on dhcp, instead of being set by it directly 295 296 We can do this for dhcp, but we can't do it for ppp. Running the ppp service 297 creates a ppp[012n] interface and assigns it an ipv4 address and there's not 298 a whole lot we can easily do to unbundle that. 299 300 So 301 302 - the ppp service needs to behave as if it were a "link" service 303 - either it *also* needs to behave as an address service, or we could 304 have an address service that subscribes to it and does nothing other than 305 translate output formats 306 307 Note regarding that second bullet: at the moment the static address 308 service has no outputs anyway! 309 310 311 Tue Oct 4 22:43:02 BST 2022 312 313 While trying to make the TFTP workflow not awful I seem to have written 314 a TFTP server. 315 316 317 Thu Oct 6 19:26:40 BST 2022 318 319 We have a booting kernel on gl-ar750, but we aren't at a point that it can 320 find a root filesystem 321 322 I'd *like* to be able to use the same delivery mechanism (kernel uimage 323 concatenated monolithic 324 325 326 Sat Oct 8 11:12:09 BST 2022 327 328 We have it booting on hardware, mounting root fs, running getty :-) 329 330 For NixWRT TFTP boots we used a single image with both kernel and squashfs, and 331 relied on CONFIG_MTD_SPLIT_FIRMWARE to identify where the boundary was and create 332 /dev/mdtn devices at the right offsets so that the kernel could find the 333 squashfs 334 335 For Liminix we're not going to do that. 336 337 * CONFIG_MTD_SPLIT_FIRMWARE is only available in OpenWrt patches 338 * it's an uncomfortable level of automagic just to save us doing two TFTPs 339 instea of one 340 * the generated image is anyway not the one we'd write to flash (has unneeded 341 PHRAM support) 342 * it means we need to memmap out enough ram for the whole image inc kernel when really 343 all we need to reserve is the rootfs bit 344 345 346 Sat Oct 8 11:23:08 BST 2022 347 348 "halt" and "reboot" don't work on gl-ar750 349 350 Sat Oct 8 13:10:00 BST 2022 351 352 Where do we go with this ar750? 353 354 - wired networking 355 - wifi 356 357 358 Sun Oct 9 09:57:35 BST 2022 359 360 We want to be able to package kernel modules as regular derivations, so that 361 they get added to the filesystem 362 363 This means they need access to kernel.modulesupport 364 365 This means kernel.modulesupport needs to be in pkgs too? 366 367 This is fine, probably, but we'd like to avoid closing over vmlinux because 368 there's no need for it to be in the filesystem 369 370 Mon Oct 10 22:57:23 BST 2022 371 372 The problem is that kernel kconfig options are manipulated in the 373 liminix modules, which means that data must be (transitively) available 374 to modules, so they can't be regular packages as they're tied so tightly 375 to the exact config. Unless we define a second overlay that references 376 the configuration object, but my head hurts when I start to think about that 377 so maybe not. 378 379 Tue Oct 11 00:00:13 BST 2022 380 381 Building ag71xx (ethernet driver) as a module doesn't work because 382 it references a symbol ath79_pll_base in the kernel that hasn't been 383 marked with EXPORT_SYMBOL. 384 385 We could forge an object file that "declares" it with a gross and disgusting hack like this 386 387 $ echo > empty # not actually "empty", objcopy complains about that 388 $ grep ath79_pll_base /nix/store/jcc114cd13xa8aa4mil35rlnmxnlmv09-vmlinux-mips-unknown-linux-musl-modulesupport/System.map 389 ffffffff807b2094 B ath79_pll_base 390 $ mips-unknown-linux-musl-objcopy -I binary -O elf32-big --add-section .bss=empty --add-symbol ath79_pll_base=.bss:0x807b2094 empty f.o 391 392 I don't claim this is a good idea, just an idea. Thought was that we would not 393 have to declare its type this way. Also it might not work with kaslr 394 https://stackoverflow.com/a/68903503 395 396 397 Backstory: why are we trying to build this as a module? because the 398 openwrt fork of it seems to be a bit more advanced than the mainline, 399 and I *suspect* that the mainline version doesn't work with our 400 openwrt-based device tree which ahs the mdio as a nested node inside 401 the ag71xx node - in mainline the driver seems to have all the mdio 402 stuff inline. So, could we build the openwrt driver without patching 403 the crap out of our kernel 404 405 Sun Oct 16 15:25:33 BST 2022 406 407 Executive decision: let's use the openwrt kernel (at least for 408 gl-ar750). Mainline kernel doesn’t have devicetree support for this 409 device or the SoC it’s based on, and the OpenWrt dts for it doesn’t 410 have the same "compatible"s, which makes me think that an indefinite 411 amount of patching will be necessary to make dts/modules for one of 412 them work with a kernel for the other 413 414 As a result: now we have eth0 appearing, but not eth1? Guessing we 415 need to add some kconfig for the switch 416 417 Mon Oct 17 21:23:37 BST 2022 418 419 we are spending ridiculous amounts of cpu/io time copying kernel source 420 trees from place to place, because we have kernel tree preparation 421 and actual building as two separate derivations. 422 423 I think the answer is to have a generic kernel build derivation 424 in the overlay, and then have the device overlays override it with 425 an additional phase to do openwrt patching or whatever else they 426 need to do. 427 428 Tue Oct 18 23:02:43 BST 2022 429 430 * previous TODO list is Aug 02, need to review 431 * dts is hardcoded to gl-ar750, that needs cleaning up 432 * figure out persistent addresses for ethernet 433 * fix halt/reboot 434 * "link" services have a "device" attribute, would much rather 435 have everything referenced using outputs than having two 436 different mechanisms for reading similar things 437 * Kconfig.local do we still need it? 438 * check all config instead of differentiating config/checkedConfig 439 440 Sun Feb 5 18:14:02 GMT 2023 441 442 We have resumed. 443 commit eb4efab6a215bf03cf5aab10d4ac909e83e9c148 444 Author: Daniel Barlow <dan@telent.net> 445 Date: Sat Jan 28 23:18:28 2023 +0000 446 447 448 * find out what works 449 * add that stuff to hydra 450 * fix the rest 451 * add that stuff to hydra 452 * convert to flake 453 * check if routeros can be run interactively 454 * some per-device docs in a form that can be transcluded for website 455 456 457 ci builds 458 459 * each of the tests has hardcoded device/config/etc 460 * build an "empty" configuration for each target device 461 * build an unstable configuration for qemu 462 463 464 Wed Feb 8 16:52:22 GMT 2023 465 466 We have hydra builds for all the previously-working devices, though we 467 don't yet know if any of those builds actually boots or does anything 468 useful. 469 470 [DONE] Would be nice to clean up the run-qemu and connect-qemu scripts 471 and put them in the buildEnv 472 473 Some thought needed about how to hook up the gl-ar750 to the internets, 474 ideally in a way that mirrors typical real uses. AAISP have an L2TP 475 service, but I would prefer to use pppoe on the device, so how to 476 translate one to t'other on an intermediary/gateway machine? 477 https://www.rfc-archive.org/getrfc.php?rfc=3817#gsc.tab=0 exists 478 as an RFC but I can't find anything that actually implements it 479 480 Actual Documentation (e.g. user and developer manuals) should live in 481 the liminix repo so it corresponds with the code, and can be rsynced 482 from there to the web site, maybe with a deploy hook or something. 483 Haven't decided what a good doc format is yet 484 485 If we create a flake for Hydra to run on, that _more or less_ means we 486 don't have any manual hydra jobset configuration to document. 487 488 There are still some tests that need adding to CI 489 490 [DONE] Should the per-device config be a module not an overlay? Given that 491 half of what's in it is kernel config (a module could set this) 492 and the rest is source tarball download specs (needs nixpkgs, 493 a module has this and could set it too) I wonder why it isn't already 494 495 [ALREADY DOES] Can we make Hydra report output sizes so we can plot closure size 496 trends and see if it all goes awful? 497 498 Thu Feb 9 08:14:39 GMT 2023 499 500 For better developer experience, I am thinking that either (1) 501 swap tasks 2 and 3 (writable filesystem before module system) 502 or (2) add NBD support so I can iterate on a real device without 503 full rebuilds every time 504 505 506 Fri Feb 10 06:18:25 PM GMT 2023 507 508 did the overlay->module thing 509 510 [DONE] Need to fix all the configuration around PHRAM, I can't see how it 511 would ever work 512 513 Sat Feb 11 14:37:45 GMT 2023 514 515 Consolidated TODO 516 517 * figure out persistent addresses for ethernet (?) 518 [SEEMS DONE] * fix halt/reboot 519 [DONE, NO] * Kconfig.local do we still need it? 520 [DONE] * check all config instead of differentiating config/checkedConfig 521 522 Things we can do in qemu 523 524 * "link" services have a "device" attribute, would much rather 525 have everything referenced using outputs than having two 526 different mechanisms for reading similar things 527 1) make interface address service that depends on dhcp, instead of 528 being set by it directly 529 2) check out restart behaviour of dependent services when depended-on 530 service dies 531 3) pppd _creates_ an interface, work out how to fit it into this model 532 5) add bridge support for lan 533 8) upgrade ppp to something with an ipv6-up-script option, move ppp and pppoe derivations into their own files 534 9) get ipv6 address from pppoe 535 10) get ipv6 delegation from pppoe and add prefix to lan 536 11) support dhcp6 in dnsmasq, and advertise prefix on lan 537 12) firewalling and nat 538 - default deny or zero trust? 539 14) write secrets holder as a service with outputs 540 20) should we check that references to outputs actually correspond with 541 those provided by a service 542 * Actual Documentation (e.g. user and developer manuals) 543 * make a flake 544 * There are still some tests that need adding to CI 545 546 Things we probably do on hardware 547 548 [DONE] * dts is hardcoded to gl-ar750, that needs cleaning up 549 6) writable filesystem (ubifs?) 550 7) overlay with squashfs/ubifs - useful? think about workflows for 551 how this thing is installed 552 16) gl-ar750 553 [DONE] * decide how to hook up the gl-ar750 to the internets 554 17) mediatek device - gl-mt300 or whatever I have lying around 555 18) some kind of arm (banana pi router?) 556 [DONE DIFERENTLY] 19) should we give routeros a hardware ethernet and maybe an l2tp upstream, 557 then we could dogfood the hardware devices. we could run an l2tp service 558 at mythic-beasts, got a /48 there 559 560 561 Sat Feb 11 15:57:31 GMT 2023 562 563 The reason we would like to run PPPoE instead of L2TP on the "rotuer" device is 564 565 - closer to real world scenario 566 - means no need to run dhcp client on the wan interface before we 567 even get to start the l2tpd 568 569 570 Sun Feb 12 14:57:28 GMT 2023 571 572 https://github.com/katalix/go-l2tp#kpppoed 573 574 575 Mon Feb 13 04:44:09 PM GMT 2023 576 577 if the gl-ar750 is connected to an ethernet card that linux is ignoring, 578 we're going to have to set up _some_ qemu thing just to run tftp from. 579 580 Tue Feb 14 17:59:34 GMT 2023 581 582 We should do a derivation that creates an ISO image and a qemu shell 583 script based on a configuration.nix, and put it in buildEnv. We'll 584 call it "borderNetVm" : 585 586 > A broadband remote access server (BRAS, B-RAS or BBRAS) routes 587 traffic to and from broadband remote access devices such as digital 588 subscriber line access multiplexers (DSLAM) on an Internet service 589 provider's (ISP) network.[1][2] BRAS can also be referred to as a 590 broadband network gateway or border network gateway (BNG).[3] 591 592 (for consistency we should rename the "access" qemu socket network to 593 match whatever we call this) 594 595 rm border.qcow2 ; nix-shell --argstr liminix `pwd` --argstr nixpkgs `pwd`/../nixpkgs --argstr unstable `pwd`/../unstable-nixpkgs/ ci.nix -A buildEnv --run "run-border-vm" 596 597 Wed Feb 15 22:56:59 GMT 2023 598 599 configuration for border vm needs to come from somewhere so it's good 600 for more people than just me 601 602 - pci device for setting up the ethernet 603 - lns address 604 - uid so it can do 9p shares? do we need to map things here? 605 606 also need to document the host-side bits so that people can set up 607 their spare ethernet as vfio 608 609 next step for hacking is to figure out what I was doing with pppoe 610 611 Wed Feb 15 22:59:56 GMT 2023 612 613 docs ... 614 615 * introduction 616 617 * user guide 618 ** how to build it 619 ** how to flash it on your device 620 ** what to put in configuration.nix 621 ** modules 622 623 * developer guide 624 ** building/running with qemu 625 *** emulated upstream 626 ** building/running on hardware 627 *** run in place with TFTP 628 *** emulated upstream 629 ** CI 630 ** Roadmap 631 ** Contributing 632 633 634 635 nix-shell -p sphinx --run "make -C doc html" 636 637 https://francis.begyn.be/blog/nixos-home-router contains information about avahi reflector 638 639 640 Fri Feb 17 00:09:34 GMT 2023 641 642 29 11.282085831 81.187.76.242 → 8.8.8.8 ICMP 106 Echo (ping) request id=0x0187, seq=2/512, 4 643 30 11.286314642 90.155.53.19 → 81.187.76.242 ICMP 78 Destination unreachable (Communication admin) 644 645 We're getting packets over the pppoe-l2tp relay thing. Just have to 646 work out now why we're not routing 647 648 Fri Feb 17 16:54:41 GMT 2023 649 650 Haha. We weren't routing because we'd used the wrong CHAP password 651 652 653 654 Fri Feb 17 16:58:27 GMT 2023 655 656 This TODO is for nlnet task 1 and for bits of subsequent tasks that 657 are annoying enough that I might poke at them anyway: 658 659 660 1) gl-ar750, why do we get "ag71xx 19000000.eth: invalid MAC address, using random address" 661 2) gl-ar750, wifi 662 3) document services so I can remember how they work. Refer back to Oct 18 for notes that no longer make sense 663 4) check out restart behaviour of dependent services when depended-on service dies 664 5) pppd _creates_ an interface, work out how to fit it into this model 665 6) add bridge support for lan 666 7) upgrade ppp to something with an ipv6-up-script option, move ppp and pppoe derivations into their own files 667 8) get ipv6 address from pppoe 668 9) get ipv6 delegation from pppoe and add prefix to lan 669 10) support dhcp6 in dnsmasq, and advertise prefix on lan 670 11) firewalling and nat - default deny or zero trust? 671 13) should we check that references to outputs actually correspond with 672 14) make a flake? 673 15) see if there are other tests that need adding to CI 674 15a) is bordervm derivation tested? 675 18) gl-mt300a 676 19) gl-mt300n-v2 677 20) publish the manual using CI 678 679 12) write secrets holder as a service with outputs 680 16) writable filesystem (ubifs?) 681 17) overlay with squashfs/ubifs - useful? think about workflows for how this thing is installed 682 683 684 I could plug tninkpad into the gl-ar750 LAN port to dogfood the wired 685 networking 686 687 Sat Feb 18 14:26:45 GMT 2023 688 689 Apparently we're not currently doing anything special with busybox, 690 just using the default nixos build with the default applets. 691 692 We'd like to be able to say in modules which applets they need, 693 so that we build all necessary applets but don't waste any space. 694 But we don't want to build a busybox for each module because that 695 would be a big waste of space. 696 697 One option: 698 - add busybox configuration to `config` so that modules can maul it 699 - add a busybox module that builds it with union of all config and 700 adds link in /bin 701 - make everything else look in /bin instead of referencing pkgs.busybox 702 703 It would be good if services could assert somehow that their required 704 config is present 705 706 Sat Feb 18 23:45:13 GMT 2023 707 708 # lsmod 709 710 cd /lib/modules/mac80211 711 insmod ./compat/compat.ko 712 insmod ./net/wireless/cfg80211.ko 713 insmod ./net/mac80211/mac80211.ko 714 insmod ./drivers/net/wireless/ath/ath.ko 715 insmod ./drivers/net/wireless/ath/ath9k/ath9k_hw.ko 716 insmod ./drivers/net/wireless/ath/ath9k/ath9k_common.ko 717 insmod ./drivers/net/wireless/ath/ath9k/ath9k.ko 718 insmod ./drivers/net/wireless/ath/ath10k/ath10k_core.ko 719 insmod ./drivers/net/wireless/ath/ath10k/ath10k_pci.ko 720 721 [21.344930] ath9k 18100000.wmac: failed to load calibration data from mtd device 722 [21.352728] ath: phy0: parsing configuration from OF node 723 [21.362576] ath: phy0: serialize_regmode is 0 724 [21.367092] ath: phy0: UNDEFINED -> AWAKE 725 [21.372051] ath: phy0: Trying EEPROM access at Address 0x03ff 726 [21.377999] ath: phy0: Trying EEPROM access at Address 0x0fff 727 [21.383940] ath: phy0: Trying EEPROM access at Address 0x01ff 728 [21.389879] ath: phy0: Trying OTP access at Address 0x03ff 729 [21.400396] Data bus error, epc == 8027964c, ra == 83125880 730 [21.406156] Oops[#1]: 731 732 733 Sun Feb 19 18:15:27 GMT 2023 734 735 We have ath9k listening for packets. To make this ready to use: 736 737 - need to load the modules 738 - enable bridging lan with wlan 739 - packet forwarding 740 - firewall 741 742 743 Mon Feb 20 20:41:17 GMT 2023 744 745 need to fix all the other broken ci jobs :-( 746 747 The wlan test is failing because we moved mac80211 to a module and 748 there's nothing running to insmod it 749 750 Wed Feb 22 18:17:17 GMT 2023 751 752 bridge is e2b3738d0f8c3f2fd76ebcef65612de502a7b121 but it's the wrong 753 way around: the master interface needs to be up whether or not all 754 of its children are, so members depend on master not vice versa 755 756 Next steps: 757 - re-implement bridge, enable bridging lan with wlan 758 - packet forwarding 759 - firewall 760 - ath10k 761 - ipv6 762 763 Fri Feb 24 23:37:56 GMT 2023 764 765 bridging wlan was made complex because can't add a device to a bridge 766 until it's operational, and wlan0 is not operational until hostapd 767 has churned awhile. Therefore, "waitup" listens for netlink messages 768 and notifies s6 readiness stuff 769 770 we have a firewall nft script but we're not running it on boot 771 772 we have forwarding but no dns, maybe because we haven't told 773 dnsmasq about any upstream servers 774 775 Sun Feb 26 21:08:47 GMT 2023 776 777 to add firmware we need to put files in /lib/firmware, which means 778 a module 779 780 i guess we should do that in the device module 781 782 we can create the firmware files as packages 783 784 785 for the cal data we would like to get it from the device MTD "art" 786 partition at 787 boot time. 788 789 f 790 ====from openwrt 791 792 793 794 case "$FIRMWARE" in 795 "ath10k/cal-pci-0000:00:00.0.bin") 796 case $board in 797 allnet,all-wap02860ac|\ 798 araknis,an-500-ap-i-ac|\ 799 araknis,an-700-ap-i-ac|\ 800 engenius,eap1200h|\ 801 engenius,enstationac-v1|\ 802 glinet,gl-x750|\ 803 watchguard,ap300) 804 caldata_extract "art" 0x5000 0x844 805 ath10k_patch_mac $(macaddr_add $(mtd_get_mac_binary art 0x0) 2) 806 807 808 809 810 caldata_extract part offset count 811 caldata_dd $mtd /lib/firmware/$FIRMWARE $count $offset || \ 812 caldata_die "failed to extract calibration data from $mtd" 813 dd if=$source of=$target iflag=skip_bytes,fullblock bs=$count skip=$offset count=1 2>/dev/null 814 815 ======= 816 817 part=$(basename $(dirname $(grep -l art /sys/class/mtd/*/name))) 818 dd if=/dev/$part \ 819 of=/run/cal-pci-0000:00:00.0.bin iflag=skip_bytes,fullblock \ 820 bs=0x844 skip=0x5000 count=1 821 822 Mon Feb 27 22:46:37 GMT 2023 823 824 Found and fixed a bunchg of things that were stopping ath10k from 825 working. The remaining problem is (I think) that insmod is not 826 synchronous, so "ip link set up dev wlan1" doesn't work immediately 827 after the module is inserted. Maybe we need another netlink thing 828 to wait until the interface is present. 829 830 831 Wed Mar 1 18:26:44 GMT 2023 832 833 ath10k works, but the wlan module loading stuff is quite kludgey 834 835 I wonder if wlan0, wlan1, eth0, eth1 etc should be defined per-device 836 - how does the aplication config know which devices exist? If we 837 decide to switch to some form of persistent device naming, the names 838 will differ from one device to the next. Perhaps the device should 839 also provide standard names where possible? 840 841 services.network.links = { 842 lan = interface { ... }; 843 wan = interface { ... }; 844 wlan_24 = interface { ... }; 845 wlan_5 = interface { ... }; 846 } 847 848 Thu Mar 2 22:45:11 GMT 2023 849 850 We have a flashable image! 851 852 Now we can use the gl-ar750 for internet access in the shed, we can 853 apppropriate the other device that's in there and try Liminix on it 854 855 Fri Mar 3 23:08:58 GMT 2023 856 857 If we're going to unplug serial console from the gl-ar750 maybe we 858 should install an ssh server first. 859 860 0) set a root password 861 1) allow setting a root password from configuration.nix 862 (means defining config.users properly) 863 2) allow authorizedKeys per user 864 3) dropbear service 865 4) see if the wired lan works! :-) 866 867 868 Sat Mar 4 12:31:07 GMT 2023 869 870 To improve logging, each service should have its own s6-log service 871 which prefixes the service name onto the log line and then sends to 872 stdout 873 874 https://skarnet.org/software/s6/servicedir.html 875 https://skarnet.org/software/s6/s6-log.html 876 877 As far as I can tell, the `log` directory inside the service 878 directory should itself be a service directory for the s6-log 879 process that does this 880 881 .... hahaha no that doesn't work 882 883 s6-rc, for some reason, ignores the `log` directory and requires 884 that loggers be done with consumer-for and producer-for instead 885 886 887 Sat Mar 4 23:27:00 GMT 2023 888 889 notes for this week's news update 890 891 * ath10k kernel support and and firmware 892 893 - 5GHz wifi works 894 895 - need to retrieve the firmware from a special - partition on the 896 device itself, so we do that using a service that - the wlan 897 interface depends on 898 899 * replace waitup with more generally useful ifwait 900 901 to make the ath10k load at boot, we need to insert the module and then 902 wait for it to do something or other in the background before we can 903 configure the interface. so we need something like waitup but 904 for presence not operational state 905 906 it turns out that a program that just waits for a particular interface 907 state and then exits is quite simple to add into run scripts and 908 we don't need all that notification-fd stuff anyway 909 910 * move FW_LOADER* config to modules/base 911 912 * rejig config a bit. 913 - device hardware characteristics are now under 914 the `hardware` key and include the available network interfaces. 915 - options for users and groups are now defined a bit more 916 specifically than "attrset", making it possible to e.g. set a 917 root password 918 - dts is moved from `boot` to `hardware` 919 920 921 * now producing flashable images, so you can generate a liminix config 922 and write it to the device instead of having to boot using TFTP and 923 a serial console every time 924 925 * ssh support 926 927 * prefix logs with the service name 928 929 Sun Mar 5 22:51:21 GMT 2023 930 931 Added swconfig: it was a straight copy from nixwrt and hasn't changed 932 upstream since. But don't need it, because the lan port works fine 933 without it (I assume both lan ports and the cpu are all connected 934 untagged) 935 936 Mon Mar 6 09:42:33 GMT 2023 937 938 Today I plugged in the mt300a. 939 940 echo 17 >/sys/class/gpio/export 941 echo out >/sys/class/gpio/gpio17/direction 942 943 944 why are our images getting big 945 946 - lua links ncurses 947 - hostapd links openssl and sqlite 948 - nftables needs 949 - iptables? 950 - jansson? what is that? 951 - libedit/readline 952 - ifwait needs bash 953 954 955 File: result/squashfs 956 Size: 10371072 Blocks: 20256 IO Block: 4096 regular file 957 958 with smaller nftables: 9617408 Blocks: 18784 959 960 hostapd wqithout sqlite 9003008 Blocks: 17584 961 962 without bash: 8622080 Blocks: 16840 IO Block: 4096 regular file 963 964 without lua readline: bigger?! 8769536 Blocks: 17128 IO Block: 4096 regular file 965 966 967 Mon Mar 6 20:57:49 GMT 2023 968 969 [ 0.539992] mtk_soc_eth 10100000.ethernet: mdio-bus disabled 970 [ 10.493918] platform regulatory.0: Direct firmware load for regulatory.db fail 971 ed with error -2 972 [ 10.502828] cfg80211: failed to load regulatory.db 973 974 Check in morning, but whichever port the ethernet cable is plugged into, 975 is considered by the kernel as port 0 - which I think we should treat as 976 WAN 977 978 VLAN 1: 979 vid: 1 980 ports: 1 2 3 4 5 6t 981 VLAN 2: 982 vid: 2 983 ports: 0 6t 984 985 ip link add link eth0 name lan type vlan id 1 986 ip link add link eth0 name wan type vlan id 2 987 988 figure out how to add these to gl-mt300a device config 989 then extedner.nix can add a bridge 990 991 Tue Mar 7 20:13:15 GMT 2023 992 993 We need NTP or some other way to get accurate time 994 995 [done] Need to add regulatory.db somewhere standard, maybe modules/wlan? 996 997 Tue Mar 7 21:43:56 GMT 2023 998 999 When we get to phase 2, need to review how network interfaces and 1000 their addresses interplay. It should be possible to have a network 1001 interface and interrogate the addresses associated with it - esp 1002 with ipv6 where there are multiple addresses for the device 1003 1004 This thought prompted by looking at the loopback interface, which is 1005 a bundle of addresses and therefore we can't see what any of them are 1006 1007 1008 Tue Mar 7 22:05:44 GMT 2023 1009 1010 [phase 1] 1011 20) publish the manual using CI 1012 30) document flashing process 1013 31) go through all the unexpected dmesg and triage it 1014 25) ntp or some other accurate time source 1015 1016 [phase 1.5] 1017 26) ssh keys 1018 8) get ipv6 address from pppoe 1019 9) get ipv6 delegation from pppoe and add prefix to lan 1020 10) support dhcp6 in dnsmasq, and advertise prefix on lan 1021 11) firewalling and nat - default deny or zero trust? 1022 7) upgrade ppp to something with an ipv6-up-script option, move ppp and pppoe derivations into their own files 1023 32) set up iperf and do some performance measurement 1024 35) also we need to check our wireless country code 1025 1026 1027 [phase 2] 1028 3) document services so I can remember how they work. Refer back to Oct 18 for notes that no longer make sense 1029 4) check out restart behaviour of dependent services when depended-on service dies 1030 13) check that references to outputs correspond with declared outputs 1031 33) network interfaces vs the services that manage their addresses 1032 34) write a short guide explaining how to use s6-svc 1033 1034 [phase n] 1035 12) write secrets holder as a service with outputs 1036 16) writable filesystem (ubifs?) 1037 17) overlay with squashfs/ubifs - useful? think about workflows for how this thing is installed 1038 1039 1040 dmesg lines to investigate for gl-mt300a: 1041 [ 0.467314] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partition 1042 [ 0.539709] mtk_soc_eth 10100000.ethernet: mdio-bus disabled ? 1043 [ 8.778513] compat: loading out-of-tree module taints kernel. 1044 [ 17.686561] ieee80211 phy0: rt2800_wait_bbp_rf_ready: Error - BBP/RF register access failed, aborting 1045 [ 17.696025] ieee80211 phy0: rt2800_loft_iq_calibration: Warning - RF RX busy in LOFT IQ calibration 1046 [ 17.875147] ieee80211 phy0: rt2800_rxiq_calibration: Warning - Timeout waiting for MAC status in RXIQ calibration 1047 1048 for gl-ar750: 1049 [ 0.000000] Unknown kernel command line parameters "earlyprintk=serial,ttyS0", will be passed to user space. 1050 [ 0.416679] OF: Bad cell count for /ahb/spi@1f000000/flash@0/partitions 1051 [ 0.825495] ag71xx 19000000.eth: Could not connect to PHY device. Deferring probe. 1052 [ 1.632700] pci_bus 0000:00: root bus resource [mem 0x10000000-0x13ffffff] 1053 [ 1.639824] pci_bus 0000:00: root bus resource [io 0x0000] 1054 [ 1.645601] pci_bus 0000:00: No busn resource found for root bus, will use [bus 00-ff] 1055 [ 32.032326] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware 1056 [ 36.627844] ath10k_pci 0000:00:00.0: failed to receive initialized event from target: 00000000 1057 1058 1059 Fri Mar 10 13:17:56 GMT 2023 1060 1061 Lunchtime notes on images for real devices, vs ci.nix 1062 1063 * successfully building an image doesn't mean that the image boots 1064 or does anything useful 1065 1066 * don't want to faff with serial wires on every device every time 1067 to test it. so 1068 1069 * ideally, build ram-based images of rotuer, extneder, arhcive in CI 1070 with a watchdog timer that will reboot if it can't see the network 1071 1072 * figure out how to boot into the new image from an ssh connection. I 1073 assume the challenging bit here is grabbing x MB of contiguous phys 1074 mem after boot: I think we'd have to reserve it at _first_ boot and 1075 then somehow copy into it before rebooting 1076 1077 An easier first goal might be a tool to flash from the shell command line, 1078 but that runs a greater risk of bricking 1079 1080 1081 Fri Mar 10 14:35:40 GMT 2023 1082 1083 programs.busybox = { 1084 enable = true; 1085 applets = [... ]; 1086 config = { 1087 }; 1088 } 1089 1090 Fri Mar 10 23:49:04 GMT 2023 1091 1092 Well, we have the backup host config up and running - though haven't 1093 plugged it back into its disk yet. 1094 1095 For task 1 what remains is 1096 1097 1) ntp sync 1098 2) write up the flashing procedure 1099 3) a video? 1100 1101 1102 Sat Mar 11 13:58:20 GMT 2023 1103 1104 ================== for video 1105 1106 what is liminix 1107 - nix-based system for creating OS images for routers 1108 - not "nixos on your router" 1109 - nixos-like module system, 1110 - musl for libc 1111 - s6/s6-rc for services 1112 - entirely cross-compiled 1113 1114 why am i making a video? 1115 - unless you have a suitable spare device to install on, 1116 and you want to take it apart, it's currently hard to 1117 take liminix for a spin 1118 - I have these things, so I can give you a tour 1119 1120 let's have a look at how the hardware's hooked up 1121 1122 web site & manual 1123 1124 a config file: 1125 - observe the comments: 1126 - not going to spend ages on this because it's not in its final form. 1127 - as we get more configs for more use cases, we will 1128 get a better feel for what can be abstracted 1129 - that will come later: work so far has been on the 1130 hardware support side 1131 1132 to show that it builds, we're going to add a package. otherwise, 1133 everything from this build is probably already cached 1134 1135 build the config 1136 1137 tftpboot on hardware 1138 1139 flash on hardware 1140 1141 show ci 1142 1143 show a qemu target 1144 1145 Mon Mar 13 22:46:46 GMT 2023 1146 1147 1) rsync on arhcive is failing because no nogroup group 1148 1149 "/nix/store/gfzzl157r8xyp38lpcfxydkiiy6zrs3c-rsync-3.2.6/bin/rsync" "--verbose" "--stats" "--password-file" "/etc/nixos/secrets/arhcive-rsync" "-rltgoDz" "/var/spool/backup" "backup@arhcive.lan::srv/" 1150 @ERROR: invalid gid nogroup 1151 rsync error: error starting client-server protocol (code 5) at main.c(1837) [sender=3.2.6] 1152 1153 2) we can run findfs in a loop until the disk appears 1154 1155 3) still haven't decided how to do ntp but maybe we should just use 1156 the busybox one 1157 1158 4) some way to do upgrades over the wire 1159 1160 - boot with reserved mem and a phram device at 110-128MB even in the 1161 flashable version 1162 1163 - watchdog timer in kernel 1164 1165 - kexec in kernel 1166 1167 - userland service to feed the dog as long as local network is up 1168 (may need to start it a couple of minutes after boot, how do we 1169 do that?) 1170 1171 - can we use flashcp on a phram mtd? 1172 1173 5) maybe setup a vhost for hydra or something 1174 1175 [nix-shell:~/t]$ wget --reject-regex '\?' -D localhost -N -r --exclude-directories=/api --level=2 --convert-links -e robots=off http://localhost:3003/jobset/liminix/build/ 1176 1177 is almost a mirror 1178 1179 Tue Mar 14 20:17:35 GMT 2023 1180 1181 - do we have a phram mtd? need config for size and location 1182 1183 - how do we set the boot device 1184 - for first boot need to boot real flash, use dtb, ignore bootloader args 1185 - for kexec, boot phram, specify args somehow (could rewrite dtb) 1186 1187 => can use same kernel for both if we can give kexec a dtb with 1188 different params, which seems to be possible 1189 1190 so we need a module for the initial kernel to say 1191 - create phram mtd 1192 - boot from real mtd (will be index + 1) 1193 - enable KEXEC in kernel 1194 - add kexec-tools 1195 1196 and for the kernel we boot into 1197 - most of the above 1198 - except for the boot device 1199 - create an output with objects that kexec(8) can parse 1200 1201 Could be same module for both with different outputs 1202 1203 what do we call this thing? "revertable" 1204 1205 Wed Mar 15 19:11:09 GMT 2023 1206 1207 "revertable" implies mtd support for the rootfs and a ramdisk at 1208 defined location 1209 1210 "tftpboot" implies "revertable", because it will use the same ramdisk 1211 1212 1213 Fri Mar 17 11:44:40 GMT 2023 1214 1215 - patch the kernel kexec code to pass DTB to new kernel unconditionally 1216 - unpatch pernel to pass command line to kexec (breaks DTB passing) 1217 - decide how we specify rootfs. doing it by number is awkward 1218 - may be phram 1219 - may be real mtd root 1220 - may be real mtd root but renumbered becuase phram exists 1221 1222 Wild idea: we could probably get rid of the need for declaring a phram 1223 device in the first kernel, if we can use kexec to copy the squashfs into 1224 physical ram. As far as I can see this is a simple(sic) matter of 1225 specifying it as a segment, but we would have to extend kexec-tools 1226 to do this and it's quite a niche option if we make it do all the 1227 mtd setup. 1228 1229 kexec --dtb=foo.dtb --map-file=squashfs@0x120000 1230 1231 1232 Sat Mar 18 18:02:26 GMT 2023 1233 What if: we added derivations for "apply openwrt changes" as packages, 1234 which could then be called from the kernel derivation's extraPatchPhase? 1235 There could be one for generic and one for each openwrt targetop 1236 1237 Mon Mar 20 18:40:53 GMT 2023 1238 1239 - kexec patch is sent to mailing list, keep an eye for replies 1240 - watchdog 1241 - ntp 1242 - rebuild images for live devices 1243 - can we build a static busybox with flashcp applet and scp it 1244 to arhcive etc? 1245 - [DONE] install mailman and hyperkitty on myhtic, create mailing list 1246 1247 Tue Mar 21 22:59:54 GMT 2023 1248 1249 I haven't found a way to arm the watchdog before userland runs, which 1250 would be really nice: although there's WATCHDOG_HANDLE_BOOT_ENABLED 1251 and WATCHDOG_OPEN_TIMEOUT it doesn't seem to be sufficient, Maybe 1252 those options work only when the hardware watchdog is already 1253 armed. It might not be completely awful insofar as any failure to 1254 mount root usually results in panic anyway, so provided we start 1255 watching early in boot then there's not a big window for anything 1256 to go wrong 1257 1258 What should the watchdog service do? Ideally we want something that 1259 "ratchets" : can be started in early boot and signals health as long 1260 as the system is starting up, then once the system is in "steady 1261 state" it stops pinging as soon as any part of that steady state 1262 becomes unhealthy. This feels like a refinement for a much later 1263 phase though. 1264 1265 Maybe the health criteria might be 1266 (sshd and lan services are running) or (time since boot < 120s) 1267 1268 Thu Mar 23 00:11:23 GMT 2023 1269 1270 tftpboot and (kexecboot || flashable) have incompatible DTB-finding 1271 strategies which is painful if you add both modules and then 1272 expect tftp booting to still work 1273 1274 Maybe we could patch the kernel to use some better strategy for when 1275 to use/ignore the bootloader command line: e.g "only if it 1276 contains the string 'liminix'". Could do this by patching 1277 arch/mips/kernel/setup.c bootcmdline_init to 1278 1279 if(strstr(arcs_cmdline, "liminix") == NULL) arcs_cmdline[0] = '\0' 1280 and then defining CONFIG_MIPS_CMDLINE_DTB_EXTEND. The 1281 bootloader command line then needs to specify only the 1282 _additional_ parameters that weren't in the DTB 1283 1284 (later: that turned out to be quite straighforward) 1285 1286 Fri Mar 24 23:45:12 GMT 2023 1287 1288 - add ntp support 1289 - [DONE] expose hydra to internet 1290 - check MAC address weirdness? 1291 - call Task 1 "done" 1292 1293 Sun Mar 26 00:19:15 GMT 2023 1294 1295 Would be nice to have a flash.sh built in outputs.flashable 1296 1297 Sun Mar 26 15:27:14 BST 2023 1298 1299 Let's think about services and modules. 1300 1301 Module 1302 + can change global config 1303 * add users, groups etc 1304 * change kernel config 1305 * change busybox config 1306 + well-typed parameters 1307 - is a "singleton": can't have the same module included twice 1308 with different config. e.g. can't have two hostap modules running on 1309 different wlan radios. 1310 - can't express dependencies: a depends on b 1311 1312 suppose: 1313 1314 * modules add service functions to the config? then there's no 1315 way to define a service while forgetting to import the module 1316 1317 * we use the lib.types stuff for service function arguments 1318 1319 * maybe we stop naming services.foo for every damn thing 1320 1321 * but remember, s6 services do need unique names 1322 1323 1324 imports = [ ../modules/dhcp4 ]; 1325 1326 services.dhcp4 = config.services.udhcp { 1327 interface = lan.device; 1328 options = { 1329 foo = true; 1330 bar = 42; 1331 }; 1332 depends = [ services.some_other_thing ]; 1333 } 1334 1335 modules/dhcp4 udhcp fn needs to define a type for its argument, then 1336 use something like 1337 1338 if arg_type.check def.value then res 1339 else throw "The option value `${showOption loc}' in `${def.file}' is not a ${arg_type.name}.") 1340 1341 (where def comes from I don't know yet) 1342 1343 Tue Mar 28 10:44:40 BST 2023 1344 1345 we should reserve the name "service" for actual instantiated 1346 services. This means we need a name for the functions that 1347 make services. "class", "template", "fn", "maker", "factory"? 1348 And a namespace name so they're not interleaved with real 1349 services, which sort of suggests they are packages 1350 1351 if we want to do services = { 1352 foo = longrun { ... }; 1353 bar = longrun { ... }; 1354 } 1355 1356 without repeating the `name` as an attribute of the longrun, 1357 then longrun can't return a derivation: it has to return some 1358 function that accepts `name` as a parameter. 1359 1360 where services.a depends on services.b, at the time its builder is run 1361 it needs to know what name s6-rc will use for service b 1362 1363 maybe an s6 service definition should be an attrset not a derivation. 1364 1365 maybe this is outside scope for phase 2 1366 1367 Tue Mar 28 13:22:06 BST 2023 1368 1369 Reading nixos/doc/manual/development/building-parts.chapter.md it 1370 suggests to me that we should rename config.outputs to 1371 config.system.outputs. The more general question here is whether it's 1372 good to be augmenting a variable called "config" with all this 1373 generated stuff that is patently not configuration - perhaps putting 1374 it under a "system" key will keep it all in one place 1375 1376 Tue Mar 28 13:32:30 BST 2023 1377 1378 how should we handle filesystem state? e.g. resolvconf service 1379 1380 if a service provides a file at a known global pathname, it can't be 1381 parametrised - it must be a singleton. 1382 1383 Tue Mar 28 20:25:20 BST 2023 1384 1385 wondering if we should swap phases 2 and 3. We can't really address 1386 modules without addressing services, which is phase +n, whereas we 1387 can tackle overlay/ubi whenever 1388 1389 nand flash may have bad blocks 1390 nor flash (supposedly) doesn't 1391 1392 ubi provides erase counts and bad block remapping on top of the mtd 1393 interface. this means we should avoid flashcp of a ubi image straight 1394 onto (nand) mtd as we will lose the erase counts and bad block information 1395 that UBI tracks. 1396 1397 overlayfs works on a filename basis, so might not be very effective : 1398 any change that results in a new store path will mean the entire package 1399 appears in two places. I think it's reasonable to offer squashfs or 1400 ubifs without overlay. 1401 1402 open questions: 1403 1404 1) if uboot doesn't support UBI, we can't boot a kernel on a ubifs 1405 so we need reserved space for the kernel. 1406 1407 - unless we add some padding after the kernel, every new kernel 1408 that's bigger than its predecessor will trash the start of the 1409 ubi space (and wipe out its erase count) 1410 - This suggests we should build more stuff as modules and less as 1411 compiled-in 1412 1413 2) once a device has had a ubi volume created on it, probably we want 1414 to use ubi-aware tools to update that volume in future instead of a 1415 whole new flash, because we wish to preserve erase counts. This means 1416 running ubiformat --image-file=foo.ubi on the device instead of flashcp 1417 1418 we can add a "ubi-flashable" output that creates a .ubi image and 1419 a flashcp image that wraps it, with instructions on which to use. 1420 1421 Fri Mar 31 22:13:54 BST 2023 1422 1423 Error: too small LEB size 3968, minimum is 15360 1424 1425 > This error means that you are trying to mount too small UBI 1426 volume. Probably because your flash is too small? Try to use JFFS2, 1427 then, because it suits small flashes better since it has much lower 1428 space overhead. Indeed, UBIFS stores much more indexing information on 1429 the flash media than JFFS2, so it has much higher overhead. Also, UBI 1430 has some overhead (see here). Thus, if you have a small flash device 1431 (e.g., about 64MiB), it makes sense to consider using JFFS2. 1432 1433 1434 Argh. Oh well, 1435 1436 Sat Apr 1 15:27:39 BST 2023 1437 1438 There's limited value in recreating pseudofiles for jffs2 because 1439 the system is writable - changes made to /bin, /dev etc in config.filesystem 1440 should take effect on a running system. 1441 1442 Can we take inspiration from https://grahamc.com/blog/erase-your-darlings/ ? 1443 1444 in early boot: 1445 mount ramfs on / 1446 mount the writeable filesystem on /persist/ 1447 bind mount /persist/nix on /nix 1448 run script to populate rootfs from pseudofiles 1449 1450 on a router, do we need _anything_ persistent that's outside the store? 1451 1452 - state for dhcp leases and stuff 1453 - secrets 1454 - maybe, files that the user has downloaded 1455 1456 this will probably require initramfs. if just use jffs2 as the rootfs and 1457 don't worry about /persist, we can skip that step. 1458 1459 1460 1461 1462 [ aside: I think we may be putting two busyboxes in the image: 1463 see modules/s6/default.nix s6-init-scripts has buildInputs = [busybox]; ] 1464 1465 Mon Apr 3 18:34:26 BST 2023 1466 1467 suppose 1468 - we boot the system with systemConfig=/nix/store/eeeeee-system 1469 - the early-init script runs /target/$systemConfig/create-root /target 1470 after mounting /target 1471 - then it runs chroot /target $systemConfig/bin/init "$@" 1472 1473 or maybe we could combine those steps? 1474 or maybe it doesn't matter too much ... 1475 1476 Thu Apr 6 21:25:41 BST 2023 1477 1478 what now? 1479 1480 - put a jffs2 onto some hardware device 1481 - what do we do with uboot? 1482 - should we pad the kernel? 1483 - maybe kernel module support would be good if we're making it 1484 hard to do kernel updates 1485 - try the nix-copy-closure thing and work out what else we don't know 1486 - [done] detect endian correctly 1487 1488 1489 to ask a different question, what else do we need to dogfood a router? 1490 1491 Sun Apr 9 10:06:08 BST 2023 1492 1493 - rename outputs.flashable to outputs.flashimage 1494 - rename modules/flashable to modules/flashable_ro 1495 - create outputs.flashable in modules/jffs2 1496 - rename modules/jffs2 to modules/flashable_rw 1497 - add enable config to both? 1498 - enable kernel module compilation 1499 1500 1501 Mon Apr 10 23:50:41 BST 2023 1502 1503 - initramfs parses /proc/cmdline to find root fs, might not play 1504 nice with defaulting 1505 1506 - how to build kernel modules 1507 1508 - look at closure size, is it this big because we've broken it 1509 or is jffs2 usually this much bigger than squashfs 1510 1511 - maybe squashfs with overlay might be better if we could 1512 ensure hardlinks? 1513 1514 - maybe there's something like overlayfs but content-addressable? 1515 1516 Sat Apr 15 18:57:46 BST 2023 1517 1518 for the same configuration: 1519 1520 -r--r--r-- 1 root root 6066176 Jan 1 1970 /nix/store/0x271rg45mcjjgbma9wi31h1yd109fpy-frob-squashfs 1521 -r--r--r-- 1 root root 12255232 Jan 1 1970 /nix/store/zx11adagcbzqsnqkyz5kgvr392vhlrpr-make-jffs2 1522 1523 may want to reconsider not using squashfs with overlay 1524 1525 Wed Apr 19 22:22:48 BST 2023 1526 1527 Where next? 1528 1529 Sun Apr 23 18:24:34 BST 2023 1530 1531 - we are down to ~ 11MB image for a barely functional (IPV4) router 1532 this is by avoiding all dependencies on openssl or gnutls 1533 - rotuer is not recognising when I set the hostname 1534 - I may have forgotten the root password :-( 1535 - why is hello world 70K unless hardeningDisable? 1536 1537 Fri Apr 28 20:51:52 BST 2023 1538 1539 To do nix-copy-closure we need nix-store, which is a symlink to nix, 1540 which is 1541 1542 -rwxr-xr-x 1 dan users 2.3M Apr 28 21:08 nix 1543 1544 (stripped). This is a lot bigger than, say, a simple script to 1545 loop through the closure of a derivation and copy only the store 1546 folders that don't exist already. 1547 1548 * we'd like to only transmit the packages that aren't already present 1549 1550 * we'd like to use a single ssh connection 1551 1552 1553 S: here is a list of package names 1554 C: these are the names of the packages I want 1555 S: here are the packages 1556 1557 while read $f ; do 1558 test -d $f || echo $f 1559 end 1560 1561 Tue May 2 21:53:08 BST 2023 1562 1563 1) we have a script that runs on the receiver, which 1564 1565 - accepts a list of store paths 1566 - prints the missing store paths 1567 - runs cpio -i < stdio 1568 1569 2) we need a script for the sender that 1570 1571 - refs=$(nix-store -q --references $1 && echo end) 1572 - opens ssh connection 1573 - print ssh $refs 1574 - needed= capture result until "end" received 1575 - find needed | cpio -o > ssh-connection 1576 - close connection 1577 1578 3) to have a reasonable hope of testing this we should do it with qemu. It would be nice 1579 if we could connect without faff to the qemu lan interface : either we do this by bringing up 1580 another qemu vm (preferably with the host store shared, otherwise it has to build a mips cross 1581 compiler/libc) or maybe we could do something unholy with ssh ProxyCommand 1582 1583 ssh -o ProxyCommand "socat - UDP4-DATAGRAM:230.0.0.1:1234,sourceport=1234,reuseaddr,ip-add-membership=230.0.0.1:127.0.0.1" 1584 1585 1586 1587 4) we haven't solved garbage collection, though I think "remove everything not in 1588 nix-path-registration" might be what's needed there 1589 1590 Wed May 3 22:01:19 BST 2023 1591 1592 Something weird is going on with qemu net device enumeration: when I 1593 run it interactively I'm getting the access network (mac ending :02) 1594 on eth0 and the lan (mac ending :01) on eth1, and if it's behaving the 1595 same in CI then how come any of the tests work? vanilla-confinguration.nix 1596 definitely assumes lan=eth0 1597 1598 By switching from -device virtio-net-pci to -device virtio-net then 1599 I get the desired behaviour back 1600 1601 Sat May 6 18:42:28 BST 2023 1602 1603 Next: 1604 1605 - package min-copy-closure 1606 - see if we can use it on some output to copy the whole system closure 1607 - post-copying symlink munging 1608 - try it on a real device, see if it works for config file updates 1609 - collect-garbage/delete-old-generation 1610 1611 1612 Sun May 7 23:03:03 BST 2023 1613 1614 Shortly after all the work to reduce system closure size last time, I 1615 tried adding the necessary packages to support nix-copy-closure and 1616 saw it start building a complete C++ system with Boost. My fears that 1617 this would lead to quite a large increase in the system size were, it 1618 turned out, entirely founded. 1619 1620 So I wrote my own - or at least, a quite minimal substitute. The core 1621 logic is simple - on the sender, we get the list of required packages, 1622 then we check for the existence of `/nix/store/eeeeeee-foo` for 1623 each of them on the target, and whatever's missing we send across the 1624 link using cpio. 1625 1626 It sounds simple, and it should be simple, and in retrospect it _was_ 1627 simple. Along the way I went on a bit of a Qemu networking tangent and 1628 learned quite a lot about the bash `coproc` command 1629 1630 Tue May 9 21:06:53 BST 2023 1631 1632 General direction of my thoughts: 1633 1634 - get a baseline working rotuer system 1635 - prove that min-copy-closure works with it 1636 - refactor the crap out of it 1637 - configurablise the bordervm usb ethernet setup 1638 - when we have a good idea of how/whether min-copy-closure *actually* 1639 works, declare "writeable filesystem" to be done 1640 - start to get more of a feel for how the services/config hang together 1641 1642 ? why does rotuer not have a hostname? 1643 1644 ? how can we get a device hooked up to rotuer's lan port that we can 1645 control remotely 1646 1647 Sun May 14 23:25:46 BST 2023 1648 1649 the outputs.systemConfiguration attribute builds a derivation 1650 containing a single file bin/activate 1651 1652 _Presumably_, copying its closure will copy all the things, as 1653 we already use it as the roots for jffs2 creation. However, there 1654 is also a symlink created from /init at jffs2 creation 1655 1656 Mon May 15 21:32:38 BST 2023 1657 1658 Had a neat idea about uing an overlayfs combining jffs2 and ramfs 1659 to do upgrades that would otherwise be larger than the flash. 1660 Could use "overlay merge" from https://github.com/kmxz/overlayfs-tools 1661 1662 Wed May 17 15:18:55 BST 2023 1663 1664 liminix-rebuild doesn't collect garbage (this is a mising feature, not 1665 a bug). We think we can fix this using nix-path-registration: specifically, 1666 by deleting anything not in it. 1667 1668 What we're going to do: build a fresh system image for rotuer, then 1669 dogfood liminix-rebuild until we've succeeded in getting it to 1670 change its hostname 1671 1672 Also wondering if we should drop outputs.default, but maybe not 1673 1674 * systemConfiguration: used for updates 1675 * vmroot: used for qemu 1676 * flashimage: used for flashing 1677 * tftproot: used for dev/test 1678 1679 As long as we're consistently setting the default output to whichever 1680 is the appropriate "full production image" I think we're good. 1681 1682 Wed May 17 22:45:40 BST 2023 1683 1684 Random thought: when we bind mount /target/persist/nix to /target/nix 1685 we could make it read-only. worth doing? 1686 1687 Thu May 18 10:59:39 BST 2023 1688 1689 - liminix-rebuild can't find reboot: probably the PATH is just 1690 generally wrong for ssh sessions (maybe all non-login sessions?) 1691 1692 - need to copy path registration file somewhere useful and 1693 delete stuff not in it at the appropriate time. Would be safest 1694 to do that either late in the shutdown process before rebooting, 1695 or during boot. 1696 1697 Fri May 19 15:18:13 BST 2023 1698 1699 If we make min-collect-garbage - just a command you can run whenever - 1700 that will be fine for current capabilities. It won't work with the 1701 theoretical overlayfs system, though: we need to copy-down from the 1702 ramfs to real flash before rebooting, and that can't happen until 1703 there's disk space to do it 1704 1705 Sat May 20 22:35:25 BST 2023 1706 1707 We have a working min-collect-garbage (seems to, anyway ...) 1708 1709 - having ssh host key wiped on reboot is sucky. maybe we can have 1710 /persist/secrets and a service that looks there? 1711 1712 - find out what files ash sources on non-login shell startup 1713 [ set ENV=/etc/ashrc in parent process env ] 1714 1715 - services.default is suboptimal as there is no way to add to it 1716 without wiping it 1717 1718 - decide whether to use liminix- or min- as our prefix for nixy 1719 commands 1720 1721 - should we move config.outputs -> config.system.outputs ? see Mar 28 1722 1723 - less crap firewall 1724 1725 - add ipv6 support to rotuer 1726 1727 - create an l2tp configuration 1728 1729 - iperf and tuning 1730 1731 - wlan country code 1732 1733 1734 dropbear weak hashes? https://github.com/mkj/dropbear/issues/138 1735 1736 Sun May 21 11:48:07 BST 2023 1737 1738 dropbear will generate host keys on first connection. It's (probably) good that the 1739 key is generated on-device and also that we wait until there's some randomness. 1740 It's not so good that it will only write the key to DSS_PRIV_FILENAME which is 1741 hardcoded to /run 1742 1743 Sun May 21 17:27:31 BST 2023 1744 1745 What do we need for ipv6? 1746 1747 - upgrade ppp to something with an ipv6-up-script option, move ppp and pppoe derivations into their own files 1748 - get ipv6 address from pppoe 1749 - get ipv6 delegation from DHCPv6 1750 - support dhcp6 in dnsmasq, and advertise prefix on lan 1751 - firewall settings 1752 1753 Sun May 21 21:30:17 BST 2023 1754 1755 Making hydra build the docs is straightforward, but making it 1756 _publish_ the docs is outside scope, really. It can serve the files 1757 but they're all text/plain 1758 1759 Should hydra push the docs to www.liminix.org or should www.liminix.org 1760 pull? 1761 1762 TODO-at-some-point: assign uids and gids dynamically, somehow 1763 1764 Tue May 23 22:56:33 BST 2023 1765 1766 following the guidance at https://support.aa.net.uk/IPv6: 1767 1768 we run odhcp6c to do router solicitation/advertisement dance 1769 1770 1771 odhcp6c environment variables: 1772 1773 RA_ADDRESSES= 1774 RA_REACHABLE=0 1775 CER= 1776 PASSTHRU=00170020200108b0000000000000000000002020200108b0000000000000000000002021 1777 SERVER=fe80::203:97ff:fed6:0 1778 RA_MTU=0 1779 RA_ROUTES=::/0,fe80::203:97ff:fed6:0,65535,512 1780 OPTION_1=00030001e4956e4ef2fa 1781 NTP_FQDN= 1782 OPTION_2=00030001000397d60000 1783 RA_DOMAINS= 1784 DOMAINS= 1785 AFTR= 1786 SIP_IP= 1787 NTP_IP= 1788 PREFIXES=2001:8b0:de3a:40dc::/64,7198,7198 1789 RA_HOPLIMIT=64 1790 RA_DNS= 1791 RDNSS=2001:8b0::2020 2001:8b0::2021 1792 SNTP_IP= 1793 RA_RETRANSMIT=0 1794 SIP_DOMAIN= 1795 ADDRESSES=2001:8b0:1111:1111:0:ffff:51bb:4cf2/128,3598,7198 1796 1797 # ip -6 route |grep default 1798 default via fe80::203:97ff:fed6:0 dev ppp0 metric 1024 expires 65211sec 1799 1800 presumably from RA_ROUTES but why is the metric appaently doubled? 1801 1802 Tue May 30 21:25:37 BST 2023 1803 1804 We have an odhcpc script that preserves the prefix delegation from the 1805 ISP. We need a service that notices whenever the state is 1806 available/has changed, and updates the LAN IPv6 address. 1807 1808 The service can depend on odhcp 1809 1810 add inotify to packages 1811 use writeFennelScript with that dep 1812 see if it works 1813 1814 Wed May 31 23:33:00 BST 2023 1815 1816 We have a thing that sets ipv6 address on lan interface, yay us 1817 1818 A firewall would be a very good idea 1819 1820 Thu Jun 1 18:46:59 BST 2023 1821 1822 TODO for now: 1823 1824 - services.default is suboptimal as there is no way to add to it 1825 without wiping it 1826 1827 - decide whether to use liminix- or min- as our prefix for nixy 1828 commands 1829 1830 - should we move config.outputs -> config.system.outputs ? see Mar 28 1831 1832 - less crap firewall 1833 1834 - create an l2tp configuration 1835 1836 - iperf and tuning 1837 1838 - wlan country code 1839 1840 Thu Jun 1 21:26:37 BST 2023 1841 1842 how can a client machine "opt out" of using the firewall, to allow 1843 incoming connections? Most convenient would be to have a separate SSID 1844 for grownups. Assuming it shows up as a separate wlan device, we can 1845 write firewall rules to allow incoming connections on that interface 1846 (can we? only if the packet is identifiable as destined for that interface) 1847 1848 https://www.rfc-editor.org/rfc/rfc6092.html 1849 https://emailstuff.org/rfc/rfc7084 1850 1851 We could block incoming for slaac and dhcp addresses and permit it for 1852 stable private addresses. If we were fairly sure that devices won't 1853 ask for stable private addresses just for funsies. 1854 1855 https://wiki.archlinux.org/title/IPv6_#Stable_private_addresses 1856 1857 1858 1859 Fri Jun 2 14:42:43 BST 2023 1860 1861 I found a handy guide to nftables at https://ww.telent.net/2023/6/2/turning_the_nftables 1862 1863 Mon Jun 5 16:56:44 BST 2023 1864 1865 1866 How are we going to do this firewall thing then? 1867 I can see no reason to have more than one table per family, so lets 1868 just name the tables after families 1869 1870 There is nothing in nftables for functionally grouping rules by 1871 requirement that may touch multiple hooks/chains, so we need our own 1872 abstraction - and we can't call it any name that nftables uses already 1873 (so, not "ruleset"). rulegroup? 1874 1875 "policy" would be a good name except that it's already taken 1876 1877 "concern"? "requirement"? "feature"? 1878 1879 Mon Jun 19 20:45:48 BST 2023 1880 1881 why is chrony using libedit? 1882 1883 Thu Jun 22 09:52:57 BST 2023 1884 1885 - There is a lot more lua being installed (luac, docs, static 1886 libraries etc) than we really need. 1887 1888 - update User docs to include a list of supported targets 1889 1890 Thu Jun 22 23:43:06 BST 2023 1891 1892 - is there a sysfs to enable ipv6 forwarding? 1893 - we haven't an ipv4 firewall yet 1894 1895 1896 PATH=`echo /nix/store/*nftables*/bin`:$PATH 1897 nft list ruleset 1898 1899 Thu Jun 22 23:58:58 BST 2023 1900 1901 Looks like we're missing at least one kernel config setting for 1902 nftables. Would this be a good time to do a derivation for building 1903 kernel modules? 1904 1905 1906 Sun Jul 9 21:21:17 BST 2023 1907 1908 1909 Tue Jul 11 22:10:17 BST 2023 1910 1911 - s6 cheatsheet, find or write 1912 - could we have > 1 module add to services.default? 1913 - odhcp should parse values from environ and write more files, to save readers 1914 from parsing it 1915 - pkgs.liminix, who knows what thats for any more? 1916 - interface.device, as a general rule, doesn't work because the 1917 device name may be known only at runtime (e.g. for ppp) 1918 - iperf 1919 - figure out wifi regdomain 1920 1921 Tue Jul 11 23:01:59 BST 2023 1922 1923 We can make services depend on kernel modules, however not on bakedin 1924 kernel config 1925 1926 [from March: "Let's think about services and modules."] 1927 1928 Module 1929 + can change global config 1930 * add users, groups etc 1931 * change kernel config 1932 * change busybox config 1933 + well-typed parameters 1934 - is a "singleton": can't have the same module included twice 1935 with different config. e.g. can't have two hostap modules running on 1936 different wlan radios. 1937 - can't express dependencies: a depends on b 1938 1939 thought I had then was: modules provide services. requiring the 1940 ppp module causes config.services.ppp to exist, so you can 1941 1942 services.default = [ 1943 (config.services.ppp { 1944 tty = "17"; 1945 baud = "57600"; 1946 secrets = blah; 1947 }) 1948 ... 1949 ] 1950 1951 this might work. also though we should find out how to do type checking on 1952 service params 1953 1954 Wed Jul 12 23:23:02 BST 2023 1955 1956 https://github.com/NixOS/mobile-nixos/pull/406 // why mobile nixos uses 1957 mobile.outputs instead of system.build 1958 1959 suggests that system.build may not be a thing to blindly emulate 1960 1961 if a service is a derivation should we expect to want to be able to call 1962 it with .override? maybe we want to override the package containing the 1963 daemon it runs. How do we best pass service config as well? 1964 1965 Maybe a service template is a function that returns a derivation 1966 1967 1968 imports = [ ./modules/pqmud.nix ]; 1969 services.mud = system.services.pqmud { 1970 realm = "A deep cavern"; 1971 port = 4067; 1972 users = import ./allowed-users.nix; 1973 # etc etc 1974 }; 1975 services.mudBeta = let mud = 1976 system.services.pqmud { 1977 realm = "A very deep cavern"; 1978 port = 4068; 1979 users = import ./allowed-users.nix; 1980 }; 1981 in mud.overrideAttrs { pqmud = pkgs.pkmudLatest; }; 1982 1983 so we have 1984 1985 config.system.services # services provided by modules 1986 config.system.outputs # build artefacts of various types 1987 1988 the services provided by a module must be introspectable in some way 1989 so that we can compile a list of service options per module 1990 1991 service parameters are defined using the module type system. 1992 Something like this? 1993 1994 # mud.nix 1995 1996 system.services.pqmud = args : 1997 let t = { 1998 name = mkOption { type = types.str; }; 1999 realm = mkOption { type = types.str; }; 2000 port = mkOption { type = types.port; default = 12345; }; 2001 users = mkOption { type = types.any; }; 2002 }; 2003 in assert isType (submodule { options = t; }) args; longrun { 2004 inherit (args) name; 2005 run = "${pkgs.pqmud}/bin/pqmud --port ${port} ....." 2006 }; 2007 2008 Fri Jul 14 19:07:59 BST 2023 2009 2010 It works for pppoe, though typechecking error messages could be 2011 better. 2012 2013 - We need to find a good place to keep the typeCheck function so that 2014 everyone can use it. 2015 2016 - also the type_service type defn exists only locally in modules.nix, 2017 and we would like to refer to it elsewhere 2018 2019 Thu Aug 10 21:46:36 BST 2023 2020 2021 to finish service/modules milestone 2022 2023 [done] there are some modules not using serviceDefn 2024 - modules included by standard.nix should have all their options 2025 grouped together in docs 2026 how can we determine which they are? or maybe "modules that 2027 don't contain services" is an acceptable criterion 2028 maybe this is not actually an issue, if the modules are 2029 reasonably coherent. It looks odd now because base.nix is a mess 2030 [done] print the module pathname so people know what to import 2031 [done] docs don't print the examples 2032 [check?] and seem to be getting the default wrong too 2033 - decide what we deem to be "internal" (if anything) 2034 is `filesystem` internal, for example? or `busybox`? they're 2035 both mostly _used_ internally but may still be valuable to expose 2036 [done] maybe document outputs separately or not at all? 2037 [done] bridge to be one service instead of two? 2038 [done] get rid of services/ 2039 - anything else in rotuer.nix that we should servicify 2040 - services for liminix.networking 2041 - a nice way to specify service dependencies 2042 [done] - do another video 2043 2044 Mon Aug 21 20:02:55 BST 2023 2045 2046 a nice way to do dependencies would be somethng like 2047 2048 services.thething = 2049 let s = svc.thing { .... }; 2050 in addDependencies s (with config.services; [otherthing yetanother]); 2051 2052 except that addDependencies is a really klunky name. dependsOn is very 2053 slightly better? or maybe it could be a function of the derivation? 2054 2055 services.thething = 2056 svc.thing { .... }.depends (with config.services; [otherthing yetanother]); 2057 2058 --- 2059 2060 what does it mean to be dependent on an interface? that's it up? running? 2061 has an address? has a collection of addresses? 2062 2063 2064 services.defaultroute4 = route { 2065 name = "defaultroute4"; 2066 via = "$(output ${services.wan} address)"; 2067 target = "default"; 2068 dependencies = [ services.wan ]; 2069 }; 2070 2071 - this route requires the interface to have an address (if wan is an 2072 interface, anyway ...) 2073 2074 - but otoh a dhcp client doesn't want to wait for an address, because 2075 it is assigning the address. 2076 2077 should an address provider have "interface name" as an output? 2078 is there a set of outputs that every address provider should have - 2079 whether static, dhcp, pppoe? 2080 2081 maybe we're in decision paralysis and should just move forward with 2082 what we know 2083 2084 Wed Aug 23 18:56:08 BST 2023 2085 2086 We may want to change the hardware device files to specify network 2087 interface names not services. Otherwise hardware devices (boards) 2088 depend on module-based-services, which is a bit weird. 2089 2090 Thu Aug 24 18:54:03 BST 2023 2091 2092 - we want network and bridge to be separate modules, because bridge 2093 introduces extra kernel config 2094 2095 - bridge/service wants to create a network device ("ip link"), 2096 using quite similar code as network/link.nix 2097 2098 - but bridge/service is a derivation: it has sight of pkgs but not 2099 config 2100 2101 offtopic: useful s6-rc commands at https://www.skarnet.org/software/s6-rc/faq.html 2102 2103 Fri Aug 25 23:37:57 BST 2023 2104 2105 where we left off: bridge is a bundle, and bundles can't have outputs, 2106 so how do we set the ifname of the bridge? 2107 2108 - ifname of the primary is set 2109 - actually, most things that depend on the bridge really just depend 2110 on the primary anyway (it's OK if 1 <= n < #members are down) 2111 - but *something* should depeond on all the members 2112 2113 turns out maybe we needed two services after all? 2114 2115 Sun Aug 27 23:50:18 BST 2023 2116 2117 I've done enough to make rotuer build, but in the process 2118 trashed vanilla-configuration as I entirely forgot we don't have 2119 a dhcpv4 client service. Need to fix that ... 2120 2121 - anything else in rotuer.nix that we should servicify 2122 - anything in vanilla-configuration ditto 2123 - and arhcive (rsync, watchdog) 2124 - services for liminix.networking 2125 - tidy up the dependency handling in serviceDefn build 2126 (interface is fine, implementation is a bit brutal) 2127 - write a blog entry 2128 2129 Mon Aug 28 16:58:49 BST 2023 2130 2131 - [done] ntp is not setting the time 2132 - nftables syntax error 2133 2134 Thu Aug 31 23:53:54 BST 2023 2135 2136 - anything else in rotuer.nix that we should servicify 2137 [done] - packet forwarding 2138 - dhcp6 client 2139 - what to do with acquire-{wan,lan} scripts? 2140 - resolvconf 2141 - [done] anything in vanilla-configuration ditto 2142 - packet forwarding 2143 - and arhcive 2144 - [not doing] rsync 2145 - [done] watchdog 2146 - [done] mount 2147 - nftables syntax error 2148 - tidy up the dependency handling in serviceDefn build 2149 (interface is fine, implementation is a bit brutal) 2150 - [done] services for liminix.networking 2151 - [done] write a blog entry 2152 - [done] ntp is not setting the time 2153 - [done] static dhcp(6) lease support reqd for dogfooding 2154 2155 Sat Sep 2 21:35:41 BST 2023 2156 2157 Considerations for "mount" service: each filesystem needs to depend on 2158 any mount points for its parent directories, and maybe also on other 2159 services (e.g. filesystem modules, network devices, routes) 2160 2161 mountpoints = { 2162 mnt = { 2163 media = svc.mountpoint.build { 2164 fstype = "msdos"; 2165 device = "/dev/sda1"; 2166 options = [ ...]; 2167 }; 2168 archive = svc.mountpoint.build { 2169 fstype = "ext4"; 2170 device = "/dev/sda2"; 2171 options = [ ...]; 2172 mountpoints = { 2173 remote = svc.mountpoint.build { 2174 fstype = "nfs"; 2175 device = "doc.ic.ac.uk:/public"; 2176 }; 2177 }; 2178 }; 2179 }; 2180 } 2181 2182 services.somethingelse = svc.ftpd.build { 2183 # ... 2184 dependencies = [ mountpoints.mnt.archive ]; 2185 } 2186 2187 what don't we like about this? we have to walk the nested attrset in a 2188 weird way, because the services may contain other mountpoints. Maybe 2189 just keep it simple and do 2190 2191 2192 services.mountpoints = bundle { 2193 name = "mountpoints"; 2194 contents = [ 2195 svc.mountpoint.build { 2196 device = "/dev/sda2"; fstype = "ext4"; directory = "/mnt/isos"; 2197 }; 2198 svc.mountpoint.build { 2199 device = "/dev/sdb1"; fstype = "msdos"; directory = "/mnt/backup"; 2200 dependencies = [ load-vfat-module ]; 2201 }; 2202 ]; 2203 } 2204 2205 Sun Sep 3 17:34:36 BST 2023 2206 2207 how to dogfood 2208 2209 DHCP6 server: static lease support 2210 DHCP client and acquire-{lan-prefix,wan-address} 2211 2212 The emergency boot thingy in glinet u-boot won't help because it 2213 expects to flash from its tftp request instead of booting it. So we 2214 could use kexec instead except that the openwrt install doesn't have 2215 it. So we could swap the hardware devices, the only downside of that 2216 being that then I don't have a test system any more. Or we could YOLO it. 2217 2218 Sun Sep 3 22:11:02 BST 2023 2219 2220 I think we should rejigger the documentation ... 2221 2222 - "getting started": worked example, building and installing Liminix 2223 with a very simple config (wifi AP with ssh daemon) 2224 2225 - using modules 2226 - link to module reference 2227 2228 - creating custom services 2229 - longrun or oneshot 2230 - dependencies 2231 - outputs 2232 2233 - creating your own modules 2234 2235 - hacking on Liminix itself 2236 2237 - contributing 2238 2239 - external links and resources 2240 2241 - module reference 2242 2243 - hardware device reference 2244 2245 --- 2246 2247 I think we might rename wlan_24 to wlan and wlan_5 to wlan1. 2248 This is on the assumption that almost no device is 5GHz only, so 2249 would make it easier to write a basic wlan example that works 2250 both on 2.4GHz boards and dual radio boards 2251 2252 Mon Sep 4 23:15:26 BST 2023 2253 2254 If dhcpcd parsed the update-script output into separate files, half 2255 the complexity of acquire-lan-prefix would go away. The other half is 2256 because it subscribes to changes in the outputs instead of just 2257 running once. Perhaps there's a better way to do that? 2258 2259 Could separate prefixes and addresses something like this... 2260 2261 outputs/prefix/2001\:8b0\:de3a\:40dc\:\:/prefix 2262 outputs/prefix/2001\:8b0\:de3a\:40dc\:\:/length 2263 outputs/prefix/2001\:8b0\:de3a\:40dc\:\:/preferred 2264 outputs/prefix/2001\:8b0\:de3a\:40dc\:\:/valid 2265 outputs/prefix/2001\:8b0\:de3a\:80\:\:/prefix 2266 outputs/prefix/2001\:8b0\:de3a\:80\:\:/length 2267 outputs/prefix/2001\:8b0\:de3a\:80\:\:/preferred 2268 outputs/prefix/2001\:8b0\:de3a\:80\:\:/valid 2269 2270 the directory name is arbitrary as long as it's unique. Might even be better to 2271 remove the colons 2272 2273 outputs/prefix/200108b0de3a40dc/valid 2274 2275 or we could adopt the MS convention and replace with hyphens 2276 2277 outputs/prefix/2001-8b0-de3a-40dc--/prefix 2278 2279 Also: we should write some kind of test for this... 2280 2281 2282 Tue Sep 5 21:36:39 BST 2023 2283 2284 How do we set the cpu governor? 2285 2286 2287 Fri Sep 8 21:26:36 BST 2023 2288 2289 We want a fennel thing that reads a filesystem tree into a nested 2290 table. And a thing to diff two tables 2291 2292 Sat Sep 9 22:40:50 BST 2023 2293 2294 Subscribers to odhcp6c outputs need to be able to tell which addresses 2295 are new and which have been removed since the last run, which now we 2296 have ohdcp-script producing parsed data means they need to compare 2297 tables by value. Which is a faff. 2298 2299 What if the directory name were a hash of all the relevant fields 2300 such that clients could just say "new directory, must be new address" 2301 2302 We can have literal prefix, then need to encode 2303 length,preferred,valid,extra space-efficiently. I cannot currently see 2304 any way to use whatever hashing Lua uses for its table lookups, 2305 which is a bit disapppointing, so we might have to make our own 2306 2307 2308 https://gist.github.com/scheler/26a942d34fb5576a68c111b05ac3fabe 2309 this is DJBHash, though doesn't appear to deal with integer overflow 2310 2311 2312 function hash(str) 2313 h = 5381; 2314 2315 for c in str:gmatch"." do 2316 h = ((h << 5) + h) + string.byte(c) 2317 end 2318 return h 2319 end 2320 2321 2322 Mon Sep 11 20:31:25 BST 2023 2323 2324 acquire-lan/wan-foo have no tests, and the test setup is a bit of a 2325 faff as they are both waiting on the filesystem 2326 2327 also, testing lua scripts is a faff without splitting them into 2328 script/module 2329 2330 am wondering if we could do some kind of convention that we only write 2331 modules not scripts but something in the fennel->lua can call the 2332 module's `run` method. 2333 2334 Tuesday 2335 2336 Here is a working shebang for write-fennel: 2337 2338 #!/nix/store/5iwv3h2jjbk2vib2bpwx3g9knpb02x3y-lua-5.3.6/bin/lua -e dofile(arg[0]).run() 2339 2340 Tue Sep 12 20:47:52 BST 2023 2341 2342 We don't handle unbound or stopped states in odhcp consumers. I think 2343 probably we should do this in odhcp-script by deleting the outputs, 2344 rather than making each consumer do it. 2345 2346 ... turns out that odhcp6c itself unsets ADDRESSES and PREFIXES before 2347 calling the script with "unbound", so maybe we don't need to do 2348 anything special. 2349 2350 Wed Sep 13 17:55:33 BST 2023 2351 2352 2353 2354 @400000000000001f2723b3cb eth1.link.pppoe Script /nix/store/nyks8zl86dcp44k5sjcc76digrnfgm17-ip-up finished (pid 403), status = 0x0 2355 @400000000000001f27b2db3b eth1.link.pppoe Script /nix/store/ds0lc4qd1zfiyxsva87rpplyr21awjh1-ip6-up finished (pid 404), status = 0x1 2356 2357 @400000000000001f30a7c5c5 /nix/store/v9ijgyywizqbbd9y73r2wifkxc0d1jjm-route-default-1a22c69d0e1f-up: line 4: input: not found 2358 @400000000000001f31abf9b5 ip: command line is not complete, try "help" 2359 @400000000000001f31ca1395 s6-rc: warning: unable to start service route-default-1a22c69d0e1f: command exited 1 2360 @400000000000001f31f236b4 s6-rc: info: service route-default-d2586cf00da0 successfully started 2361 @ 2362 2363 2364 Wed Sep 13 18:05:38 BST 2023 2365 2366 TODO 2367 2368 - service for dhcp6 client 2369 - move acquire-{wan,lan} scripts out of examples/ 2370 - service for resolvconf 2371 - nftables syntax error 2372 - tidy up the dependency handling in serviceDefn build 2373 (interface is fine, implementation is a bit brutal) 2374 - docs 2375 2376 considerations: 2377 2378 1) in some ways, we should be able to specify acquire-{wan,lan} as if 2379 they were just additional addresses on the respective 2380 interfaces. However, they're longruns so the implementation of 2381 "address" doesn't really fit. 2382 2383 2) should they be bundled into a dhcp client service? I think the 2384 answer is "no" because which of the dhcp config we want to 2385 honour locally (and how) is policy not mechainmsm 2386 2387 svc.dhcp6c.client.build { interface = wan; }; 2388 svc.dhcp6c.address.build { 2389 inherit client; 2390 interface = lan; 2391 }; 2392 svc.dhcp6c.address.build { 2393 inherit client; 2394 interface = wan; 2395 }; 2396 svc.dhcp6c.prefix.build { 2397 inherit client; 2398 interface = lan; 2399 index = 1; # default to first interface 2400 }; 2401 svc.dhcp6c.prefix.build { 2402 inherit client; 2403 interface = vpn; 2404 index = 2; 2405 }; 2406 2407 2408 2409 Fri Sep 15 12:04:25 BST 2023 2410 2411 Qemu worked example provides dhcp and ssh service 2412 2413 Hardware worked example needs to be plugged into same lan as build 2414 machine if we are going to tftp the image onto it - so it might be 2415 awkward if we run dhcp on it 2416 2417 The device I have lying around is the A 2418 2419 How do we do the actual flash step? Assuming the device is running 2420 stock firmware, from a laptop we can wifi to it and use the web ui to 2421 upgrade 2422 2423 we can't build the hellonet config because it requires tftp 2424 2425 plug in mt300a 2426 put stock firmware on it 2427 2428 Sun Sep 17 00:08:03 BST 2023 2429 2430 I don't think the user manual needs a full justification of why we 2431 have the module/service split. Maybe we should have "decision records" 2432 in the git tree instead 2433 2434 Sun Sep 17 16:44:31 BST 2023 2435 2436 Can we figure out which bits of the old doc are missing from the new 2437 one and just transplant those? Then we can merge it sooner 2438 instead of blocking on writig all the new stuff 2439 2440 Mon Sep 25 16:58:51 BST 2023 2441 2442 jffs2 on mt300a isn't finding root partition in initramfs, and it 2443 seems to be because MTD_SPLIT_UIMAGE_FW isn't working 2444 2445 [ 0.426792] spi spi0.0: force spi mode3 2446 [ 0.431305] spi-nor spi0.0: w25q128 (16384 Kbytes) 2447 [ 0.436322] 5 fixed-partitions partitions found on MTD device spi0.0 2448 [ 0.442875] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions 2449 [ 0.450400] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions 2450 [ 0.458208] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions 2451 [ 0.465751] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions 2452 [ 0.473522] Creating 5 MTD partitions on "spi0.0": 2453 [ 0.478466] 0x000000000000-0x000000030000 : "u-boot" 2454 [ 0.484447] 0x000000030000-0x000000040000 : "u-boot-env" 2455 [ 0.490888] 0x000000040000-0x000000050000 : "factory" 2456 [ 0.497110] 0x000000050000-0x000000fd0000 : "firmware" 2457 [ 0.596423] 0x000000ff0000-0x000001000000 : "art" 2458 [ 0.611508] gsw: setting port4 to ephy mode 2459 2460 with squashfs root it's the same but for the extra split partitions: 2461 2462 [ 0.468715] Creating 5 MTD partitions on "spi0.0": 2463 [ 0.473653] 0x000000000000-0x000000030000 : "u-boot" 2464 [ 0.479652] 0x000000030000-0x000000040000 : "u-boot-env" 2465 [ 0.486085] 0x000000040000-0x000000050000 : "factory" 2466 [ 0.492318] 0x000000050000-0x000000fd0000 : "firmware" 2467 [ 0.499304] 2 uimage-fw partitions found on MTD device firmware 2468 [ 0.505457] Creating 2 MTD partitions on "firmware": 2469 [ 0.510543] 0x000000000000-0x000000260000 : "kernel" 2470 [ 0.516616] 0x000000260000-0x000000f80000 : "rootfs" 2471 [ 0.522570] mtd: device 5 (rootfs) set to be root filesystem 2472 [ 0.528565] 0x000000ff0000-0x000001000000 : "art" 2473 2474 turns out this is because the device thinks it has 4k erase block size 2475 because MTD_SPI_NOR_USE_4K_SECTORS was set, and that was causing 2476 mtdsplit to look in the wrong place for a root filesystem 2477 2478 Mon Sep 25 18:50:05 BST 2023 2479 2480 No, that wasn't it. Turned out to be an endianness-dependent check for 2481 JFFS2 magic in mtdsplit. 2482 2483 setenv serverip 10.0.0.1 2484 setenv ipaddr 10.0.0.8 2485 tftp 0xa00000 result/uimage 2486 bootm 0xa00000 2487 2488 Fri Sep 29 20:50:39 BST 2023 2489 2490 setenv bootargs 'liminix mtdparts=phram0:M(rootfs) phram.phram=phram0,0x40411f28,4194304,65536 memmap=4194304$0x40411f28 root=/dev/mtdblock0 console=ttyAMA0,115200 earlycon' 2491 2492 setenv serverip 10.0.0.1 2493 setenv ipaddr 10.0.0.5 2494 setenv bootargs 'liminix console=ttyS0,115200 panic=10 oops=panic earlycon=uart8250,mmio32,0x11002000 root=/dev/mtdblock0' 2495 tftp 0x4007ff28 result/uimage ; tftp 0x40432f28 result/rootfs 2496 bootm 0x4007ff28 2497 2498 setenv bootargs 'liminix console=ttyS0,115200 panic=1 oops=panic earlycon root=/dev/mtdblock0' 2499 2500 md 0x42ff0000 2501 2502 Cannot find regmap for /infracfg@10000000: -524. 2503 2504 # "console=ttyAMA0,38400 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/mtdblock0 rootfstype=squashfs fw_devlink=off" 2505 2506 2507 40812468: 69726553 203a6c61 41424d41 304c5020 Serial: AMBA PL0 2508 40812478: 55203131 20545241 76697264 3c0a7265 11 UART driver.< 2509 40812488: 61723e33 706f6f6d 61203a73 6165726c 3>ramoops: alrea 2510 40812498: 69207964 6974696e 7a696c61 3c0a6465 dy initialized.< 2511 408124a8: 61723e34 706f6f6d 70203a73 65626f72 4>ramoops: probe 2512 408124b8: 20666f20 66663234 30303030 6d61722e of 42ff0000.ram 2513 408124c8: 73706f6f 69616620 2064656c 68746977 oops failed with 2514 408124d8: 72726520 2d20726f 3c0a3232 61433e33 error -22.<3>Ca 2515 408124e8: 746f6e6e 6e696620 65722064 70616d67 nnot find regmap 2516 408124f8: 726f6620 6e692f20 63617266 31406766 for /infracfg@1 2517 40812508: 30303030 3a303030 32352d20 333c0a34 0000000: -524.<3 2518 40812518: 6e61433e 20746f6e 646e6966 67657220 >Cannot find reg 2519 MT7622> 2520 40812528: 2070616d 20726f66 666e692f 66636172 map for /infracf 2521 40812538: 30314067 30303030 203a3030 3432352d g@10000000: -524 2522 40812548: 3e333c0a 6e6e6143 6620746f 20646e69 .<3>Cannot find 2523 40812558: 6d676572 66207061 2f20726f 72666e69 regmap for /infr 2524 40812568: 67666361 30303140 30303030 2d203a30 acfg@10000000: - 2525 40812578: 0a343235 433e333c 6f6e6e61 69662074 524.<3>Cannot fi 2526 40812588: 7220646e 616d6765 6f662070 702f2072 nd regmap for /p 2527 40812598: 63697265 31406766 32303030 3a303030 ericfg@10002000: 2528 408125a8: 32352d20 313c0a34 616e553e 20656c62 -524.<1>Unable 2529 408125b8: 68206f74 6c646e61 656b2065 6c656e72 to handle kernel 2530 408125c8: 67617020 20676e69 75716572 20747365 paging request 2531 408125d8: 76207461 75747269 61206c61 65726464 at virtual addre 2532 408125e8: 66207373 66666666 66666666 66666666 ss fffffffffffff 2533 408125f8: 0a656666 4d3e313c 61206d65 74726f62 ffe.<1>Mem abort 2534 2535 2536 CONFIG_SERIAL_8250_FSL=y 2537 CONFIG_SERIAL_8250_MT6577=y 2538 CONFIG_SERIAL_8250_NR_UARTS=3 2539 CONFIG_SERIAL_8250_RUNTIME_UARTS=3 2540 CONFIG_SERIAL_DEV_BUS=y 2541 CONFIG_SERIAL_DEV_CTRL_TTYPORT=y 2542 CONFIG_SERIAL_MCTRL_GPIO=y 2543 CONFIG_SERIAL_OF_PLATFORM=y 2544 2545 Mon Oct 2 10:17:04 BST 2023 2546 2547 We have a bootable aarch64 kernel for the Belkin, but it does not 2548 understand the memmap= parameter we're using to protect the 2549 phram image from being used as general memory. 2550 2551 One option is to add a reserved-memory stanza in the device tree, 2552 using u-boot "fdt" command, but we don't know the fdt address in u-boot 2553 because it doesn't have any commands to parse the image and set 2554 variables pointing at the sub-components. (There's iminfo, but it's 2555 onyl human-readable) 2556 2557 Second option is to amend the dtb in the tftpboot module: this 2558 would mean regenerating the uimage 2559 2560 Third option: for tftpboot do we _have_ to use FIT? maybe we could 2561 grab the fdt as a separate tftp transaction 2562 2563 we need to apply kernel patch 9401911f2d9f89035f7acebab16e72d43d1282fb 2564 to avoid using ioremap on sysem ram which is not allowed on arm 2565 2566 2567 Tue Oct 3 14:15:38 BST 2023 2568 2569 Progress on Liminix ARM support. The device I'm starting with is 2570 the Belkin RT3200 (also known as [Linksys E8450](https://openwrt.org/toh/linksys/e8450)) which seems to be a featureful piece of kit, and whioch I snagged for a 2571 very good price on the Bay of E 2572 2573 # Where are we right now? 2574 2575 * we can TFTP boot it to userland 2576 * ethernet works 2577 2578 ## What else needs doing? 2579 2580 * it has dual band wifi with many interesting features. I've built the 2581 2582 * we're only running in RAM, probably need to add some kernel config 2583 to support the flash 2584 * initramfs support is not yet implemented 2585 * the flash is NAND flash and it's quite large compared with the 2586 existing Liminix devices, so we're going to add UBIFS which will 2587 use it better than JFFS2 does 2588 * all this work is on a branch and needs to be cleaned up a lot before 2589 I'm letting it into main 2590 2591 ## What have we found? 2592 2593 There are some significant differences between this and the MIPS 2594 devices (yes, other than an entirely different architecture), mostly 2595 to do with "legacy boot" support or the lack thereof. For example: 2596 2597 * there aren't any options like `MIPS_RAW_APPENDED_DTB` to glom 2598 together a kernel and device tree (FDT), because the bootloader is 2599 expected to be able to provide a FDT following "standards". 2600 U-Boot will do this, provided that we use the newer "FIT" Uimage 2601 format which allows a kernel and DTB and initrd to be combined in 2602 the same container. (Sadly we can't use FIT everywhere because a 2603 lot of MIPS devices use really old forks of U-Boot that don't 2604 understand it) 2605 2606 * for tftpboot, on MIPS we use the `memmap` kernel command line option 2607 to reserve some RAM for the root filesystem. On Arm there's no such 2608 option, so we have to add a 2609 [reserved-memory](https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/reserved-memory/reserved-memory.yaml) 2610 node in the device tree instead. Which means, given that we _only_ 2611 want to do this when tftp booting (the memory is wasted 2612 otherwise), we have to rewrite the device tree in that 2613 scenario. 2614 2615 * then it turns out that phram doesn't (didn't) work anyway, because 2616 it calls ioremap() and [you can't use ioremap on system memory on ARM](https://lwn.net/Articles/409689/). In newer kernels this is 2617 fixed: there is a [conditional](https://github.com/torvalds/linux/blob/master/drivers/mtd/devices/phram.c#L127) here to use whichever of ioremap or 2618 memremap is appropriate for the memory passed to phram, but it looks 2619 non-trivial to backport so I've gone for a [much less sophisticated approach](https://gti.telent.net/dan/liminix/commit/f7cd9c2b6e6c99a228e066b09e3febcf71c63fa1#diff-d8e355f1b2dcde7378ebb40c92cdd2ce3125753c) 2620 2621 * we're using [DSA](https://www.kernel.org/doc/Documentation/networking/dsa/dsa.txt) instead of the OpenWrt [swconfig](https://openwrt.org/docs/techref/swconfig) program. There was actually surprisingly little work needed to adjust to this. 2622 2623 2624 Other than that, it was mostly the usual process of "did the kernel 2625 crash silently, or has it just been unable to open a console device?". 2626 In this regard, *one neat trick*: even though U-Boot on this device 2627 doesn't support pstore, we can use it anyway if we don't do compression. 2628 Enable 2629 2630 ``` 2631 PSTORE = "y"; 2632 PSTORE_RAM = "y"; 2633 PSTORE_CONSOLE = "y"; 2634 PSTORE_DEFLATE_COMPRESS = "n"; 2635 ``` 2636 2637 then boot with `panic=3 oops=panic`, then when it resets use the 2638 U_Boot `md` command to see what happened: 2639 2640 ``` 2641 MT7622> md 0x42ff0000 2642 42ff0000: 43474244 00000000 00000ff4 3d3d3d3d DBGC........==== 2643 42ff0010: 39342e31 36373031 500a442d 63696e61 1.491076-D.Panic 2644 42ff0020: 50203123 31747261 3e363c0a 20505050 #1 Part1.<6>PPP 2645 42ff0030: 20445342 706d6f43 73736572 206e6f69 BSD Compression 2646 [....] 2647 42ff0ca0: 20676e69 73756e75 6b206465 656e7265 ing unused kerne 2648 42ff0cb0: 656d206c 79726f6d 3731203a 0a4b3832 l memory: 1728K. 2649 42ff0cc0: 523e363c 2f206e75 74696e69 20736120 <6>Run /init as 2650 42ff0cd0: 74696e69 6f727020 73736563 3e373c0a init process.<7> 2651 ``` 2652 2653 Wed Oct 4 21:08:44 BST 2023 2654 2655 By randomly including chinks of the openwrt config we have made it 2656 find the mt7915e on the pcie bus. I just don't yet know which bit of 2657 the openwrt config it was. 2658 2659 It doesn't actually work yet though. 5GHz wifi gets calibration data 2660 from the flash, so it is not going to work unless (1) we reflash the 2661 firmware partition, or (2) we find another way to provide it 2662 calibration data. 2663 2664 https://forum.openwrt.org/t/belkin-rt3200-linksys-e8450-wifi-ax-discussion/94302/401 2665 2666 Sat Oct 7 22:56:40 BST 2023 2667 2668 We're almost ready to merge the rt3200 support (it's not finished 2669 but it mostly won't break mips) except for the uimage module 2670 which needs all that FIT stuff, and the tftpboot contortions 2671 to amend the dtb for memmap 2672 2673 For legacy uimage 2674 2675 1) add commandline params to dtb 2676 2) objcopy fdt into vmlinux.elf 2677 3) strip to raw image and compress 2678 4) mkimage 2679 2680 2681 For FIT uimage 2682 2683 1) add commandline params to dtb 2684 2) strip to raw image and compress 2685 3) create its file 2686 4) mkimage 2687 2688 Do we still want to handle the no-dtb case? what about 2689 standards-compliant boot, where u-boot is providing the dtb? No option 2690 to provide a commandline in that case, but maybe also no need to. 2691 2692 For tftpboot, am undecided. We could use the dtb rewriting thing 2693 everywhere, in the interest of consistency. 2694 2695 Mon Oct 9 20:45:54 BST 2023 2696 2697 we bumped the kernel entry point to the 32MB mark, as 2698 2699 (1) when using jffs2 (big rootfs image) it was clobbering the dtb at 2700 the end of the filesystem 2701 2702 (2) it should be 2MB aligned anyway and wasn't 2703 2704 However, this has given us the next problem: 2705 2706 OF: fdt: Reserved memory: failed to reserve memory for node 'secmon@43000000': base 0x00000000430B 2707 OF: reserved mem: OVERLAP DETECTED! 2708 phram-rootfs (0x0000000040400000--0x0000000053a31488) overlaps with ramoops@42ff0000 (0x000000004) 2709 Zone ranges: 2710 DMA [mem 0x0000000040000000-0x000000005fffffff] 2711 DMA32 empty 2712 Normal empty 2713 Movable zone start for each node 2714 2715 the end of that phram-rootfs region looks well sus. 2716 2717 [ turns out that decimal is not hex ] 2718 2719 Tue Oct 10 21:37:31 BST 2023 2720 2721 UBI bleurgh ... 2722 2723 The DTB for this device, and/or the OpenWrt installer, seems to 2724 expect already that mtd4 is a UBI thing 2725 2726 mtdinfo -a says: 2727 mtd4 2728 Name: ubi 2729 Type: nand 2730 Eraseblock size: 131072 bytes, 128.0 KiB 2731 Amount of eraseblocks: 1000 (131072000 bytes, 125.0 MiB) 2732 Minimum input/output unit size: 2048 bytes 2733 Sub-page size: 2048 bytes 2734 OOB size: 64 bytes 2735 Character device major/minor: 90:8 2736 Bad blocks are allowed: true 2737 Device is writable: true 2738 2739 <5>UBI: auto-attach mtd4 2740 <5>ubi0: attaching mtd4 2741 <5>ubi0: scanning is finished 2742 <5>ubi0: attached mtd4 (name "ubi", size 125 MiB) 2743 <5>ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes 2744 <5>ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 2048 2745 2746 we made a volume using "ubimkvol /dev/ubi0 -N liminix -S 825" 2747 2748 and from ubinfo -a we can see 2749 2750 0 ubootenv 2751 1 ubootenv2 2752 2 recovery 2753 3 boot_backup 2754 4 liminix 2755 2756 Now we could use "ubiupdatevol /dev/ubi0_4 /path/to/ubifs.img" 2757 to put a ubifs on that volume, but obviously we'd have to boot the 2758 device into Liminix somehow first. 2759 2760 Alternatively in the build environment we could use ubinize to 2761 create the entire image that can be flashed to MTD, but 2762 2763 - this will overwrite erase counters. 2764 - we have to give it a config file that describes all the volumes, 2765 and I'm guessing they need to match up with the existing ones 2766 otherwise we trash the uboot env 2767 2768 2769 Wed Oct 11 17:37:09 BST 2023 2770 2771 We can write ubi volumes from u-boot. Let's for the moment use 2772 mkfs.ubifs and tftp those files to u-boot - we can figure out the 2773 ubinize dance later 2774 2775 We need either 2776 2777 (a) to write an analogue of our jffs2 graft option for mkfs.ubifs, or 2778 (b) to have a "cpio-like" mkfs.ubifs variant that reads filenames on stdin 2779 and writes only those, or 2780 (c) to create a "staging" directory during build with all the store folders that need to go into the filesystem 2781 2782 although the least elegant, option (c) is the simplest and probably 2783 not even slow, at least by comparison with unpacking the kernel source 2784 tarball 2785 2786 we used 2787 uboot> tftpboot 0x40400000 result/rootfs 2788 uboot> ubi write 40400000 liminix $filesize 2789 2790 then can use ubifsmount ubi0:liminix ; ubifsls / to check that it wrote 2791 something valid. To boot this: 2792 2793 setenv serverip 10.0.0.1 2794 setenv ipaddr 10.0.0.8 2795 setenv bootargs 'liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=ubi0:liminix rootfstype=ubifs fw_devlink=off' 2796 tftpboot 0x4007ff28 result/uimage 2797 bootm 0x4007ff28 2798 2799 The other thing we had to fix here is that activate wasn't being built 2800 statically. Have to add -Xlinker -static to CFLAGS - I don't know if 2801 this is a no-op on MIPS 2802 2803 Mon Oct 16 20:51:08 BST 2023 2804 2805 Here's a thing: the u-boot installed by openwrt on this device has a 2806 ubifsload command, and it has a writable ubootenv. So instead of 2807 having a separate partition for the kernel we could put the kernel in 2808 the actual filesystem 2809 2810 I think we should do this by excluding flashimage and including some 2811 other module (to be written) instead. ubimage or somesuch, perhaps. 2812 2813 So the image we wish to create is a ubifs with a kernel inside it in 2814 /boot and we also need to change the u-boot env value of 2815 2816 boot_production=led $bootled_pwr on ; run ubi_read_production && bootm $loadaddr#$bootconf ; led $bootled_pwr off 2817 2818 so that it mounts the rootfs and finds /boot/uimage inside it 2819 2820 From uboot this is setenv and saveenv; from a running linux this is fw_setenv 2821 2822 Thu Oct 19 09:34:15 BST 2023 2823 2824 setenv bootargs 'liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=ubi0:liminix rootfstype=ubifs fw_devlink=off' 2825 2826 2827 ubifsmount ubi0:liminix 2828 ubifsload 4007ff28 boot/uimage 2829 bootm 0x4007ff28 2830 2831 2832 Thu Oct 19 23:11:17 BST 2023 2833 2834 Assuming you've done the openwrt installer to repartion the device, 2835 what are the steps to install Liminix? 2836 2837 1) build rootfs, which incorporates kernel 2838 2839 2) from u-boot: 2840 2841 uboot> ubimkvol /dev/ubi0 -N liminix -S 825 2842 uboot> tftpboot 0x40400000 result/rootfs 2843 uboot> ubi write 40400000 liminix $filesize 2844 uboot> setenv boot_production 'led $bootled_pwr on ; ubifsmount ubi0:liminix; ubifsload 4007ff28 boot/uimage; bootm 4007ff28' 2845 2846 What if we don't have a serial console? can we do all this from openwrt? 2847 2848 Fri Oct 27 23:21:08 BST 2023 2849 2850 setenv serverip 10.0.0.1 2851 setenv ipaddr 10.0.0.8 2852 setenv bootargs 'liminix earlyprintk earlycon=uart8250,mmio32,0xf1012000 ramoops.mem_address=0x8000000 ramoops.mem_size=0x40000 ramoops=max_reason=2 mem=128M earlycon=ttyS0 console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=ubi0:liminix rootfstype=ubifs fw_devlink=off' 2853 setenv bootargs 'liminix ramoops.mem_address=0x8000000 ramoops.mem_size=0x40000 ramoops=max_reason=2 mem=128M console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=ubi0:liminix rootfstype=ubifs fw_devlink=off' 2854 tftpboot ${kernel_addr_r} result 2855 bootm ${kernel_addr_r} 2856 2857 2858 --- 2859 this is potentially worth checkng out because we do have very slow 2860 decompress speed 2861 2862 https://scm.linefinity.com/common/u-boot/commit/5818198e6a184963c6afc82178b23a64435ace6a 2863 2864 Commit 5bb2c550b1 ("arm: mvebu: Move internal registers in 2865 arch_very_early_init() function") implemented code movement according to 2866 (now incomplete) comments which resulted in semi-broken code. 2867 2868 The result is that I-cache is currently disabled for all Armada 38x boards 2869 and maybe there are some other (unreported / undetected) issues. 2870 2871 [...] After this change lzmadec command with lzma image of 0x7000000 bytes is 2872 doing decompression just 5 seconds. Before this change it was 30 seconds. 2873 2874 2875 Mon Oct 30 21:03:55 GMT 2023 2876 2877 We have a kernel that boots on the Omnia, now we need to build a 2878 rootfs. Given this device uses mmc for its primary storage, we should 2879 use a block filesystem not a flash filesystem. 2880 2881 2882 setenv serverip 10.0.0.1 2883 setenv ipaddr 10.0.0.8 2884 setenv bootargs 'liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/mtdblock0 rootfstype=ext4fs fw_devlink=off mtdparts=phram0:22M(rootfs) phram.phram=phram0,0x1300000,23068672,65536 root=/dev/mtdblock0' 2885 tftpboot 0x1000000 tftpboot/uimage ; tftpboot 0x1300000 tftpboot/dtb ; tftpboot 0x29b0000 tftpboot/dtb; tftpboot 0x29c0000 initrd.img 2886 2887 2888 Sat Nov 4 12:22:37 GMT 2023 2889 2890 setenv serverip 10.0.0.1 2891 setenv ipaddr 10.0.0.8 2892 setenv bootargs 'liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/mtdblock0 rootfstype=ext4 fw_devlink=off mtdparts=phram0:22M(rootfs) phram.phram=phram0,0x40300000,23068672,65536 root=/dev/mtdblock0' 2893 tftpboot 0x40000800 tftpboot/uimage ; tftpboot 0x419b0800 tftpboot/dtb ; tftpboot 0x41a00000 initrd.img ; tftpboot 0x40300000 tftpboot/rootfs 2894 bootm 0x40000800 0x41a00000 0x419b0800 2895 2896 kernel 0x40000800 + 2af400 = "402afc00" 2897 rootfs 0x40300000 + 15f9400 = "418f9400" 2898 dtb 0x419b0800 + 4e00 = "419b5600" 2899 2900 Sun Nov 5 00:01:56 GMT 2023 2901 2902 Open questions 2903 2904 1) using -device loader for qemu phram, how do we choose appropriate 2905 start address for all architectures? we could try to unify this 2906 with the tftpboot approach, but that would mean providing the dtb 2907 ourselves somehow which seems silly when qemu already does it 2908 2909 2) flash erase block size for tftpboot phram, it need to match the hardware 2910 - we don't use it at all for squashfs 2911 - for jffs2 it needs to match if tftpboot is to use same rootfs image 2912 as flash 2913 - we don't have any way to do a tftpboot ubifs 2914 - ext4 doesn't care 2915 2916 3) the rootfstype needs thought. 2917 2918 - for all but squashfs it implies an initramfs 2919 - jffs2 can be flashed naively 2920 - ubifs needs an installer to not clobber erase counts 2921 - ext4 (for omnia) needs a block device and can't be flashed along 2922 with the kernel/initramfs because it's a separate disk. or maybe 2923 the omnia can load kernel from mmc? 2924 2925 phram plus mtd_block is ok for ext4 for qemu, so that's one thing 2926 fewer to deal with 2927 2928 4) for tftpboot with a separate initramfs, is there any way to 2929 make address selection easier? 2930 2931 5) ubifs needs a different set of flash parameters (PEB, LEB etc) 2932 2933 Tue Nov 7 23:19:10 GMT 2023 2934 2935 We can flash the turris omnia from a USB stick 2936 https://docs.turris.cz/hw/omnia/rescue-modes/ 2937 2938 it looks for omnia-medkit-latest.tar.gz and (I'm guessing) just 2939 unpacks it onto the emmc. 2940 2941 Perhaps we need per-device installation instructions in the docs. 2942 2943 * rename defaultOutput to installerOutput 2944 * attach some docs to the various options for installerOutput 2945 * add a link from the device's rendered manual section to the 2946 relevant installer output doc 2947 2948 do we expect that outputs will build on each other? e.g. 2949 turris omnia output is basically a tarball (other devices 2950 might want this too) but with docs describing how to reset the 2951 device and hold the button down until the led flashes four times 2952 (other devices probably won't want this). maybe the model here 2953 is that the turris output is a directory with a symlink to the 2954 tarball and an informative README containing the instructions. 2955 Although we also want the instructions in the manual where people 2956 can read them before building anything. 2957 2958 Fri Nov 10 21:27:50 GMT 2023 2959 2960 Realising now that outputs and installers aren't the same thing. 2961 2962 e.g. flashimage can be installed from u-boot or from kexecboot 2963 2964 perhaps we distinguish between the 2965 2966 "installation image": 2967 - firmware.bin 2968 - tarball 2969 - ubifs image 2970 - kernel + rootfs 2971 2972 and "installer" 2973 - kexecboot script 2974 - u-boot script to flash squashfs image 2975 2976 Sun Nov 12 17:17:30 GMT 2023 2977 2978 What TODO? 2979 2980 - "does the kernel live on the filesystem" depends on the bootloader 2981 not the filesystem 2982 - could we implement this with a module that adds to config.filesystem ? 2983 it would depend on whether the bootloader can follow symlinks to 2984 files not in /boot (probably fine unless crossing filesystems) 2985 - the other question is how much futzing around in u-boot can/do we do 2986 to tell u-boot how to boot? for grown-up u-boot it's not a 2987 problem as we can saveenv but are there broken u-boots that would 2988 prevent this? 2989 2990 - kexecboot is unloved and documented in the wrong place. do we have a 2991 test for it even? 2992 - it won't work on aarch64 because it needs memmap= 2993 - hardcodes memory size, which we should probably work out dynamically 2994 2995 - how to put device name into the device docs 2996 maybe devices 2997 2998 - make config.boot.commandLine a single string 2999 - finish omnia 3000 - for installation on turris omnia we need tarball not ext4 image 3001 (but keep the ext4 image anyway for tftpboot and possibly kexecboot) 3002 [done] - now we have lim.parseInt should we use it consistently? 3003 - usefulness tiers for devices ("stable", "experimental", "wip") 3004 - params for ubi(fs) are a mess 3005 - create an l2tp configuration 3006 - iperf and tuning 3007 - wlan country code 3008 3009 3010 Fri Nov 17 17:30:39 GMT 2023 3011 3012 kexec is fraught. I spent some time trying (unsuccesfully) to get a 3013 kexecboot test running, but it doesn't work in qemu for the reason 3014 that the kernel I built for qemu has SMP support but does not have 3015 kexec_nonboot_cpu_func() - which is the needed function to stop 3016 non-boot CPUs before jumping into the new kernel. That this code is 3017 all MIPS-specific (I have to assume that other architectures have 3018 entirely different ways to stop non-boot CPUs?) is a bit worrisome: how 3019 many other ways is kexec hardware-dependent? 3020 3021 We have two scenarios where we may want to use it: 3022 3023 1) the "installer": e.g. for UBI platforms, we want to plonk 3024 a new ubifs on the device without clobbering the erase counters, which 3025 means either doing it from U-Boot - needs serial connection - 3026 or doing it from a Linux of some kind that is not running on the 3027 filesystem we're toasting. 3028 3029 2) reinstalling after the initial install - this is a big deal for 3030 squashfs where there is no other way to change data, and an only slightly 3031 smaller deal for jffs2, where there isn't much room to change much 3032 data. 3033 3034 Maybe instead of kexec we could do this by stopping services and then 3035 pivot_root into a ramfs. We would, I assume, need to stop any processes 3036 that have open files on the root fs, but we would need to have network 3037 interfaces running. So we need a subset of services that run in recovery: 3038 can make this a bundle 3039 3040 * mount a ramfs 3041 * copy the closure of the bundle into the ramfs 3042 * stop all processes (including init?) 3043 3044 sending pid 1 a signal FOO will cause it to run .s6-svscan/SIGFOO 3045 3046 Fri Nov 17 23:53:43 GMT 2023 3047 3048 So we need to extend .s6-svscan/finish to 3049 3050 if test -e /maintenance/bin/init 3051 cd /maintenance 3052 mount --bind /maintenance/ / 3053 chroot . 3054 exec /bin/init -D maintenance 3055 fi 3056 3057 3058 foreground { 3059 if { test -e /maintenance/bin/init } 3060 cd /maintenance 3061 foreground { mount --move /maintenance/ / } 3062 foreground { chroot . } 3063 redirfd -r 0 /dev/console 3064 redirfd -w 1 /dev/console 3065 fdmove -c 2 1 3066 emptyenv /bin/init -D maintenance 3067 } 3068 ${s6-linux-init}/bin/s6-linux-init-hpr -fr 3069 3070 3071 https://openwrt.org/docs/techref/sysupgrade 3072 3073 s6-svscanctl -t /run/service 3074 3075 Sun Nov 19 10:23:17 GMT 2023 3076 3077 # cat `type -p reboot` 3078 #!/nix/store/0v3q2lnh7bwg0ldk24lzmsdnmidmdvm6-execline-mips-unknown-linux-musl-2.9.3.0-bin/bin/execlineb -S0 3079 /nix/store/j41b85ccx0rmf7lm5g13zqb7fs68l8y2-s6-linux-init-mips-unknown-linux-musl-1.1.1.0-bin/bin/s6-linux-init-hpr -r \$@ 3080 3081 3082 s6-linux-init-hpr calls hpr_send("", 0) 3083 then 3084 hpr_shutdown(what, &tain_zero, 0)) 3085 which sends "Shpr"[what] to SCANDIRFULL "/" SHUTDOWND_SERVICEDIR "/" SHUTDOWND_FIFO 3086 3087 which I assume is s6-linux-init-shutdownd.c 3088 3089 it calls prepare_shutdown on socket read, which sets deadline, grace_time. 3090 later (when?) it calls 3091 3092 run_stage3(basedir) ; # we can see this by adding a message to rc.shutdown 3093 # this causes s6-rc services to be downed gently 3094 s6-rc -v2 -bDa change 3095 3096 prepare_stage4(basedir, what) 3097 3098 creates a file STAGE4_FILE with the contents: 3099 s6-linux-init-umountall 3100 scripts/rc.shutdown.final 3101 s6-linux-init-hpr -f -r 3102 3103 unsupervise_tree() ; 3104 3105 goes through /run/service/*/supervise/control fifos 3106 except shutdownd and logger, sending 3107 "d" to each 3108 3109 then does 3110 s6_svc_write(SCANDIRFULL "/" S6_SVSCAN_CTLDIR "/control", "an", 2) 3111 (this is a rescan not a terminate) 3112 3113 #define SCANDIRFULL S6_LINUX_INIT_TMPFS "/" S6_LINUX_INIT_SCANDIR 3114 (works out to be /run/service "/" ".s6-svscan") 3115 3116 ls 3117 kill(-1, SIGTERM) ; 3118 3119 s6-rc -v2 -bDa change 3120 cd /run/service 3121 for i in s6-linux-init-runleveld s6rc-oneshot-runner s6rc-fdholder eth* getty ; do s6-svc -d /run/service/$i; done 3122 3123 3124 s6-rc -v2 -bDa change 3125 cd /run/service 3126 for i in s6-linux-init-runleveld s6rc-oneshot-runner s6rc-fdholder eth* ; do s6-svc -d /run/service/$i; done 3127 s6-svscanctl -an /run/service 3128 3129 Wed Nov 22 22:01:02 GMT 2023 3130 3131 - define a subset of services that run in maintenance mode. 3132 - write a command that copies the closure of this bundle into 3133 /run/maintenance 3134 - create enough non-store filesystem (proc dev etc) to make it run 3135 3136 Thu Nov 23 00:09:44 GMT 2023 3137 3138 I was as surprised as anybody that this seems to work, at least 3139 insofar as it has started a busybox sh process. there is a serious 3140 deficit of symlinks to busybox, so almost no shell scripts work. and I 3141 think we need an rc.init 3142 3143 It would be worth tidying up the main s6 run-image quite a lot before 3144 we go further with this 3145 3146 We'd like to be able to reuse the s6 pseudofile structure (/etc/s6-rc 3147 and /etc/s6-linux-init) but we can't make it a derivation because it's 3148 pseudofiles (with funny permissions) not real files. Maybe we can 3149 invoke the module as a function? 3150 3151 Fri Nov 24 23:29:48 GMT 2023 3152 3153 Turris TODO 3154 3155 - see if network works (eth[012], which is which?) 3156 - wireless drivers 3157 [DONE] ath9k and ath10k, it's like old times 3158 https://docs.turris.cz/hw/omnia/omnia/#turris-omnia-wi-fi-6 3159 (note: other variant of the device has a MT7915AN, should we add 3160 support for that as well?) 3161 3162 - [DONE] feed the watchdog 3163 it looks like compiling watchdog support is sufficient to stop the 3164 thing from rebooting after three minutes, there is no need to actually 3165 feed it from userspace. 3166 3167 - "does the kernel live on the filesystem" depends on the bootloader 3168 not the filesystem 3169 - could we implement this with a module that adds to config.filesystem ? 3170 it would depend on whether the bootloader can follow symlinks to 3171 files not in /boot (probably fine unless crossing filesystems) 3172 - the other question is how much futzing around in u-boot can/do we do 3173 to tell u-boot how to boot? for grown-up u-boot it's not a 3174 problem as we can saveenv but are there broken u-boots that would 3175 prevent this? 3176 - perhaps we need different boot "recipes" - e.g. some device 3177 might want boot.scr and another something different 3178 3179 - create installable tarball and test 3180 - gpio thingy for SFP switching 3181 - iperf 3182 3183 - document the watchdog 3184 - remove kexecboot? it's unloved and documented in the wrong place. do we have a 3185 test for it even? 3186 - it won't work on aarch64 because it needs memmap= 3187 - hardcodes memory size, which we should probably work out dynamically 3188 3189 - how to put device name into the device docs 3190 3191 - [WONT] make config.boot.commandLine a single string 3192 this sounds sensible but it just makes it harder to put useful comments 3193 against command line fragments so that we know why they're there 3194 3195 - usefulness tiers for devices ("stable", "experimental", "wip") 3196 - params for ubi(fs) are a mess 3197 - iperf and tuning 3198 - wlan country code 3199 - create an l2tp configuration 3200 3201 Sun Nov 26 15:37:07 GMT 2023 3202 3203 hatching a plan ... we could do "predictable" network interfaces like this: 3204 3205 . add a devpath attr to network/link.nix 3206 . get the kernel-issued name from "/sys${devpath}/net" 3207 . use ip link set ${oldname} name ${newname} 3208 3209 if we had the full iproute2 thng we could keep the old name as well: 3210 # ip link property add dev wan altname eth1 3211 3212 maybe we could do this with lua/netlink? no support in there currently 3213 for RTM_NEWLINKPROP though 3214 3215 Maybe we'll skip doing the altname. The attraction of it is that it 3216 means the existing name isn't removed, so there's no possibility of a 3217 race. 3218 3219 The kernel will allocate eth0 when asked for eth%d and there is no 3220 eth0. This might be the case where eth0 previously existed but it 3221 just got renamed to lan 3222 3223 Sun Nov 26 21:20:23 GMT 2023 3224 3225 The wrinkle here is ifwait: using netlink we can't wait for an 3226 interface by devpath but have to do it by name - which is a problem if 3227 the interface is not yet present, because there won't be a devpath 3228 in which to look up the name until it is. 3229 3230 So we need a new flow 3231 3232 - wait for devpath to exist 3233 - get the ifindex (which shouldn't change, even if the name does) 3234 - churn rtnetlink messages for that index 3235 3236 We don't want to poll the sysfs file, but we can check it whenever 3237 we get a netlink message 3238 3239 Sun Nov 26 22:33:16 GMT 2023 3240 3241 There is no way to refer to the hardware device for a bridge interface 3242 by sysfs path because it has none. This is probably true of other 3243 virtual devices as well. 3244 3245 ls: cannot access '/sys/class/net/vbridge0/device': No such file or directory 3246 3247 Also, there is no way to refer to the _netdevice_ of a hardware 3248 interface without also knowing its default name, which doesn't help us 3249 if enumeration changes 3250 3251 ls /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/net/ 3252 enp1s0 3253 3254 So we should only be specifying devices by devpath if they're 3255 hardware devices discovered by the kernel, not synthetic devices (that 3256 we pick the name of anyway). 3257 3258 So maybe we don't need to rewrite ifwait, we just do it after renaming 3259 the device 3260 3261 Wed Nov 29 21:28:37 GMT 2023 3262 3263 How do we name outputs? 3264 3265 fileystem image 3266 with-boot filesystem image (e.g. ubifs for belkin) 3267 tarball 3268 with-boot tarball (e.g. omnia) 3269 flashable combined image of kernel + filesystem (e.g. gl-mt*) 3270 kernel + filesystem image + dtb + tftpboot glue 3271 kernel + filesystem image + qemu script 3272 3273 we could add initramfs as a separate thing for tftpboot and qemu 3274 (and FIT images) but it would mean not sharing a kernel with 3275 the outputs that require embedded initramfs 3276 3277 we can enable with-boot variants of outputs by adding a 3278 boot.loader config option. if we go that route, can we 3279 use config options to drive the whole output thingy? 3280 We could have a config option that just changes "defaultOutput" 3281 but is that useful? 3282 3283 Maybe the question is: we can choose a different output at build time 3284 rather than editing configuration - how often is this useful? I think 3285 tftpboot vs installable is about the only case. 3286 3287 adding a /boot to a filesystem differs from making a combined 3288 flashable image because it is a config change and not a composition 3289 of two other already-built outputs. 3290 3291 we could have done tftpboot that way as well, but we chose to unpack 3292 and repack the fdt so we don't have to build two kernels - and also so 3293 that we can have both outputs from the same configuration without 3294 editing any files. 3295 3296 for tftpboot we don't want to make the filesystem embed the kernel 3297 if we need a separate kernel for booting (guessing we can't usually make 3298 u-boot loopback mount a downloaded filesystem image). so that points to 3299 not making it a config option, _or_ to making the inclusion logic 3300 (hardware wants a kernel in filesystem) && !(output == tftpboot) 3301 which itself means the output somehow needs to be injected into the 3302 config 3303 3304 nix-build -I liminix-config=./examples/hello-from-mt300.nix --arg device "import ./devices/my-device" --arg output=tftpboot 3305 3306 let's see if we can not do that? 3307 3308 repacking a ubifs to add /boot is awkward and unpleasant 3309 3310 Thu Nov 30 14:33:08 GMT 2023 3311 3312 ~~We need a new boot-in-rootfs output which calls rootfs with 3313 (config.filesystem // { /boot ... }) when the device 3314 boot type is extlinux~~ 3315 3316 ~~Do we need to put it in systemConfiguration as well? Yes, 3317 otherwise liminix-rebuild won't install it~~ 3318 3319 We may have a problem here actually: if /boot is only set up after 3320 reboot, by adding a link while running the initramfs, how did the 3321 bootloader find the kernel to boot in the first place? So 3322 we need /boot to exist and to point to the new kernel before rebooting 3323 into it, so we need to create it as a real directory along with /nix/store 3324 when making the filesystem, 3325 instead of relying on activate which will be too late. 3326 3327 maybe we could extract the root directory structure creation 3328 as a separate output from rootfs, then there is a single place 3329 to put "and also add /boot" 3330 3331 we will need to update pkgs/min-copy-closure/liminix-rebuild.sh 3332 to add /boot 3333 3334 we could make the contents of /boot a derivation and then 3335 /boot itself is just a symlink to it. we would need to ensure that 3336 the derivation is part of the system closure, though 3337 3338 Sat Dec 2 15:33:07 GMT 2023 3339 3340 - make rootfs the directory structure 3341 3342 Sun Dec 3 23:31:35 GMT 2023 3343 3344 Spent too much of the weekend first fighting run-liminix-vm.sh and 3345 then rewriting it in Fennel, but we are now at the point that we can 3346 boot u-boot in qemu. However, it maps the rootfs into high memory 3347 where phram can find it, instead of putting it into a flash that 3348 _qemu_ can see as flash, so u-boot is not able to boot the kernel or 3349 at least not in a similar-to-hardware fashion. Once we've added that, 3350 we need to write a test for boots-a-kernel-in-the-filesystem 3351 3352 Mon Dec 4 19:46:58 GMT 2023 3353 3354 We wanted a test that we are creating an image that u-boot can boot 3355 using extlinux. Turns out that u-boot only has scripts to do this in 3356 the case that the storage device has a partition table. Which is 3357 representative of the Omnia mmc, but maybe not going to work for 3358 jffs2/ubifs 3359 3360 (For ubifs it might be OK, there's some concept of partitions 3361 3362 ubifs_boot=if ubi part ${bootubipart} ${bootubioff} && ubifsmount ubi0:${bootubivol}; then devtype=ubi; devnum=ubi0; bootfstype=ubifs; distro_bootpart=${bootubivol}; run scan_dev_for_boot; ubifsumount; fi 3363 3364 ) 3365 3366 So what do we need? a disk image with a partition table and an ext4fs 3367 image in the only partition, and that partition to be bootable. Then 3368 run-liminix-vm gets a --disk-image option which causes it to use 3369 U-Boot instead of direct load (can we wrap it with something that sets 3370 up the paths so it can find u-boot and qemu?) 3371 3372 ok, we're going to define outputs.diskimage which is like 3373 outputs.flashimage but it has a partition table. Then we can make a 3374 qemu configuration with an ext4 filesystem and defaultOutput="diskimage" 3375 and config.boot.loader.extlinux.enable = true 3376 3377 sfdisk default behaviour for GPT partitioned disk is to start at sector 2048 3378 (sectors are 512 bytes) 3379 3380 Device Start End Sectors Size Type 3381 disk.bin1 2048 14335 12288 6M Linux filesystem 3382 3383 Tue Dec 5 23:54:22 GMT 2023 3384 3385 Need mbr not gpt. At least, it takes up less space and it doesn't 3386 inveigle us into EFI boot. 3387 3388 This is close to working except that it doesn't want to boot a uimage 3389 3390 Enter choice: 1 3391 1: Liminix 3392 Retrieving file: /boot/initramfs 3393 Retrieving file: /boot/kernel.gz 3394 append: console=ttyAMA0 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/mtdblock0 rootfstype=ext4 fw_devlink=off 3395 zimage: Bad magic! 3396 3397 Thu Dec 7 19:33:02 GMT 2023 3398 3399 virtio devices don't have standard major/minor so we can't create 3400 device nodes for them at build time. Either we mount devtmpfs in the 3401 initramfs (do we then have to move it to /target?) or we parse the 3402 /sys/block/vda/vda1/dev node to get 253:1 or whatever 3403 3404 Fri Dec 8 16:36:07 GMT 2023 3405 3406 We'd like to remove the ugly special handling of qemu dtb. Here are 3407 some thoughts 3408 3409 1) qemu provides its guest the correct DTB for the configured hardware. 3410 This is a desirable thing and we wish more platforms did it, so would 3411 like not to replace it with a static dtb if we can avoid that 3412 3413 2) so we need some kind of config option that says "platform provides the 3414 device tree" (or alternatively, "we need a static dtb, platform doesn't 3415 provide it"). 3416 3417 config.boot.platformProvidesDeviceTree 3418 3419 3420 3421 Monday 3422 3423 so where are we? we've removed the need for every hardware device to 3424 be able to build flashimage, because not every hw device (looking at you, 3425 bellkin/turris) works like that. 3426 3427 for the belkin we can have imports = [ modules/outputs/ubimage ] 3428 and extlinux enabled 3429 3430 for the nwa from Raito we'd like to have imports = [ modules/outputs/ubimage ] 3431 but the bootloader is mtd partition-based (or ubi volume? check?) - 3432 so ubimage needs to know how to do that 3433 3434 perhaps we need an output for "smash together a kernel and a 3435 filesystem image that does not also contain a kernel, and don't 3436 put a partition table on the front" 3437 3438 diskimage { 3439 partitionType = "mtd" ; # or "mbr" or maybe "gpt" 3440 partitions = [ o.uimage o.rootfs ]; 3441 } 3442 3443 3444 3445 3446 for the turris we need to check but proceeding on the assumption 3447 it wants a tarball with extlinux enabled 3448 3449 https://docs.turris.cz/geek/schnapps/schnapps/#export-and-import 3450 https://wiki.turris.cz/en/howto/omnia_booting_from_external_storage 3451 3452 if we adopt this as our installation format then we are not 3453 reformatting the flash and will keep the btrfs that the device 3454 was shipped with. 3455 3456 https://forum.turris.cz/t/update-to-5-1-x-by-medkit/13986/12 3457 suggests that we could install a custom medkit from the 3458 vendor OS 3459 3460 => btrsubvol mmc 0:1 3461 ID 257 parent 5 name /@ 3462 ID 259 parent 5 name /@factory 3463 3464 there don't seem to be any other btr commands in u-boot 3465 3466 Tue Dec 12 14:38:53 GMT 2023 3467 3468 from the source code, to get to the various omnia revovery modes 3469 3470 uboot> setenv omnia_reset 3 # or 1..n 3471 uboot> setenv boot_targets rescue 3472 uboot> boot 3473 3474 // reset boot_targets to default value. 3475 3476 Tue Dec 12 22:44:34 GMT 2023 3477 3478 The hold-down-reset-until-n-leds-flash support depends quite heavily 3479 on the post-boot Linux environment, in that it appears to be passing 3480 omniarescue=3 rescue_mode=3 to the kernel command line -> pid 1 3481 cmdline 3482 3483 On the other hand, it is described as being able to boot from usb 3484 stick if there's a boot.scr on the usb stick, so maybe we just do 3485 that. The installation process could then be "boot usb, dd the 3486 disk image to mmc, reboot, remove usb, realise we got the wrong root=". 3487 Hmm. 3488 3489 * Could we edit extlinux.conf for first boot? But bear in mind it's a 3490 link to a store file. 3491 3492 * Could we have extlinux.conf point at mmc0 and somehow override it for the 3493 usb stick boot? 3494 3495 * Could preinit try multiple root mounts until it gets one that works? 3496 3497 * maybe we could detect omniarescue on kernel command line and switch to 3498 usb root? 3499 3500 * maybe outputs.usbstick could generate a customised rootfs image? 3501 it might be unworkable to 3502 3503 (narrator: it boots from mmc0 first and usb stick second, so that's not 3504 particularly useful) 3505 3506 3507 3508 3509 Device 0: Vendor: SanDisk Rev: 1.00 Prod: Cruzer Blade 3510 Type: Removable Hard Disk 3511 Capacity: 7632.0 MB = 7.4 GB (15630336 x 512) 3512 ... is now current device 3513 Scanning usb 0:1... 3514 No EFI system partition 3515 fdt_find_or_add_subnode: chosen: FDT_ERR_BADSTRUCTURE 3516 ERROR: /chosen node create failed 3517 - must RESET the board to recover. 3518 3519 Thu Dec 14 15:32:39 GMT 2023 3520 3521 from the omnia rescue image, we have 3522 3523 ## Loading kernel from FIT Image at 01700000 ... 3524 3525 Load Address: 0x00800000 3526 Entry Point: 0x00800000 3527 3528 3529 int lzmaBuffToBuffDecompress(unsigned char *outStream, SizeT *uncompressedSize, 3530 const unsigned char *inStream, SizeT length) 3531 3532 err = image_decomp(os.comp, load, os.image_start, os.type, 3533 load_buf, image_buf, image_len, 3534 CONFIG_SYS_BOOTM_LEN, &load_end); 3535 3536 configs/mvebu_db_armada8k_defconfig:CONFIG_SYS_BOOTM_LEN=0x800000 3537 default 0x4000000 if PPC || ARM64 3538 default 0x1000000 if X86 || ARCH_MX6 || ARCH_MX7 3539 default 0x800000 3540 3541 Fri Dec 15 19:21:53 GMT 2023 3542 3543 Let's put some English words on the page to explain the above 3544 gibberish. Since I upgraded the U-Boot on my Turris Omnia, it has 3545 stopped being able to tftpboot. 3546 3547 Uncompressing Kernel Image 3548 lzma compressed: uncompress error 7 3549 Must RESET board to recover 3550 3551 "uncompress error 7" means there is not enough space in the output 3552 buffer, and the output buffer is set by CONFIG_SYS_BOOTM_LEN which is 3553 8192k, smaller than the uncompressed 12104200 of our kernel. 3554 3555 So how can we fix? 3556 3557 * one possibility is to do what the turris rescue mode does: build an 3558 uncompressed uimage and then lzma the result. u-boot can uncompress 3559 the received file using lzmadec command. we'd want to do this without 3560 breaking tftpboot on every other device that might not have an lzmadec 3561 command 3562 3563 * is there bloat in the kernel we could trim? probably not 4MB of it 3564 3565 * we could build a custom u-boot with a bigger buffer. this _might_ 3566 not be a completely stupid idea as it's only the people prepared to 3567 open the box that would be doing tftp workflows anyway, so provided 3568 it's possible to replace u-boot without bricking it 3569 3570 * we could try building zimage instead of uimage and use bootz to 3571 start it 3572 3573 Sat Dec 16 11:15:56 GMT 2023 3574 3575 there is another use case for weird tftpboot derivation, which is the 3576 device Raito has ported to where you need to wave a magic chicken at 3577 u-boot on each command line 3578 3579 Sat Dec 16 23:32:11 GMT 2023 3580 3581 Turns out that even when using an uncompressed uimage, u-boot runs the 3582 code to check the decompressed size, so that doesn't help at all. But 3583 booting a zImage works fine. I am committing a first pass of 3584 modules/outputs/tftpbootlz.nix which does this using a lot of 3585 copy-paste and (ironically) no lzma stuff at all. 3586 3587 Sun Dec 17 16:25:30 GMT 2023 3588 3589 it's started failing to mount root on arm32 because it's not recognising 3590 the reserved-memory and something is trashing the phram filesystem 3591 3592 reserved-memory { 3593 3594 phram-rootfs { 3595 reg = <0x1400000 0x1900000>; 3596 compatible = "phram"; 3597 }; 3598 }; 3599 3600 Sun Dec 17 17:13:54 GMT 2023 3601 3602 * We need to write the fdt phram differently on 64 bit vs 32 bit 3603 (address-cells and size-cells should be 2 or 1) 3604 3605 * this might be why it wasn't working on mips (can we test this 3606 somehow in qemu or do we need to plug a device in?) 3607 3608 qemu user-mode networking has a builtin tftp server. 3609 3610 so we need a test that builds the tftpboot target for each qemu arch, 3611 and then does run-liminix-vm with the --lan and --u-boot options, then 3612 drives it with expect 3613 3614 3615 * maybe tftpboot[lz] could be reintegrated with the regular one somehow 3616 3617 Fri Dec 22 15:11:35 GMT 2023 3618 3619 We have a working test for tftpboot, on all platforms, which took a 3620 while. 3621 3622 * tftpbootlz needs to be updated with what we learned, or merged 3623 back into it 3624 3625 * omnia install 3626 3627 - build a stripped-down installer image which can be put on a 3628 usb stick 3629 - from openwrt on the device, use fw_setenv to set boot order 3630 - boot into the installer image- 3631 reformat the emmc as per requirements 3632 - PREFIX=/mnt liminix-rebuild 3633 - profit 3634 3635 Could we liminix-rebuild into /some/prefix/nix/store this would 3636 actually be useful both for this boot-from-usb scenario and for 3637 levitate. Looks like min-copy-closure doesn't need anything much on 3638 the destination system except a working sshd and maybe an empty 3639 /nix/store and /persist. Potentially we could even do it without 3640 chroot (which would save running a second sshd), just add a prefix on 3641 the paths 3642 3643 what's the fallback? we're not touching the turris rescue 3644 system (which is in nor flash) so we can expect all the 3645 knightrider modes to continue to workto be able to 3646 boot back into that and restore the vendor os? I think so 3647 3648 Fri Dec 22 16:56:40 GMT 2023 3649 3650 If we're going to use fw_setenv to change boot order, 3651 we could equally well boot from tftp and not need the 3652 usb stick 3653 3654 fw_setenv boot_targets "tftp mmc0 nvme0 scsi0 usb0 pxe dhcp" 3655 setenv bootcmd_tftp "echo TFTP BOOT" 3656 3657 how can we get a tftp boot into few enough characters to reasonably 3658 put it in an environment variable? 3659 - use dhcp? 3660 - embed bootargs into the fdt 3661 3662 Fri Dec 22 21:10:53 GMT 2023 3663 3664 * dtb needs size and offset of uncompressed root filesystem to add 3665 reserved-memory and cmdline params 3666 3667 * setting these in the dtb will change the size of the dtb 3668 3669 * am assuming that we don't want the kernel to relocate into 3670 ram that clashes with the root fs 3671 3672 * should we care about phys mem fragmentation? 3673 3674 conclusion: 3675 3676 hardware.loadAddress 3677 <uncompressed/relocated kernel> 3678 tftp.loadAddress 3679 <space for uncompressed root> 3680 uimage or zimage 3681 dtb 3682 compressed root 3683 3684 3685 [ should we rename hardware.loadAddress to something that expresses 3686 more clearly it's the _kernel_ load address? ] 3687 3688 [ another thing we need to do is stop building two kernels 3689 because the uimage and zimage derivations are different ] 3690 3691 Sat Dec 23 18:07:43 GMT 2023 3692 3693 Addendum: for a zimage we need the compressed kernel to be at the 3694 highest address, otherwise it prints "Starting" and then hangs 3695 indefinitely. 3696 3697 I believe this to be because the kernel decompressor sets up a stack 3698 directly after the compressed payload, so will trash the fdt if it was 3699 also there. The bug didn't exhibit on Turris Omnia with the same 3700 layout, but maybe that was just luck. 3701 3702 Sat Dec 23 18:11:04 GMT 2023 3703 3704 Here is scope of work for Turris: 3705 3706 (I) we need to build a suitable tftpboot image for 3707 recovery/install. 3708 3709 - disk partitioning tools and mkfs stuff 3710 - kernel with all the filesystems 3711 - dhcp client for connecting to wired network 3712 3713 (II) we need instructions for building the real system 3714 and using min-copy-closure to copy and install the system 3715 configuration of the real one into /mnt 3716 3717 (III) probably try the same recovery image as a USB stick 3718 3719 (IV) I've lost track of what we're doing with /boot, does that work? 3720 3721 (V) gpio thingy for SFP switching 3722 3723 (VI) iperf, performance testing 3724 3725 (VII) put device name and usefulness tiers ("stable", "experimental", "wip") in the docs 3726 3727 (VIII) params for ubi(fs) are a mess 3728 3729 (IX) wlan country code 3730 3731 Tue Dec 26 16:23:37 GMT 2023 3732 3733 I seem to have lost a chunk of notes here. Have added 3734 systemConfiguration/bin/install which does the stuff to copy the right 3735 files into /bin and wherever. There is currently no test for it though 3736 3737 We could further simplify liminix-rebuild by adding --reboot as a flag 3738 to install 3739 3740 Tue Dec 26 21:38:43 GMT 2023 3741 3742 To be any use, the test needs to be end-to-end - as in, rather than 3743 just checking some files are copied, test that the machine rebooted 3744 successfully 3745 3746 Fri Dec 29 18:36:16 GMT 2023 3747 3748 Our test for liminix-rebuild uses qemu block device and ext4 instead 3749 of phram because -device loader doesn't seem to survive a reboot. 3750 And it needs some free space in the ext4 partition inside the 3751 mbr image so that it can install new stuff. However, the 3752 filesystem is sized to be near-full. 3753 3754 If the mbrimage output is to be much use, probably there should be 3755 some way of telling it how big the disk is. Maybe it should use 3756 hardware.flash.size? 3757 3758 UBI also does a bad job of integrating into the hardware.flash hierarchy 3759 (but ubi is also more complicated as the ubi volumes are "nested" inside 3760 an MTD partition) 3761 3762 To move forwards with this test I think I will make it not depend on 3763 mbrimage for now, but we have to come back to this. Maybe importing 3764 the mbrimage module provides new hardware.disk = { partitions, size etc} 3765 config options. 3766 3767 Sun Dec 31 23:52:04 GMT 2023 3768 3769 https://developer.ridgerun.com/wiki/index.php/Setting_up_fw_printenv_to_modify_u-boot_environment_variables#Preparing_the_fw_env.config_file 3770 3771 can we extract the fw_env config data somehow to produce an appropriate 3772 file for the device? 3773 3774 the device config needs to specify partition name and offset at minimum, 3775 possibly also size. 3776 3777 we can create a service that writes the config based on those values. but 3778 if we are to be using fw_setenv from the shell, there is no service 3779 which depends on that service. whatever defines the service also needs 3780 to add it to system.services so that the recovery system can specify it 3781 3782 Sat Jan 6 12:30:27 GMT 2024 3783 3784 How do we min-copy-closure to the device when we don't have anything 3785 hooked to the LAN port? It's rather easy to break the WAN connection 3786 when it involves going out to the internet and back 3787 3788 * Don't want to plug it into the actual lan because it's doing dhcp service 3789 and that is going to confuse 3790 3791 * the machine we're copying from is loaclhost 3792 3793 * we could do some kind of port forwarding thing? maybe a port forward on 3794 run-border-vm qemu user networking ... 3795 3796 * static route on loaclhost? 3797 3798 512 sudo ip netns add test-lan 3799 514 sudo ip link set dev enp1s0 netns test-lan 3800 3801 525 sudo ip link add veth-test-lan type veth peer veth1 netns test-lan 3802 533 sudo ip netns exec test-lan ip link add name br0 type bridge 3803 536 sudo ip netns exec test-lan ip link set veth1 master br0 3804 537 sudo ip netns exec test-lan ip link set enp1s0 master br0 3805 sudo ip netns exec test-lan /nix/store/dh66q9k402pwpmmgc983xwmwb3vvvjbr-busybox-1.36.1/bin/busybox udhcpc -i br0 3806 3807 then we could add a route to 10.8.0.1/32 with dev veth-test-lan ? 3808 3809 Sat Jan 6 20:52:45 GMT 2024 3810 3811 This is all beside the point right now because the _recovery_ system 3812 does not run all this stuff - it just has a dhcp client on the lan 3813 interface. We could plug it straight into the switch. 3814 3815 3816 As we already just plugged it into enp1s0 on loaclhost, could we 3817 do somethin to put it on the lan from there? add it to vbridge0? 3818 3819 Sun Jan 7 15:30:57 GMT 2024 3820 3821 Turns out we should have used a working ethernet cable. 3822 3823 Sun Jan 7 15:31:14 GMT 2024 3824 3825 OK, so 3826 3827 # on device 3828 mount /dev/mmcblk0p1 /mnt 3829 [ take a snapshot if needed ] 3830 [ clear out the turrisos files ] 3831 ls /mnt/@ 3832 3833 # on build 3834 3835 $ nix-build -I liminix-config=./examples/rotuer.nix --arg device "import ./devices/turris-omnia" -A outputs.systemConfiguration 3836 $ nix-shell --run "min-copy-closure -r /mnt/@ root@recovery.lan result " 3837 3838 # on device 3839 3840 $ mkdir /mnt/@/persist 3841 $ /mnt/@/nix/store/swf3vn9bzx198c0cwp6naq0glqa9192n-make-stuff-armv7l-unknown-linux-musleabihf/bin/install /mnt/@/ 3842 3843 this fails because it tries to copy from the unprefixed nix 3844 store. Also probably it should mkdir $prefix/persist. Also it needs to 3845 create $prefix/boot: it's too late to do that with `activate` 3846 because u-boot will need it to exist in order to load the initramfs 3847 that runs activate 3848 3849 Thu Jan 11 23:36:47 GMT 2024 3850 3851 squashfs rootfsType doesn't rebuild when the kernel config is changed 3852 3853 Mon Jan 22 19:04:45 GMT 2024 3854 3855 setenv serverip 10.0.0.1 3856 setenv ipaddr 10.0.0.8 3857 compraddr=0x01000000 3858 tftpboot ${compraddr} recovery.img.lzma 3859 setexpr writeaddr ${filesize} + $compraddr 3860 lzmadec ${compraddr} $writeaddr 3861 usb start 3862 usb dev 0 3863 wdt dev watchdog@20300 3864 wdt stop 3865 usb write ${writeaddr} 0 ${filesize} 3866 3867 3868 Thu Jan 25 11:55:36 GMT 2024 3869 3870 openwrt: 3871 CONFIG_BROADCOM_PHY=m 3872 CONFIG_FIXED_PHY=y 3873 CONFIG_GENERIC_PHY=y 3874 CONFIG_IP17XX_PHY=m ? 3875 CONFIG_MARVELL_PHY=y 3876 CONFIG_MVSW61XX_PHY=y ? 3877 CONFIG_RTL8366RB_PHY=m ? 3878 CONFIG_RTL8366S_PHY=m ? 3879 CONFIG_RTL8367B_PHY=m ? 3880 CONFIG_SWPHY=y 3881 CONFIG_USB_PHY=y 3882 3883 CONFIG_FIXED_PHY=y 3884 CONFIG_GENERIC_PHY=y 3885 CONFIG_MARVELL_PHY=y 3886 CONFIG_PHY_MVEBU_A3700_COMPHY=y 3887 CONFIG_PHY_MVEBU_A38X_COMPHY=y 3888 CONFIG_SWPHY=y 3889 # 3890 3891 Sat Jan 27 18:14:13 GMT 2024 3892 3893 To make the recovery system (and tftpboot generally) more useful, it 3894 would be good to resize the root fs on boot. Need to do this before 3895 anything that writes to it 3896 3897 Mon Jan 29 21:50:59 GMT 2024 3898 3899 something is corrupted in the uncompressed rootfs 3900 3901 3902 $ head -c $(printf "%d" 0x2be0000) rootfs | sha1sum 3903 142571fe0436c18191727d1d4c2fd32163c1f2e1 - 3904 => sha1sum 0x1000000 2be0000 3905 sha1 for 01000000 ... 03bdffff ==> 142571fe0436c18191727d1d4c2fd32163c1f2e1 3906 3907 but! 3908 3909 $ head -c $(printf "%d" 0x2bf0000) rootfs | sha1sum 3910 7aa004ba87c6772bade491fbade164e2dfe100f9 - 3911 => sha1sum 0x1000000 2bf0000 3912 sha1 for 01000000 ... 03beffff ==> 1a0923a94784d0c0b86006c5e6fff1649770dad3 3913 3914 something is trashing something in the range 03be0000 - 03beffff 3915 or else it's not being decompressed properly 3916 3917 pxefile_addr_r=0x1900000 3918 ramdisk_addr_r=0x2200000 3919 scriptaddr=0x1800000 3920 fdt_addr_r=0x2000000 3921 fdtcontroladdr=7fb19b30 3922 fdtfile=armada-385-turris-omnia.dtb 3923 fdt_high=0x10000000 3924 initrd_high=0x10000000 3925 kernel_addr_r=0x1000000 3926 0x1700000; 3927 0x10000000 3928 3929 Sun Feb 4 11:55:00 GMT 2024 3930 3931 restructuredtext headings: 3932 3933 https://devguide.python.org/documentation/markup/#sections 3934 3935 3936 ####### chapter (one per filename) 3937 ******* 3938 ======= 3939 ------- 3940 3941 Mon Feb 5 09:57:52 GMT 2024 3942 3943 Before calling the Omnia "done" I'd like to get it to the point that 3944 I can actually use it as a CPE. This means 3945 3946 - writing something down about how we handle static addresses 3947 - hosts that need static ipv6 can configure it themselves as ::n 3948 where n is a small number. this won't clash with slaac 3949 - the `hosts` param to dnsmasq can specify static ipv4 3950 3951 - dealing with port forwards and allowed incoming in the firewall 3952 3953 - would be quite cool to run sniproxy instead of forwarding to 3954 loaclhost (extra credit) 3955 3956 Sat Feb 10 18:23:54 GMT 2024 3957 3958 ARGH KERNEL 3959 3960 You can't define CONFIG_NETFILTER=y in a monolithic kernel and expect 3961 later to separately build some modules that use it, because there are 3962 a bunch of symbols that only get defined if certain other CONFIG 3963 options are set at the time that the monolithic kernel is built. 3964 3965 https://github.com/torvalds/linux/blob/master/net/netfilter/core.c#L689 3966 3967 Another example is 3968 https://github.com/torvalds/linux/blob/master/include/linux/netdevice.h#L160 3969 - if you decide after building the kernel that you're going to build 3970 some wireless modules, you can't do that without rebuilding the kernel 3971 so that it knows to expect them 3972 3973 The moral of the story seems to be: if you have a compiled Linux kernel source tree and you change some symbol from "is not set" to m and then run make modules, you cannot in general expect that newly compiled module to work. 3974 3975 AP advertised VHT without HT, disabling HT/VHT/HE 3976 3977 TODO 3978 3979 - [done] support kernel version as parameter to builder pkgs/kernel/default.nix 3980 - [done] extract the change in how module loading works from omnia device config, 3981 and fix the other thing that uses it 3982 - [axed] wlan module to take 'backported' as a parameter 3983 half of the omnia conditionalConfig can go into the module 3984 - [done] upgrade omnia to kernel v6 3985 - figure out what mdns we need for local hostname resolution 3986 (maybe bridging lan/wlan)? 3987 - [DONE] slow wifi because "AP advertised VHT without HT, disabling HT/VHT/HE" 3988 - [DONE] add local domain to secrets 3989 - run sniproxy instead of forwarding 3990 - [test] forward some port to loaclhost 22 for inbound ipv4 ssh 3991 3992 3993 Mon Feb 12 21:50:35 GMT 2024 3994 3995 # find /run/service-state/dhcp6c.wan.link.pppoe/address/ 3996 /run/service-state/dhcp6c.wan.link.pppoe/address/ 3997 /run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM 3998 /run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/valid 3999 /run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/preferred 4000 /run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/len 4001 /run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/address 4002 # 4003 4004 valid 7199 preferred 3599 4005 4006 Tue Feb 13 19:44:57 GMT 2024 4007 4008 Before we put this back live, would be good to 4009 4010 [done] 1) move the leases file into /persist 4011 4012 I think we'll do /persist/service/<name>/ and change ssh to use the same 4013 scheme. 4014 4015 we could put mkpersist() in serviceFns which would check for /persist 4016 and return a directory in /persist/service/ or /run/service-state 4017 4018 (will something bad happen if we use /run/service-state? it will also 4019 expose the thingy as an output, but whether it's accessible that way 4020 will depend on whether there's a writable fs or not, which is unexpected) 4021 4022 : rename service-state to /run/services/outputs 4023 : on boot 4024 : if /persist 4025 : create /persist/services/state and symlink /run/services/state to it 4026 : else create /run/services/state 4027 4028 4029 [done] 2) maybe change the local domain back to .lan? setting up 4030 systemd-networkd with search domains is an awful faff 4031 4032 [done] 3) work out what to do with incoming ssh from wan 4033 4034 - For noetbook and thinkpad we have a vpn anyway so can expect to 4035 reach loaclhost directly using ipv6 4036 4037 - stop ssh from ever trying to get to our ipv4 address. 4038 - we could get rid of A record for loaclhost.telent.net but 4039 there are a bunch of CNAMES pointing at it for web servers. 4040 - we could reject incoming connections to tcp4 port 22 in firewall 4041 and then there is a clear signal to Dont Do That Then 4042 4043 - for emergency use, dnat ipv4 2200 and 2201 to rotuer and loaclhost 4044 4045 Tue Feb 13 22:31:03 GMT 2024 4046 4047 * the reason we can't reboot is that there is a service to add each 4048 lan device to the bridge which does ifwait $dev running, which doesn't 4049 return until there's something plugged in. So s6-rc hangs indefinitely 4050 until the lan switch is fully populated. This is definitely a "next 4051 milestone" thing. 4052 4053 * another example of "thing that depends on other thing but which it 4054 is actually OK if neither of them happen" might be "mount a 4055 filesystem if there is a usb mass storage device attached" 4056 4057 * I don't know if failover also fits into the model we don't quite 4058 have. LTE route depends on pppoe not being healthy 4059 4060 we can have services (or bundles) that aren't part of the default target, 4061 and plumb them into events of some kind (netlink?) to bring them up/down? 4062 4063 we can use s6-rc instanced services: 4064 https://skarnet.org/software/s6/instances.html 4065 4066 "s6-instance-create and s6-instance-delete are relatively expensive operations, because they have to recursively copy or delete directories and use the synchronization mechanism with the instance supervisor, compared to s6-instance-control which only has to send commands to already existing supervisors. If you are going to turn instances on and off on a regular basis, it is more efficient to keep the instance existing and control it with s6-instance-control than it is to repeatedly create and delete it. " 4067 4068 Probably we need something that reads netlink messages and converts 4069 them to a format that we can use to control services. Is there a 4070 benefit to using services here and not just running commands? it means 4071 the system state change we desire will stay changed. 4072 4073 TODO items not to lose track of 4074 4075 - speed testing (iperf) 4076 - make gl-ar750 tftpboot build again 4077 - finish belkin 4078 - install sniproxy 4079 - is there something simple we can do to make it reboot again? 4080 - turn rotuer,extneder examples into "profiles" that don't embed 4081 hardware specifics 4082 4083 Thu Feb 15 11:50:56 GMT 2024 4084 4085 1) to make tftpboot work with old bootm implementations we need 4086 4087 - compressed root 4088 - uncompressed root 4089 - kernel with dtb 4090 dtb needs to know where uncompressed rootfs is and how big 4091 4092 2) if the image is a zImage (arm32) or an Image (arm64) we have to stick 4093 with the three-arg bootz, and the dtb has to be lower in ram than the kernel 4094 4095 Fri Feb 16 15:43:32 GMT 2024 4096 4097 DHCP6c refresh is still wrong. We get updates for an address that 4098 hasn't changed prefix or length, when the expiry times have changed, 4099 and we can't action that by remove;add because remove will wipe out 4100 any routes through the interface but add won't put them back 4101 4102 We can use "change" for both adds and changes, but we need to know that 4103 a change is not a delete 4104 4105 The "identity" of an address is the address itself: kernel won't 4106 let you add the same address with two different prefixes. 4107 4108 Keeping it simple, we could call "change" on every address in the 4109 new-addresses list and "del" on every address in old-addresses 4110 that is no longer in new-addresses 4111 4112 If the upstream has changed length, "ip addr change" is ignored, 4113 so it needs to be in deleted as well as added/changed 4114 4115 Fri Feb 16 19:37:08 GMT 2024 4116 4117 [ 3.839775] cfg80211: module verification failed: signature and/or required key missing - tainting kernel 4118 [ 4.156952] ath10k_pci 0000:00:00.0: enabling device (0000 -> 0002) 4119 [ 4.165756] ath10k_pci 0000:00:00.0: pci irq legacy oper_irq_mode 1 irq_mode 0 reset_mode 0 4120 [ 4.399285] ath10k_pci 0000:00:00.0: qca9887 hw1.0 target 0x4100016d chip_id 0x004000ff sub 0000:0000 4121 [ 4.408906] ath10k_pci 0000:00:00.0: kconfig debug 1 debugfs 0 tracing 0 dfs 0 testmode 0 4122 [ 4.420096] ath10k_pci 0000:00:00.0: firmware ver 10.2.4-1.0-00047 api 5 features no-p2p,ignore-otp,ski 4123 p-clock-init,mfp,allows-mesh-bcast crc32 62f7565f 4124 [ 4.467443] ath10k_pci 0000:00:00.0: board_file api 1 bmi_id N/A crc32 546cca0d 4125 [ 5.472096] ath10k_pci 0000:00:00.0: htt-ver 2.1 wmi-op 5 htt-op 2 cal file max-sta 128 raw 0 hwcrypto 4126 [ 5.585796] ath: EEPROM regdomain: 0x0 4127 [ 5.589712] ath: EEPROM indicates default country code should be used 4128 [ 5.596364] ath: doing EEPROM country->regdmn map search 4129 [ 5.601875] ath: country maps to regdmn code: 0x3a 4130 [ 5.606831] ath: Country alpha2 being used: US 4131 [ 5.611425] ath: Regpair used: 0x3a 4132 [ 6.742365] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware 4133 [ 6.903389] random: hostapd: uninitialized urandom read (1027 bytes read) 4134 [ 8.169901] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware 4135 [ 14.450193] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware 4136 [ 15.518682] random: hostapd: uninitialized urandom read (1027 bytes read) 4137 [ 16.762697] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware 4138 [ 23.030622] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware 4139 [ 4140 4141 4142 Tue Feb 27 23:16:27 GMT 2024 4143 4144 We made it a full week with rotuer running internet chez nous and no 4145 need for an intervention, so I am happy to call it "production". There are 4146 still things that need fixing but they're mostly within scope for 4147 a services refresh 4148 4149 I have embarked on "profiles" by creating a wap.nix 4150 4151 I think we could have a service module for resolvconf 4152 4153 It would be good to build a wap.nix example for the belkin and we 4154 could start looking at ubifs 4155 4156 I've lost a chunk of notes about using events to drive desired service 4157 state. There is probably only going to be one udev listener, so 4158 what if we have udev as a config key thusly 4159 4160 udev.rules = [ 4161 { 4162 match = { 4163 SUBSYSTEM = "rpmsg"; 4164 ATTR.name = "DATA5_CNTL"; 4165 }; 4166 4167 service = longrun { 4168 name = "lte-modem"; 4169 run = "blah blah blah"; 4170 }; 4171 } 4172 4173 # this one would be provided by the bridge module instead of 4174 # adding bridge member services to the default target 4175 4176 { 4177 match = { 4178 SUBSYSTEM="net"; 4179 ID_PATH="pci-0000:04:00.0"; 4180 ATTR.operstate = "up"; 4181 }; 4182 4183 service = oneshot { 4184 up = "ip link set dev $dev master $(output ${primary} ifname)"; 4185 down = "ip link set dev $(output ${member} ifname) nomaster"; 4186 } 4187 } 4188 ] 4189 4190 This works for udev/sysfs, but we want a similar architecture(sic) for 4191 user-generated target state so we could have services that run on e.g. 4192 "is the ppp0 service healthy" or not. Probably there isn't a top-level 4193 config key for each service though 4194 4195 services.wan = svc.ppoe.build { .... }; 4196 services.lte = watcher.build { 4197 watching = services.wan; 4198 match = { 4199 # an expression matching the outputs of the service 4200 # to be watched 4201 health = "failing"; 4202 }; 4203 service = oneshot { 4204 run = "start_lte_blah"; 4205 }; 4206 } 4207 4208 thing is, we could use this syntax also for sysfs watches, but not vice versa 4209 4210 ... but it's not quite the same because here we're doing static matches 4211 on contents of files, whereas the udev one is a query expression on the 4212 sysfs database. we might need that flexibiity to implement "mount the 4213 backup drive no matter _which_ damn sda_n_ device it appears as". I don't 4214 know if there's the same need for service outputs - postulate the 4215 existence of a collection of services which are all similar enough that 4216 some other service can watch them all and do $something when one of 4217 the changes state. Or a single service with very complicated outputs. 4218 For example, something could watch the snmp database and update service 4219 status depending on what it finds. Or something something mqtt... 4220 4221 we find that the "match" needs to be interpreted differently according 4222 to the thing being watched. perhaps the service being watched needs to 4223 provide a "watch me" interface somehow which accepts match criteria and 4224 outputs a true/false. Something else then needs to 4225 4226 4227 services.addmember = services.udev.watch { 4228 match = { 4229 SUBSYSTEM = "net"; 4230 ID_PATH = "pci-0000:04:00.0"; 4231 ATTR.operstate = "up"; 4232 }; 4233 4234 service = oneshot { 4235 up = "ip link set dev $dev master $(output ${primary} ifname)"; 4236 down = "ip link set dev $(output ${member} ifname) nomaster"; 4237 }; 4238 } 4239 4240 Sat Mar 2 15:37:29 GMT 2024 4241 4242 Simply put, what I think it boils down to is that we want a service 4243 which acts as an actuator or control switch for another service, 4244 and will start/stop that controlled service according to some 4245 criteria. 4246 4247 services.addmember = svc.network.ifwatch.build { 4248 interface = config.hardware.networkInterfaces.lan1; 4249 4250 # this should be part of the definition not the params 4251 service = oneshot { 4252 name = "member-${bridge}-${interface}"; 4253 up = "ip link set dev $dev master $(output ${primary} ifname)"; 4254 down = "ip link set dev $(output ${member} ifname) nomaster"; 4255 }; 4256 } 4257 4258 we could start by writing this. we need to adapt ifwait 4259 4260 Sun Mar 3 17:09:21 GMT 2024 4261 4262 this is annoyingly hard to test. the tests we'd like to write are 4263 4264 1) when it gets events that don't match the requirement, nothing happens 4265 2) when it gets an event that should start the service, the 4266 service starts 4267 3) when stop should stop 4268 4) when start and already started, nothing happens 4269 5) when stop and already stopped, nothing happens 4270 4271 what do we do if service fails to start? s6-rc will eventually reset it 4272 to "down", I think: do we need to take action? 4273 4274 Mon Mar 4 20:46:55 GMT 2024 4275 4276 # relevant but not correct for this model: https://www.forked.net/forums/viewtopic.php?f=13&t=3490 4277 4278 # power on port 5 4279 snmpset -v 1 -c private 192.168.5.14 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 integer 1 4280 4281 # power off port 5 4282 snmpset -v 1 -c private 192.168.5.14 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 integer 2 4283 4284 # toggle off/on port 5 4285 snmpset -v 1 -c private 192.168.5.14 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 integer 3 4286 4287 Wed Mar 6 18:24:29 GMT 2024 4288 4289 What happens when we attempt to start the service but it fails? We 4290 assume the start was successful so we won't try and restart it again 4291 next time we get an event that should cause it to start. 4292 4293 Thu Mar 7 11:48:26 GMT 2024 4294 4295 what next? 4296 4297 - fennel script needs to know where s6-rc is 4298 - some nix syntax 4299 - update bridge module members.nix to use the new thing 4300 4301 I can't find a ci derivation that uses the bridge. 4302 4303 Mon Mar 11 20:31:45 GMT 2024 4304 4305 Create a qemu config where wan and lan devices are bridged into a 4306 single bridge 4307 4308 start qemu paused 4309 Use qemu monitor commands to no-carrier the network devices 4310 set_link virtio-net-pci.1 off 4311 set_link virtio-net-pci.0 off 4312 4313 Boot the system 4314 4315 See if both devices are bridge members 4316 4317 See if reboot is possible 4318 4319 Use qemu monitor commands to enable the network devices 4320 set_link virtio-net-pci.1 on 4321 set_link virtio-net-pci.0 on 4322 4323 See if both devices are bridge members 4324 4325 disable again,check if back to starting position 4326 4327 4328 Wed Mar 13 00:00:16 GMT 2024 4329 4330 aside: "trigger" is the least bad word I've thought of so far for 4331 these services that stop/start other services 4332 4333 telent: yeah, in general 'ps afuxww' (or s6-ps -H :)) is the way to solve this, look for hung s6-rc processes and in particular their s6-svlisten1 children, where the command line will show what service is still waiting for readiness 4334 4335 Wed Mar 20 19:34:36 GMT 2024 4336 4337 Because I forgot hoe to rebuild rotuer, I tihnk it is time to improve 4338 support for out-of-tree configurations. So I've made 4339 modules/profiles/gateway.nix and now I can copy rotuer.nix to 4340 telent-nixos-config. 4341 4342 Probably I should make nix-build work on the top-level derivation 4343 and install liminix-rebuild as a binary? 4344 4345 would be good if an out-of-tree config could specify the device 4346 it was targeting? 4347 4348 Fri Mar 22 20:49:54 GMT 2024 4349 4350 Ideally liminix-rebuild could accept a configuration file that 4351 specifies a liminix-config file, a target hostname (maybe plus ssh 4352 port, credentials etc) and the device name. Not going to work on that 4353 just now but it does mean we can punt on specifying the device inside the 4354 liminix-config which is unreasonably circular. 4355 4356 Maybe we'll just chuck a makefile in telent-nixos-config 4357 4358 Fri Mar 22 22:14:32 GMT 2024 4359 4360 For the service failover milestone we said 4361 4362 a. A configuration demonstrating a service which is restarted when it crashes 4363 b. A failover config where service B runs iff service A is unavailable 4364 c. A config showing different pppd behaviour when interface is flakey (retry) vs ppp password is wrong (report error, wait for resolution) 4365 4366 Sun Mar 24 23:41:27 GMT 2024 4367 4368 TODO 4369 4370 1) make liminix-rebuild bounce only affected services instead of 4371 full reboot (what does it do about triggered services?) 4372 2) sniproxy 4373 4374 3) see if arhcive still works. usb disk hotplug would be a good candidate for 4375 switching to triggers 4376 4377 Mon Mar 25 19:35:47 GMT 2024 4378 4379 to make the liminix-rebuild thing restart only affected services, it needs to 4380 know when the new service is not like the old one. By default it does not 4381 restart a service with a changed up/down/run script unless the name has 4382 also changed, so we need to figure out how to generate a "conversion" 4383 file with the services that are different 4384 4385 pkgs/s6-rc-database/default.nix creates $out/compiled, we could add 4386 $out/hashes to this 4387 4388 the other thing making this fun is that we will need to run `activate` 4389 (which is usually done in preinit) otherwise the new configuration's 4390 fhs directories won't exist. 4391 4392 so the plan woyuld be 4393 4394 in liminix-rebuild, when reboot was not chosen, 4395 4396 - run activate 4397 - compare /run/s6-rc/compiled/hashes (old services) with 4398 /etc/s6-rc/compiled/hashes (new services) 4399 4400 - whenever both files have the same column 1 and different 4401 column 2, add that name to restart list 4402 4403 (need to turn restarts.fnl into a lua script) 4404 4405 s6-rc-update /etc/s6-rc/compiled/hashes restarts 4406 4407 Tue Mar 26 23:18:53 GMT 2024 4408 4409 activate overwrites /etc/s6-rc/compiled, which is a problem because 4410 s6-rc-update expects to find the old compiled database here so that 4411 it can know what to update 4412 4413 Maybe config.filesystem should specify /etc/s6-rc/compiled.new 4414 and something in early boot could symlink /etc/s6-rc/compiled to it 4415 4416 Sat Mar 30 18:41:14 GMT 2024 4417 4418 soft restart doesn't restart services that are invoked by trigger, 4419 because it has to do -p -u default so that it prunes services that 4420 were in the old config but not the new one. Ideally we need somehow 4421 to notify the trigger that it should respawn its service. Maybe 4422 we could add triggers to the force restart list, if there's a way 4423 to detect which they are? don't want to do it by adding files in 4424 the service state directory if there may be oneshot triggers. Can 4425 there be oneshot triggers? 4426 4427 The hashes file is built when we build the service database, so we 4428 could easily(?) add something in there to mark services that 4429 need poking whenever there's a restart. It's not perfect because the 4430 triggered services will be bounced unnecessarily, but remember that 4431 the alternative is a reboot ... 4432 4433 Mon Apr 1 00:18:50 BST 2024 4434 4435 i) I don't know if digressing into remote log shipping is a tangent or 4436 an important part of making services work well. 4437 4438 ii) Should there be a single "machine state" value for all of the 4439 trigger services to reference, or is it better that each trigger 4440 service has its own private state, or (third option) one state 4441 per "state source"? We previously handwaved that a state source 4442 is a service 4443 4444 services.addmember = services.udev.watch { 4445 match = { 4446 SUBSYSTEM = "net"; 4447 ID_PATH = "pci-0000:04:00.0"; 4448 ATTR.operstate = "up"; 4449 }; 4450 4451 service = oneshot { 4452 up = "ip link set dev $dev master $(output ${primary} ifname)"; 4453 down = "ip link set dev $(output ${member} ifname) nomaster"; 4454 }; 4455 } 4456 4457 4458 Tue Apr 2 19:55:25 BST 2024 4459 4460 We could do a test script for udev usb disk mounting, which uses the 4461 qemu monitor to add/remove a disk. 4462 4463 4464 ./result/run.sh --flag -device --flag usb-ehci,id=xhci --flag -drive --flag if=none,id=usbstick,format=raw,file=./stick.img 4465 4466 (qemu) device_add usb-storage,bus=xhci.0,drive=usbstick 4467 4468 Fri Apr 5 17:11:46 BST 2024 4469 4470 1) write a fennel thing that reads from the udev rebroadcast socket 4471 2) and can check sysfs for state 4472 3) set up mdevd in liminix 4473 4474 4475 Sat Apr 6 13:23:02 BST 2024 4476 4477 I wonder if we could replace preinit with an execline script? One for 4478 the TODO stack 4479 4480 4481 Sun Apr 7 14:03:29 BST 2024 4482 4483 1) we want to know what messages are sent from mdevd under various circumstances 4484 - actually, right now the only relevant circumstances are updown and inout 4485 4486 2) we might get a wider variety of messages from real hardware? 4487 4488 3) if we log the raw messages, pref. with timestamps, then we can 4489 write tests for the parsing 4490 4491 4492 therefore: write a program that opens the netlink socket and logs 4493 all data received 4494 4495 ---- 4496 4497 what's the minimum we need here? we need the inout test to open a 4498 uevent socket and use uevents to update some state that says whether the 4499 backup drive is plugged in 4500 4501 rather awkwardly, uevents don't have filesystem labels. so we also need 4502 to run blkid to find the label of each partition, and ideally we do this 4503 while the partition is present, not each time we get an event for it. 4504 4505 We have DEVNAME, DEVTYPE, SUBSYSTEM to indicate that a filesystem of interest 4506 may be present, we should use that as a trigger to scan any known 4507 4508 4509 4510 add@/devices/pci0000:00/0000:00:13.0/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1 4511 ACTION=add 4512 DEVPATH=/devices/pci0000:00/0000:00:13.0/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1 4513 SUBSYSTEM=block 4514 MAJOR=8 4515 MINOR=1 4516 DEVNAME=sda1 4517 DEVTYPE=partition 4518 DISKSEQ=2 4519 PARTN=1 4520 SEQNUM=1528 4521 4522 Some disks on loaclhost and noetbook have PARTNAME field - I assume 4523 this is because they're GPT disks. Would it actually be better to use this 4524 field than grovelling for filesystem label? 4525 4526 Tue Apr 9 21:07:50 BST 2024 4527 4528 Having waited for the appropriately labelled disk to appear, we then 4529 also have to communicate its path to the service that mounts it 4530 4531 - create a symlink 4532 - or use an instanced service 4533 4534 Creating a symlink will be fine if we can pass the symlink name as 4535 a param to fswait 4536 4537 Wed Apr 10 20:53:48 BST 2024 4538 4539 We think that fswait will evolve into a more general 4540 waiting-for-uevents tool. Maybe we could provide the matchers on the 4541 command line: 4542 4543 waituevent -l /dev/volumes/backup-disk -s mount-srv devtype=partition partname=backup-disk 4544 4545 Thu Apr 11 23:09:43 BST 2024 4546 4547 lcommit d3a2e3a4cb80b631df2ab79d463c2c4d1adef37b 4548 commit 4a58cf9335116ce673fcf08f70f3bca921a4c9ad 4549 commit afca6d4b63dd39062f02827b3c29e16904770216 4550 4551 Sun Apr 14 19:50:27 BST 2024 4552 4553 how to get this on to main: 4554 - make uevent-watcher package (it's fswait renamed) 4555 - make mount service use it 4556 - module for mdevd 4557 - add nellie (generalise for other netlink uses w/params pid/family/groups) 4558 4559 Mon Apr 15 19:59:43 BST 2024 4560 4561 plan: 4562 4563 introduce uevent-watcher command, update test to use it 4564 4565 make mount service use it 4566 4567 4568 4569 4570 Tue Apr 16 18:59:25 BST 2024 4571 4572 Another idea for maybe-not-now: tftp local/peer addresses could be 4573 provided as top-level params (e.g. to nix-build). 4574 4575 Wednesday 4576 4577 Here's irony: this doesn't work with arhcive when the disk is already 4578 plugged at boot time because mdevd-coldplug has already run by the 4579 time uevent-watch has started. Perhaps it turns out after all that we 4580 do need to be looking at state not events? (Not that we'd ever really 4581 believed otherwise but it hadn't been apparent so far) 4582 4583 4584 options 4585 4586 - we could delay coldplugd to later in boot process. Don't know how: 4587 it would have to depend on all trigger services. (Actually this 4588 is kind-of possible as they're all marked isTrigger) 4589 4590 - we could make uevent-watch look in sysfs for matches before it opens 4591 netlink socket. This would be an in-process recursive walk of sysfs 4592 reading all the uevent files, which may or may not be an improvment 4593 on having multiple mdevd-coldplug processes do a recursive walk 4594 of sysfs writing all the uevent files so that they trigger events 4595 for uevent-watch to pick up. 4596 4597 - we could construct some kind of queryable sysfs database so that 4598 all the watchers could use the same source of state, and we could 4599 make mdevd-coldplug depend on its presence. 4600 4601 Favouring option 3 as cleanest, and actually it doesn't need to read sysfs 4602 if it gets all the coldplug events. 4603 4604 4605 for each event 4606 parse into path and attributes 4607 paths[event.path] += event.attributes # only if add/change 4608 do something with indexes to make queries cheaper 4609 4610 a first guess for indices would be to index everything: 4611 4612 index[attribute.name][attribute.value] += event 4613 4614 and then throw away the indices that are useless: let's say, the 4615 ones with more than 10% of events (we can tune this). 4616 4617 To query, look at query field names and first get the corresponding 4618 index that (1) exists; (2) has the smallest number of values, then 4619 scan through that looking for paths that match on the other fields 4620 4621 We don't need to be ludicrously fast because there is probably some 4622 human event here that triggers this (disk added, network device 4623 unplugged). We don't want to use all the RAM in the world, though. 4624 Maybe 10% is too much. 4625 4626 4627 We can probably TDD the hell out of this. 4628 4629 How should we provide the query interface? Needs to be some kind of 4630 IPC, like a socket. 4631 4632 ---- 4633 4634 Could we do something quick in the meantime so I can make arhcive work 4635 again? maybe add a second mdevd-coldplug oneshot that depends on our 4636 mount service 4637 4638 4639 Wed Apr 17 18:57:49 BST 2024 4640 4641 I hatched a plan (and forgot to save this file) to build a service 4642 that subscribes to uevents and retains state so that other services 4643 can know about things that happened before they started. I'm wondering 4644 if it's really needed though, because there could be one process to 4645 read the socket and start/stop *all* the udev triggered services. Not 4646 sure how we'd describe this in nix though: how do all the other 4647 services 4648 4649 How we would do a uevent database service (sysfsq): 4650 4651 for each event e from socket 4652 4653 if e.action in (add, change) 4654 path[e.path] = e.attribues 4655 4656 if e.action == 'remove' 4657 path.remove e.path 4658 4659 (update-indices e) 4660 4661 4662 (fn update-indices [event] 4663 for each k in (keys event) 4664 index.k.v += e) 4665 4666 we also want to not maintain indexes when there are so many values in 4667 the index entry to make searching it worthless. 4668 4669 to retrieve, look at each criterion that has an index and choose the 4670 index with fewest elements in the value. scan that index for the other 4671 criteria 4672 4673 there are 813 uevent files in sysfs on arhcive, is this all overkill? 4674 maybe we could simplify using a hardcoded stopword list - e.g. don't 4675 have indices for MAJOR, MINOR 4676 4677 what are we going to use for querying? can't be netlink because that's 4678 a shared medium (broadcast/multicast). unix dgram socket? alternative 4679 would be to somehow use the filesystem as a database 4680 4681 Wed Apr 17 22:00:29 BST 2024 4682 4683 tests. assuming the sysfs setup from all-events.txt, we can write tests lik 4684 4685 - there is a path for $foo 4686 - the attributes are x, y, z 4687 4688 - when I add a device with $attributes, I can recall it 4689 - by path 4690 - by attribute value 4691 4692 - when I remove it again, I cannot access it by path or attributes 4693 4694 - when I add a device with $attributes major minor foo bar baz 4695 it is added to indices for foo bar baz but not major minor 4696 4697 - when I remove it, it can no longer be found by looking in any index 4698 4699 - when I query with multiple attributes, the search is performed 4700 using the most specific attribute (= the attribute whose 4701 value at this key has fewest elements) 4702 4703 4704 4705 I am still looking for ways to avoid doing this, but it is potentially 4706 the first of several "database" services that triggers could want to 4707 use so maybe it's an emerging pattern. 4708 4709 4710 https://github.com/philanc/minisock useful? we could almost replace 4711 nellie with it only not quite (it hardcodes 0 as the "protocol" param 4712 to socket()) 4713 4714 Fri Apr 19 20:55:22 BST 2024 4715 4716 We could have a service that's present only when a devdb entry is 4717 present. For example mount_disk only runs when partlabel=foo 4718 4719 Or we could have a service that continues to run as the $somedatabase 4720 service state changes and does different things depending on the 4721 nature of those changes. For example, [I can't think of an example 4722 now, but it was definitely an issue the other day, maybe I dreamt it] 4723 I don't think this will be such an issue for devdb becuase there isn't 4724 much in it that has continuously varying values. Maybe battery health 4725 is the exception there 4726 4727 The step ahead we're thinking here is: how do clients do a request? A 4728 single one-of request for state is fine but chances are that a client 4729 will do that to get initial state and then need to open a netlink 4730 socket to get updates: well, if we can feed them the initial state 4731 filtered for their needs why can't we send them the relevant updates 4732 as well? This makes the database server design a bit more complicated 4733 as it needs to remember each client and their subscriptions, and then 4734 send only relevant updates to each subscribed client 4735 4736 * should a client be allowed multiple subscriptions on the same 4737 connection? 4738 4739 * do we guarantee that every message sent is matching the subscription 4740 or can we send other stuff as well if it makes implementation easier? 4741 it might defeat the purpose a bit because it means the client also 4742 needs to filter, but the client will anyway have to do some message 4743 parsing so they can distinguish add from remove 4744 4745 * where do we start? 4746 4747 Sun Apr 21 13:31:48 BST 2024 4748 4749 We have the mechanics of it working (albeit implemented in the 4750 simplest possible terms), we need to glue it to some I/O 4751 4752 1) open a netlink socket and read the events from it 4753 4754 2) "create a PF_UNIX socket of type SOCK_STREAM, and accept connections on it, then each time you accept a connection, you get a new fd" 4755 4756 - accept connection 4757 - read terms from it 4758 - register callback that writes event to connected socket 4759 4760 minisock has no support for "test if fd is ready" or "wait for [fds] 4761 to become ready", either we need poll() or we could add a call for "is 4762 this fd ready to read" and use coroutines. Fork minisock or add as 4763 another library? 4764 4765 [ if we fork minisock we could expose the protocol param to Lua 4766 so we could use it for netlink ] 4767 4768 Tue Apr 23 19:13:45 BST 2024 4769 4770 we could convert from minisock to lualinux. if we can also use that to 4771 get rid of nellie and/or lfs, the size tradeoff is minimal 4772 4773 --- 4774 4775 Is there some way we could test the devout event loop? 4776 4777 I can register a fd with a callback 4778 when the fd is ready, my callback is called 4779 when the callback return true it remains registered 4780 when the callback return true it is unregistered and the fd is closed 4781 4782 loop.register 4783 loop.registered? 4784 loop.feed 4785 4786 Tue Apr 23 20:34:03 BST 2024 4787 4788 I think we could make the event loop abstraction leak less? 4789 It's not actually a _loop_, all the actual GOTO 10 happens 4790 outside of it 4791 4792 [X] 1) see if we can do netlink in lualinux 4793 [X] 2) if so, convert it to lualinux 4794 [X] 3) add netlink socket to event loop 4795 [X] 4) make it send messages to subscribers 4796 [X] 5) package it 4797 [X] 6) make uevent-watcher use it instead of netlink directly 4798 [X] 7) write an inout test variant that has the stick inserted 4799 at boot time already 4800 4801 I'm also thinking we could wrap the raw fds from lualinux into small 4802 objects with read and close methods? It would make testing easier if 4803 nothing else - also use of with-open. Maybe do that in anoia. 4804 4805 when a subscriber connects we need to send them their matching current 4806 state before subscribing them [ needs a test ] 4807 4808 figure out what event format the subscribers want? lua-ish or send the 4809 same messages as udev would? If we're going to send the originals, 4810 should we store them alongside the parsed, or reconstruct from parsed? 4811 4812 Sat Apr 27 21:52:11 BST 2024 4813 4814 We have a passing inout test. Next thing to do is try it on 4815 the actual arhcive hardware 4816 4817 Next big thing is some kind of failovery service. Almost-obvious 4818 candidate is LTE failover with aaisp l2tp tunnel 4819 4820 Tue Apr 30 23:27:30 BST 2024 4821 4822 I want to connect my new ip camera to arthur without letting it reach the 4823 internet, or the internet reach it. 4824 4825 we could plug it into a gl.inet box running dhcp server on lan 4826 and client on wan, then use NAT to expose the camera's http and rtsp 4827 ports on whatever address it has on the wan interface 4828 4829 Tue May 7 22:23:49 BST 2024 4830 4831 If we want to build a config with an l2tp upstream, it needs an 4832 underlying dhcp interface not pppoe as we can't use the bordervm l2tp 4833 account simultaneously. Having bordervm do dhcp might be quite useful 4834 anyway for other applications, although it will have to double-nat to 4835 the internet. We could give it an aaisp /64 and have routable ipv6 but 4836 maybe that's a level of faff too high. 4837 4838 Given that we can build xl2tpd and a service for it. 4839 4840 4841 're using the same l2tp account for thingy that we use to simulate ppp, 4842 we need an upstream which is not ppp 4843 4844 We need a less shit coldplug that copes with filenames containing spaces (!) 4845 4846 Fri May 10 00:33:14 BST 2024 4847 4848 Getting xl2tp hackily running turned out to be not a lot of work. However, 4849 we need to figure out routing 4850 4851 - we need a route on lan device to the dns to lookup l2tp.aaisp.net.uk 4852 - we need a route on lan device to l2tp.aaisp.net.uk 4853 4854 also it doesn't die when the tunnel closes, which is a bit shit 4855 4856 maybe this is where we lean into health check services 4857 4858 a health check service is just a service that watches another service 4859 and kills it if it's not healthy. 4860 4861 for xl2tpd, "not healthy" is "there is no ppp process" or "there is no 4862 tunnel" or "the tunnel has no sessions". I don't know how we 4863 (robustly) test for no ppp process associated with the l2tp peer 4864 4865 when ppp quits, does the tunnel come down? 4866 in xl2tld.c child_handler we respond to sigchld by closing c->fd 4867 and setting it to -1 4868 4869 Sat May 11 17:55:04 BST 2024 4870 4871 A better way to monitor the connection health would be to ping a 4872 computer on the internet (preferably one that doesn't mind being 4873 pinged). If we combine autodial with "is $isp still there" then we 4874 should have something fairly robust. 4875 4876 xl2tpd spawns pppd, we should equip it with config that writes the 4877 ppp outputs (ip address etc) to the xl2tp service directory so 4878 that it can be used like a regular ppp. This will also make 4879 it possible to have the health check work by pinging the peer address 4880 4881 Sun May 12 22:33:09 BST 2024 4882 4883 sleep until the interface is probably up 4884 failure counter = 0 4885 loop indefinitely 4886 get outputs/peer-address of watched ppp service 4887 ping it 4888 if ok 4889 reset failure counter 4890 else 4891 increment failure counter 4892 fi 4893 if failure counter > threshold 4894 bounce the ppp service 4895 exit, if previous action didn't do that already 4896 end 4897 sleep(check interval) 4898 end loop 4899 4900 4901 # ps ax | grep l2tp 4902 72 root 1316 S s6-supervise l2tp.aaisp.net.uk.l2tp 4903 73 root 1316 S s6-supervise l2tp.aaisp.net.uk.l2tp-log 4904 122 root 1428 S {run.user} /bin/sh ./run.user l2tp.aaisp.net.uk.l2tp 4905 1099 root 1428 S {run.user} /bin/sh ./run.user l2tp.aaisp.net.uk.l2tp 4906 1102 root 1104 S {xl2tpd} /nix/store/i1bbqh7vybam03l6jzf4sm4np3k4ack5 4907 1115 root 1420 S grep l2tp 4908 # s6-rc -d change l2tp.aaisp.net.uk.l2tp 4909 # ps ax | grep l2tp 4910 72 root 1316 S s6-supervise l2tp.aaisp.net.uk.l2tp 4911 73 root 1316 S s6-supervise l2tp.aaisp.net.uk.l2tp-log 4912 122 root 1428 S {run.user} /bin/sh ./run.user l2tp.aaisp.net.uk.l2tp 4913 1102 root 1104 S {xl2tpd} /nix/store/i1bbqh7vybam03l6jzf4sm4np3k4ack5 4914 1122 root 1420 S grep l2tp 4915 4916 4917 Mon May 13 19:45:59 BST 2024 4918 4919 We need to do the usb id swithcing dance thing for the lte modem. 4920 At startup it's 12d1:14fe, which is "mass storage mode", although the 4921 disks seem to disappear as soon as they appear which is weird 4922 4923 probably the mode switch should be triggered by device insertion 4924 4925 usb_modeswitch -v 12d1 -p 14fe --huawei-new-mode 4926 4927 https://github.com/pixelspark/tymodem?tab=readme-ov-file 4928 4929 4930 Tue May 14 21:58:25 BST 2024 4931 4932 [ we didn't need this. the first form is the default, the second 4933 is what something on the internet said we should change it to, the 4934 third is setting it back to default ] 4935 4936 ^SETPORT:A1,A2;12,1,16,A1,A2 4937 AT^SETPORT="FF;12,16" 4938 AT^SETPORT="A1,A2;12,1,16,A1,A2" 4939 4940 4941 4942 Wed May 15 21:55:11 BST 2024 4943 4944 we can use uevent-watch to look for devtype=usb_device product=12d1/14fe/102 4945 and trigger a oneshot that runs usb-modeswitch 4946 4947 we can use uevent-watch to look for devtype=usb_device product=12d1/1506/102 4948 and trigger a oneshot that runs the AT commands 4949 4950 if wwan0 is a triggered service how can dhcp depend on it? arse 4951 4952 - we can get reverse dependencies from s6-rc-db, so the sematics of 4953 starting a triggered service could include starting everything it 4954 enables 4955 4956 - we don't want to inadvertently start it on boot by putting it in the 4957 global config.services 4958 4959 Thu May 16 09:09:44 BST 2024 4960 4961 we could do something cleverish with the config.services at build time 4962 by stripping from it everything depending on a trigger. but then how 4963 _do_ they get started? the intent of putting it in config.services 4964 is that it will be started when conditions are suitable. 4965 4966 can we: go through each service in config.services, detect the trigger 4967 that started it, and add it to a bundle named for that trigger? 4968 4969 we need something in the triggering service to mark the triggered 4970 service as not-for-boot, and then to apply that transitively to 4971 everyting depending on it 4972 4973 I don't think we have common code for triggers, so either we need to 4974 add some or put this marking in all of the current examples 4975 4976 Wed May 22 22:29:46 BST 2024 4977 4978 # cat /sys/bus/usb/devices/1-1.2:1.2/uevent 4979 DEVTYPE=usb_interface 4980 DRIVER=huawei_cdc_ncm 4981 PRODUCT=12d1/1506/102 4982 TYPE=0/0/0 4983 INTERFACE=255/2/22 4984 MODALIAS=usb:v12D1p1506d0102dc00dsc00dp00icFFisc02ip16in02 4985 4986 # cat /sys/bus/usb/devices/1-1.2:1.0/uevent 4987 DEVTYPE=usb_interface 4988 DRIVER=option 4989 PRODUCT=12d1/1506/102 4990 TYPE=0/0/0 4991 INTERFACE=255/2/18 4992 MODALIAS=usb:v12D1p1506d0102dc00dsc00dp00icFFisc02ip12in00 4993 4994 neither of these is a tty. however, there's a ttyUSB0 4995 diretory in there - 4996 4997 # cat /sys/bus/usb/devices/1-1.2:1.0/ttyUSB0/uevent 4998 DRIVER=option1 4999 5000 # cat /sys/bus/usb/devices/1-1.2:1.0/ttyUSB0/tty/ttyUSB0/uevent 5001 MAJOR=188 5002 MINOR=0 5003 DEVNAME=ttyUSB0 5004 5005 5006 We can query devout using s6-ipcclient: 5007 5008 $ s6-ipcclient -v /run/devout.sock sh -c "echo devtype=wwan >&7; cat <&6 >&2" 5009 5010 5011 we're going to need to expose sysfs parent attributes in devout, 5012 so that we can tie together vendor/product and tty name 5013 5014 - they don't come from netlink socket so we'll have to read the 5015 filesystem 5016 - we don't know if we can cache them indefinitely or if they change 5017 - we also will want to match on parent attributes 5018 - per https://www.kernel.org/doc/html/v4.16/admin-guide/sysfs-rules.html 5019 we should do that by getting the parent directory, not by 5020 following symlinks 5021 5022 I think we are going to document "a rule depending on attrs won't 5023 trigger if those attrs change silently without a uevent". attrs for a 5024 path are rescanned only when a event for that path (or a child?) 5025 is received. 5026 5027 we need to avoid using stdio to read sysfs attributes because of the 5028 need for atomicity 5029 5030 Sun May 26 12:24:45 BST 2024 5031 5032 cd / 5033 mkdir -p /tmp/sys/devices 5034 find /sys/devices -type f | while read F ; do 5035 D=/tmp/$(dirname $F) 5036 test -d $D || mkdir -p $D 5037 test -r $F && cat $F > /tmp/$F 5038 done 5039 5040 Tue May 28 20:58:58 BST 2024 5041 5042 To make testing easier, we pass the /sys mount point as a param 5043 to (sysfs) via (database) 5044 5045 This means that for event:match to work with attributes, the event 5046 also has to point back to the database, or at least to sysfs. However 5047 we'd like to do the attr tests after the regular uevent key tests 5048 and the attrs tests after even that, because they're that mich slower. 5049 5050 ----- 5051 5052 Let's change it up. The database contains kobjects not events. A 5053 kobject knows its mountpoint as well as its path, and implements the 5054 attr and attrs methods 5055 5056 5057 Sun Jun 2 20:58:47 BST 2024 5058 5059 we have a new uevent-rule service that can start/stop its controlled 5060 service based on uevent fields and sysfs attrs for the corresponding 5061 device/any ancestor of its sysfs path 5062 5063 we need 5064 - a way of starting _dependent_ services of the controlled service: 5065 we want to activate that whole sub-branch of the dependency tree 5066 5067 - a way to not attempt to start dependents of the controlled service 5068 at boot time: we don't want to put them in the default bundle if 5069 they depend on stuff that's part of the default service set. 5070 5071 what happens when a service depends on _two_ controlled services? 5072 we want to start it only when _both_ of them are up. That discourages 5073 any idea of creating a bundle for each controlled service and its 5074 dependants 5075 5076 Tue Jun 4 19:07:44 BST 2024 5077 5078 we have to do it at runtime. I think we can add a file in the service 5079 directory to identify that it's triggered (i.e. only run in response 5080 to an event, we could use better terminology here( 5081 5082 5083 for s in the reverse deps of $triggered 5084 unless any? (s1: { s1.triggered && s1.down } ) 5085 s.dependencies 5086 start s 5087 5088 we can do this in ci, maybe adapt the inout test 5089 5090 two triggered services, A with a uevent that does happen 5091 and B which doesn't 5092 5093 two dependent services; C depends on A only and D on A+B. 5094 5095 each should write an output 5096 we check that the output from C is present but not the one from D 5097 5098 5099 5100 Tue Jun 4 22:22:20 BST 2024 5101 5102 We can't have dependencies of triggered services unless we expose the 5103 triggered service instead of the watcher from the module that sets up 5104 uevent-watch. otherwise the user will be declaring their dependencies 5105 on the watcher and they will start when the watcher does. We need to 5106 invert it. 5107 5108 Bother. 5109 5110 services.mount-foo = oneshot { 5111 name = "blah"; 5112 up = "mount /dev/disk/by-partlabel/mydisk"; 5113 runCondition = conditions.sysfs { 5114 terms = { partlabel = "blah"; }; 5115 symlink = "/dev/disk/by-partlabel/mydisk"; 5116 } 5117 } 5118 5119 To process this, we need to 5120 5121 - remove it from the default bundle 5122 - create a service that _is_ in the default bundle which 5123 starts this one when runCondition is true 5124 5125 5126 How would it look for service conditions other than uevents 5127 5128 Thu Jun 6 21:49:10 BST 2024 5129 5130 runCondition can be a function that accepts the service to be 5131 watched and returns a service that does the watching. The 5132 derivation calls this function and makes the watched service 5133 depend on the watcher. 5134 5135 When adding services to the default bundle (wherever that happens, I 5136 no longer remember) we need to check if the service has a controller 5137 and add that instead of the service itself. 5138 5139 s6-ipcclient -v /run/devout.sock sh -c "echo subsystem=tty attrs.idVendor=12d1 >&7; cat <&6 >&2" 5140 5141 Sun Jun 9 22:55:05 BST 2024 5142 5143 services which depend on services that have controllers should not be 5144 added to default bundle, otherwise they will try to start the controlled 5145 service before its ready 5146 5147 Tue Jun 11 20:00:04 BST 2024 5148 5149 we can't look in /run/service/name to see if it's controlled, because 5150 if it's a oneshot that directory won't exist. likewise /run/s6-rc/servicedirs/ 5151 5152 we could add the controller to the dependencies, then 5153 controlled? = (any dependency is a controller) 5154 5155 what about generating a script at build time that has the knowledge 5156 baked in? 5157 5158 5159 to_start = reverse deps of service 5160 print "to_start=$to_start" 5161 for each service s1 in to_start 5162 if isControlled s1 5163 rd = reverse deps of s1 5164 print "test_stopped s1 && not_to_start="$not_to_start $rd" 5165 print '' 5166 for s in $to_start; do 5167 case " $not_to_start " in 5168 *" $s "*) true;; 5169 *) s6-rc -u change $s ;; 5170 esac 5171 done 5172 '' 5173 5174 --- 5175 5176 would it help to make an empty bundle called "controlled" and have all 5177 controlled services depend on it? no, because the condition for 5178 skipping a service is that it depends on a _different_ controlled 5179 service that's down 5180 5181 s6-rc -u change $service 5182 for s in $(s6-rc-db -d all-dependencies $service); do 5183 if (s6-rc-db all-dependencies $s | grep controlled); do 5184 # do nothing: if it's stopped, we don't want to start it, 5185 # and if it's already running there's no need to. 5186 # XXX what if it's running but its children are not? 5187 # XXX 2 this fails as written because _every_ service $s depends 5188 # on the controlled service $service 5189 else 5190 start s 5191 5192 "[service] Names cannot be duplicated and cannot contain a slash or a 5193 newline; they can contain spaces and tabs, but using anything else 5194 than alphanumerical characters, underscores and dashes is discouraged" 5195 5196 would this be simpler in fennel? 5197 5198 (each [l (lines (io.popen (%. "s6-rc-db -d all-dependencies %s" service)))] 5199 (if (not (controlled-ancestor l)) 5200 (tset to-start l true))) 5201 5202 Sat Jun 15 08:51:21 BST 2024 5203 5204 # do this at boot 5205 for s in $(s6-rc-db -d dependencies controlled); do 5206 mkdir -p /run/services/controlled 5207 touch /run/services/controlled/$s 5208 done 5209 5210 5211 # now we can do this to start a service tree 5212 5213 for controlled in $(cd /run/services/controlled/ && echo *); do 5214 if down $controlled; then 5215 blocks="$blocks $controlled " 5216 fi 5217 done 5218 5219 for s in $(s6-rc-db -d all-dependencies $service); do 5220 for dep in $(s6-rc-db all-dependencies $s) 5221 case "$blocks" in 5222 "* $dep *") 5223 : don't start 5224 ;; 5225 *) 5226 s6-rc -u change $s 5227 ;; 5228 esac 5229 done 5230 done 5231 5232 Sun Jun 16 23:13:53 BST 2024 5233 5234 what we are trying to do is set up an l2tp by hostname 5235 5236 1) this means looking up the hostname in the dns 5237 2) this means having a route to the dns server 5238 3) this means parsing the space-separated list of dns servers 5239 provided by dhcp 5240 5241 we could write the servers each into their own file, but that 5242 helps less than you'd think unless we give those files predictable 5243 names 5244 5245 Thu Jun 20 10:16:52 BST 2024 5246 5247 now we have l2tp-over-wwan, we need to do the failover mechanism 5248 5249 - can't have both l2tp and pppoe running at once (at least for aaisp) 5250 because same creds used for both and starting l2tp will cause them 5251 to route all traffic to the l2tp instead of the FTTx 5252 5253 - we could have the wwan stick permanently configured and ready to go, 5254 as long as we're not actvely using it unless the main connection is 5255 b0rked 5256 5257 - can we have the same odhcp stuff running and point it to either? 5258 maybe renaming the wan interface would be an easy-ish way to do this 5259 5260 we need some kind of health check on the main connection that will 5261 bring up the backup if e.g. packet loss over x%. Or is lcp echo good 5262 enough here? for multipath to the same backhaul, if some weird routing 5263 cockup makes google unavailable from the main connection it will most 5264 likely also be unavailable from the backup, so lcp echo is arguably better 5265 5266 5267 on a side note, use of shell functions to get the output from another 5268 service is a bit icky 5269 5270 Fri Jun 21 21:05:21 BST 2024 5271 5272 We can have a controller with two controlled services, which runs the 5273 second one when the first one isn't working. 5274 5275 how do we connect the dependent services (dhcp pd, defaultroute, anything 5276 else dependent on wan) to the correct upstream? 5277 5278 we can't use bundles because bundles just flatten to atomic services, there's 5279 no either/or there 5280 5281 controller 5282 - main service 5283 - backup service 5284 - proxy service 5285 5286 The proxy service is running when one of the main or backup services is 5287 up. It provides all the outputs of whichever backend service is active 5288 5289 https://skarnet.org/software/s6/s6-svwait.html 5290 5291 proxy could use "s6-svwait -U -o main backup" to wait for one of the two 5292 backend services, provded that both are longruns 5293 5294 so in the controller we start main-service, and if/when that fails start 5295 backup-service. we run proxy-service if any of the backend services is 5296 running, and use its outputs to indicate which. 5297 5298 the proxy could just symlink to the backing service outputs directory, 5299 or it could copy and translate if the main and backup services have 5300 different outputs, so that it presents a common interface. I'm not 5301 sure proxy is the best name but I haven't thought of a better. 5302 5303 we can do a manual switch back to main-service by restarting the 5304 controller. we could do an automatic switch by adding logic to the 5305 controller to make it restart itself. 5306 5307 perhaps the controller has an output that indicates which backend is 5308 active, then the proxy just needs to look at that to figure which one to 5309 use. 5310 5311 while true; do 5312 if s6-rc -u change $primary; then # will wait until succeeded, or exit 1 if timeout 5313 ln -sf $primary outputs/active 5314 s6-rc -u change $proxy 5315 elif s6-rc -u change $secondary; then 5316 ln -sf $secondary outputs/active 5317 s6-rc -u change $proxy 5318 else 5319 rm outputs/active 5320 s6-rc -d change $proxy 5321 fi 5322 # wait for the backend to die (down cleanup will 5323 # remove outputs directory) 5324 while test -d outputs/active/.outputs 5325 inotifywait outputs/active/.outputs 5326 fi 5327 rm outputs/active 5328 s6-rc -d change $proxy 5329 end 5330 5331 this script will when when primary dies, attempt to start primary: if 5332 it doesn't come up, start secondary 5333 5334 if the primary comes up and then goes down later, we'll start it 5335 again - which isn't what we want. When the primary dies, we 5336 want to try the secondary next 5337 5338 backends="primary secondary tertiary etc" 5339 rest=$backends 5340 while true ; do 5341 first="${rest%% *}" 5342 rest="${backends#* }" 5343 if test -n "$first"; then 5344 if s6-rc -u change $first; then 5345 ln -sf $first outputs/active 5346 s6-rc -u change $proxy 5347 5348 while test -d outputs/active/.outputs 5349 inotifywait outputs/active/.outputs 5350 fi 5351 fi 5352 rm outputs/active 5353 s6-rc -d change $proxy 5354 else 5355 rest=$backends 5356 fi 5357 done 5358 5359 in this version when the secondary dies then we try the third backend 5360 (round-robin). are there circumstances where we'd rather retry the primary? 5361 Presumably there are circumstances where we would _not_ rather 5362 retry the primary, otherwise why are we even providing a tertiary? 5363 If we could answer that question then we'd know. 5364 5365 5366 Mon Jun 24 21:22:34 BST 2024 5367 5368 the controller needs to know the names of its backends, which is ugly 5369 if they're computed names because we can't define the services themselves 5370 first without their references to the controller 5371 5372 mutual recursion ... maybe it's time to understand how this fixpoint 5373 thing works 5374 5375 Wed Jun 26 22:16:25 BST 2024 5376 5377 s6 will restart the pppoe service when it dies, and keep doing this 5378 indefinitely - unless the ./finish script returns 125. Note that this 5379 is only true for longruns, but it's not as though oneshots can die 5380 anyway as there's no process to fail. 5381 5382 Sat Jun 29 21:43:10 BST 2024 5383 5384 > s6-supervise says it restarts the supervised process when it exits 5385 "unless told not to"; however s6-rc talks about "failed 5386 transitions": if a s6-rc service doesn't signal readiness before 5387 timeout-up expires, it is stopped and won't be restarted. I *think* 5388 the behaviour I am observing is that ./run may be invoked several 5389 times if it dies without ever signalling readiness, and then it's 5390 killed when the timeout is exceeded 5391 5392 5393 ... so ... that's OK, probably. pppoe will stop running after n 5394 lcp-echoes time out 5395 5396 ---- 5397 5398 inotifywait apparently requires c++ and libgcc and transitively the 5399 kitchen sink, which is a bit silly as we have linotify in lua. So 5400 we should replace the failover scripty thing with a lua program 5401 5402 (table.concat rdepends ", ") 5403 5404 5405 Fri Jul 5 21:21:18 BST 2024 5406 5407 5408 1970-01-01 00:01:00.797696621 wan-switcher blocks ( modem-modeswitch, modem-atz, wan.link.pppoe, 194.4.172.12.l2tp, wan-proxy ) rdepends ( 194.4.172.12.l2tp ) start ( 194.4.172.12.l2tp ) 5409 5410 5411 why is it starting l2tp when it should depend on having a route to the 5412 l2tp server 5413 5414 Sat Jul 6 14:24:26 BST 2024 5415 5416 The logic for up-tree is not correct, as it assumes that the 5417 requested service is itself ready to start (so excludes it from 5418 the blocked list). If the requested service is dependent on 5419 some other block, it should not be started. 5420 5421 [ I am confused. Isn't this what happens already? ] 5422 5423 5424 @40000000000000441b51b24c wan-switcher blocks ( modem-atz, modem-modeswitch, 194.4.172.12.l2tp, wan.link.pppoe, wan-proxy ) rdepends ( 194.4.172.12.l2tp ) start ( 194.4.172.12.l2tp ) 5425 5426 5427 # s6-rc-db all-dependencies 194.4.172.12.l2tp 5428 route-05029a9e8e2c-ee8d76f34e9c 5429 hostname 5430 modem-atz 5431 modem-modeswitch 5432 wwan0.link 5433 check-lns-address 5434 resolve-l2tp-server 5435 controlled 5436 route-07d8f171cb5a-ee8d76f34e9c 5437 wwan0.link.dhcpc 5438 wwan0.link.dhcpc-log 5439 194.4.172.12.l2tp-log 5440 194.4.172.12.l2tp 5441 s6rc-fdholder 5442 s6rc-oneshot-runner 5443 5444 Wed Jul 10 23:37:00 BST 2024 5445 5446 I propose rewriting the admin section. Topics we need to cover 5447 5448 * building liminix given a configuration 5449 * installing for the first time 5450 - refer to hardware section to find which of the following apply 5451 - installation from openwrt 5452 - installation from boot monitor 5453 * upgrading 5454 - when you have a writable filesystem 5455 - using levitate 5456 * using the running system (services, logs) 5457 5458 we also need to expunge all mention of kexec 5459 5460 and mention the upgrade choices in the Configuration section so 5461 people don't build an unupgradable image and only find out later 5462 5463 Fri Jul 12 21:20:00 BST 2024 5464 5465 generalising the failover example: 5466 5467 - usb stick may or may not need a modeswitch 5468 - may need a different chat script 5469 - usb ids 5470 5471 Mon Jul 15 17:52:57 BST 2024 5472 5473 DONE 1) Should round-robin be a callService service or a function a la 5474 longrun/oneshot, or even an overridable package? 5475 5476 DONE 2) maybe we should replace all liminix.callService with 5477 config.system.callService 5478 5479 3) for consistency, can we make the networking "primitives" into 5480 services? answer: no. the only thing left there is `ifup` which is a 5481 function returning a string, not a derivation 5482 5483 Tue Jul 16 18:25:41 BST 2024 5484 5485 can we make the gateway profile able to use failover? perhaps if we 5486 add username and password as options to the pppoe service, then call 5487 gateway with the pppoe service instead of building it _in_ the profile, 5488 we can have gateways with other-than-pppoe for the wan 5489 5490 (for a straight lte uplink, could pass the wwan interface as wan) 5491 5492 Fri Aug 2 20:18:38 BST 2024 5493 5494 Some thoughts about secrets 5495 5496 1) clevis/tang is a mechanism to have encrypted local secrets that 5497 can't be decrypted unless a particular network host is present. This 5498 means we can reboot it unattended without having plaintext on the device, 5499 but it doesn't address getting the secrets onto the device. 5500 5501 when is this useful? 5502 5503 - someone steals the router and doesn't steal the tang server, they can't have my secrets 5504 5505 when not? 5506 5507 - someone compromises the machine, gets root, and looks in /run 5508 - doesn't address key rotation 5509 5510 2) hashicorp vault or something like it can download secrets from 5511 a networked server - then we just plonk them in /run. But I don't want 5512 to pay for vault itself, so "something like" are the key words there 5513 5514 3) Is sops relevant here? could we keep secrets in a big sops json/yaml 5515 and serve the decrypted file over https? 5516 5517 ==== so we have a plan, I think ===== 5518 5519 1) a secrets service that retrieves the secrets from some directory1 5520 that isn't /nix/store and copies them to its outputs with minimal 5521 permissions. Services that need secrets can depend on the secrets 5522 service and be restarted when secrets change 5523 5524 1b) a service that monitors changes in secrets (e.g. using inotify 5525 on /run/services/outputs/foo/bar) potentially doesn't need to restart 5526 when the secrets change, so how do we know which ones to pass over? 5527 This is a performance optimisation not a correctness issue 5528 5529 2) as (1) but the secrets are encrypted and we use clevis to decrypt 5530 5531 3) a secrets service that fetches the decrypted secrets as a JSON file 5532 using HTTPS. We can use this ourselves with SOPS and it will be easy 5533 to adapt for Vault users. 5534 5535 (What about fetching _encrypted_ secrets with https and then 5536 decrypting locally? I don't think we can do this with clevis because 5537 the machine that encypts has to be the machine that decrypts (ICBW). 5538 5539 Sat Aug 3 22:28:24 BST 2024 5540 5541 It would be useful maybe if the secrets could survive a reboot. Fetch 5542 using HTTPS and then store locally using clevis. But actually all that 5543 does is mean we've shifted from "can see the secrets server" to "can 5544 see the tangd server" so maybe not especially useful 5545 5546 * We can't put passwords in the secrets unless we have a service that 5547 will change /etc/passwd if they change * 5548 5549 we need a service that does the HTTPS call, parses the JSON response 5550 and writes it as nested subdirectories 5551 5552 can we encode permissions and ownership into the file? should we? 5553 will some other secret store (say, Vault) know how to encode permissions 5554 in the same way as we arbitrarily choose to? 5555 5556 5557 Tue Aug 6 18:41:16 BST 2024 5558 5559 I would like to know 5560 5561 - why the docs aren't being built 5562 - min-copy-closure error: "cpio: unsupported cpio format, use newc or crc" 5563 - can we avoid rebuilding this crapload of build packages? 5564 5565 Wed Aug 7 18:36:09 BST 2024 5566 5567 * a lua script that downloads a json file and turns it into outputs 5568 * a service that runs the script and then pauses for 30 minutes 5569 * the service will be restarted when it exits 5570 5571 export SOPS_AGE_KEY=$(age -d key.age) ; sops -a age1vearrjhv4x4cw6rfg2hdgqp46p4k673avezk3td5rd9ktrcrmslsljjsfq -e secrets.yaml > secrets.enc.yaml 5572 5573 EDITOR="emacs -nw" SOPS_AGE_KEY=$(age -d key.age) sops secrets.enc.yaml 5574 5575 Fri Aug 9 21:51:18 BST 2024 5576 5577 we have a service that periodically fetches a json and writes the values 5578 to its outputs 5579 5580 we need to figure how to *use* that data 5581 5582 - services that can't look in a file for their secrets might need a config 5583 file to be rewritten 5584 - service may need restarting to pick up a changed secret 5585 - maybe service accepts secrets using environment variables (see also 5586 previous point) 5587 5588 we already have a mechanism for watching service output changes, it's the 5589 thing we use for picking up dhcp6 config 5590 5591 it doesn't do the diff for you, you have to remember the old value and 5592 see for yourself if the change is useful. 5593 5594 what we'd like is something like this: 5595 5596 svc.secret-watcher.build { 5597 source = config.services.secret-service; 5598 watch = ["wlan" "telent5"]; 5599 service = svc.hostapd { 5600 params = { 5601 # .... 5602 wpa_passphrase = "$(output secret-watcher "wlan/telent5/wpa_passphrase")"; 5603 }; 5604 }; 5605 } 5606 5607 but output is a shell function, so how do we get this substituted into 5608 the config file? something at runtime needs to rewrite the config file 5609 into /run and interpolate the values. 5610 5611 the hostap service "run" script, before starting hostapd, needs to 5612 copy the config file from the store into /run/somewhere and 5613 interpolate secrets. 5614 5615 we could have a reasonably general command to do interpolation 5616 5617 echo 'wpa_passphrase={[ wpa_passphrase ]}' | \ 5618 patch-secrets /run/services/outputs/secrets-service/wlan/telent5 {[ ]} \ 5619 > /run/services/state/${name}/hostapd.conf 5620 5621 5622 The values might need quoting/escaping, and the quoting rules will 5623 depend on the format of the file that needs to be generated. What if 5624 we do an Erb-style thing and evaluate the bit inside quotes as 5625 Lua - then we can provide any kind of escapes needed as lua functions 5626 5627 wpa_passphrase={[ string.format("%q", wpa_passphrase) ]} 5628 5629 We could for convenience provide squote(), dquote() etc functions 5630 but the necessary rules for escaping might vary. How about 5631 having shell() or json() or ? (what else? html?) functions that 5632 format and escape per the encoding rules for that language? 5633 5634 5635 string.gsub(template_string, "%{%[.-%]%}", function(x) 5636 load(x, x, "t", myenv) 5637 end 5638 5639 Sat Aug 10 23:43:15 BST 2024 5640 5641 Every service that can be configured with secrets (at least, that uses 5642 a configuration file) will need to be altered to interpolate at 5643 startup 5644 5645 Any service that passes params on the command line may be able to 5646 use the "$(output " syntax still, but it does feel brittle (it always did) 5647 5648 will we see any kind of pattern emerge so that we can provide 5649 secrets-interpolation for config files in one place instead of 5650 everywhere? 5651 5652 svc.secret-watcher.build { 5653 source = config.services.secret-service; 5654 watch = ["wlan" "telent5"]; 5655 service = svc.hostapd.build { 5656 params = { 5657 # .... 5658 wpa_passphrase = "{{ $(output secret-watcher "wlan/telent5/wpa_passphrase")"; 5659 }; 5660 }; 5661 } 5662 5663 how does the watcher communicate to the inner service that it needs secrets 5664 from x place? 5665 5666 svc.secret-watcher.build { 5667 source = config.services.secret-service; 5668 watch = "wlan/telent5"; 5669 service = svc.hostapd.build { 5670 secrets = config.services.secret-service; 5671 params = { 5672 # .... 5673 wpa_passphrase = "{{ $(output secret-watcher "wlan/telent5/wpa_passphrase")"; 5674 }; 5675 }; 5676 } 5677 5678 or something like 5679 5680 let 5681 secret = name: get-output config.services.secret-service name; 5682 in svc.secret-watcher.build { 5683 watch = "wlan/telent5"; 5684 service = svc.hostapd.build { 5685 params = { 5686 # .... 5687 wpa_passphrase = secret "wlan/telent5/wpa_passphrase"; 5688 }; 5689 }; 5690 } 5691 5692 which is transformed into some kind of attrset that the service can 5693 interrogate and figure out how to interpolate? this would be an improvement 5694 as the knowledge of what kind of quoting to use is within the service 5695 5696 A reasonable question would be what happens if we reference outputs 5697 from more than one service. Honestly I'd be happy to not support it 5698 but it's made quite easy by this form of syntax 5699 5700 Mon Aug 12 19:42:48 BST 2024 5701 5702 what about if when we build the output template we'd have something 5703 like this: 5704 5705 wpa_passphrase={{ 5706 json_quote(output("/nix/store/eeeee-servicename/.outputs", "foo/bar")) 5707 }} 5708 5709 which it will get partly from its own knowledge and partly from 5710 the thing that called it 5711 5712 5713 let 5714 literal_or_output = o: 5715 if builtins.typeOf(o) == "string" 5716 then builtins.toJSON o 5717 else "output(${builtins.toJSON o.service}, ${builtins.toJSON o.path})" 5718 in 5719 '' 5720 wpa_passphrase={{ 5721 json_quote(${literal_or_output(wpa_passphrase)$}) 5722 }} 5723 '' 5724 5725 builtins.toJSON is not the "correct" quoting regime for Lua strings, 5726 but it's sufficient for printable ascii, and using unprintable 5727 characters in Nix strings is asking for trouble in the first place 5728 5729 Tue Aug 13 18:37:59 BST 2024 5730 5731 next thing is secret-watcher service 5732 5733 svc.secret-watcher.build { 5734 watch = { service = config.services.secrets; path= "wlan/telent5"; }; 5735 service = svc.hostapd.build { 5736 params = { 5737 # .... 5738 wpa_passphrase = {service= config.services.secrets; path= "wlan/telent5/wpa_passphrase"}; 5739 }; 5740 }; 5741 action = "restart"; # or "sighup" or "stop-start" or ? 5742 } 5743 5744 we can implement this using the same output watching thing as acquire-*.fnl 5745 use 5746 5747 - a fennel script that opens the service and calls events 5748 - when an event path matches, do the action 5749 5750 [ should the watched service do this restart thing itself? ] 5751 5752 watch-outputs -r controlled-service watched-service path1 path2 ... 5753 [-r or -R or -s n to send that signal number ] 5754 5755 5756 TODO stack 5757 5758 1) should we move all of the veneer-on-s6 scripts into a single package? s6-rc-up-tree, s6-rc-round-robin, watch-outputs, output-template 5759 5760 2) convert all writeFennelScript calls to writeFennel 5761 5762 3) implement if-modified-since in http-fstree 5763 5764 Wed Aug 14 23:00:12 BST 2024 5765 5766 we have a watch-outputs program, just need to hook it up to services 5767 that need restarting 5768 5769 if we follow the pattern that health-check uses, define a service 5770 that runs a script to do 5771 5772 s6-svwait -U /run/service/${name} 5773 watch-outputs -r ${name} ..... 5774 5775 and then insert it into the dependencies of the service that needs 5776 restarting 5777 5778 Sat Aug 17 22:25:44 BST 2024 5779 5780 hostapd is wrapping itself in a watch-outputs, so it restarts 5781 when the secrets change. TODO 5782 5783 [not worth it] 1) be smarter about the watched paths? e.g. find common prefixes? 5784 5785 [done] 2) don't need to wrap at all if there were no secrets 5786 5787 [done] 3) implement different kinds of restart 5788 5789 4) extend to other services 5790 [why?]- dnsmasq 5791 [done] - pppoe / l2tp 5792 [done] - ssh keys 5793 5794 5) other sources 5795 - local filesystem 5796 - local filesystem with tang unlocking 5797 5798 6) should we send authorization header? 5799 5800 7) install on router 5801 5802 8) docs/video 5803 5804 5805 Tue Aug 20 22:45:04 BST 2024 5806 5807 pppd is different because we do the stuff on the command line instead 5808 of using a config file. Though I suppose we could convert to a config 5809 file if it makes it simpler to reuse the template code, and that would 5810 mean that secrets were in the filesystem instead of exposed on the 5811 command line 5812 5813 Wed Aug 21 23:28:41 BST 2024 5814 5815 We may need to patch dropbear to make it look for authorized keys in 5816 somewhere under /run that we can control. Or we could have a separate 5817 dropbearpubkeyagent service that overwrites those files when things 5818 change (but only if home is writable, which it isn't). Or we could install 5819 those files as symlinks to writable storage 5820 5821 https://github.com/fabriziobertocci/dropbear-epka useful? 5822 5823 Fri Aug 23 11:51:34 BST 2024 5824 5825 Wrote a patch to dropbear that permits us to -U /run/dropbear/authorized_keys/%n 5826 5827 We need to write dropbearpubkeyagent service, which listens alongside 5828 the ssh service to create those files when secrets change. it doesn't need 5829 to interact with the actual sshd, but we _do_ need to invoke the 5830 sshd with -U if keys-from-secrets were requested 5831 5832 we need somewhere to specify the secrets path to the keys 5833 5834 sshd = svc.ssh.build { 5835 port = 2222; 5836 authorizedKeys = { 5837 service = config.services.secrets; 5838 path = "ssh/authorized_keys"; 5839 }; 5840 } 5841 5842 will 5843 5844 - start the pubkey watcher service 5845 - add it as a dependency of sshd service 5846 5847 vaguely uneasy about the difference between how we reference a 5848 directory full of secrets here and how we reference a single static 5849 secret in e.g pppoe. But maybe it's ok. the output reference just says 5850 where the value is, it's up to the implementing service script to say 5851 how it gets converted to useful form 5852 5853 How do we reconcile this with config.users, which also has ssh auth 5854 keys? Maybe we just say it overrides. 5855 5856 What if someone provided static data for authorizedKeys? 5857 (1) we would want it to be a attrset not a string 5858 (how do we distinguish an attrset from a secret reference, hmm?) 5859 5860 (2) we would convert it to /run/${name}/authorized_keys/ and use -U 5861 anyway 5862 5863 [done] - make ssh service accept keys as a param, use -U to point dropbear at them 5864 [done] - turn replacable into a function which takes a param and returns 5865 service or path 5866 [done] - replacable type definition takes a param to indicate the "underlying" 5867 type: i.e. an attr can be replacable int or replacable attrset, not 5868 just replacable string 5869 [done] - destructure args in ssh.nix 5870 [done] - write fennel script that watches a secret ref and writes authorized 5871 keys when it changes 5872 [done] - update ssh service to start the watcher instead of constructing key files using echo 5873 5874 Sun Aug 25 19:20:56 BST 2024 5875 5876 5) other sources 5877 - local filesystem 5878 - local filesystem with tang unlocking 5879 5880 should we use a json here, or nested directories like the outputs directly? 5881 I think json, then there's a single file to encrypt 5882 5883 6) should we send authorization header? 5884 5885 It's a form of protection against any random MOTP getting our secrets, 5886 but it does mean the device has to be configured with a secret as well 5887 as an URL, Is that OK? 5888 5889 7) install on rotuer 5890 5891 8) docs/video 5892 5893 9) we're not using luaposix on the host so maybe we can drop it in 5894 write-fennel? 5895 5896 Sun Aug 25 21:52:23 BST 2024 5897 5898 It turns out that fetch-freebsd (and, therefore, http-fstree) 5899 can fetch file: urls, so we don't need to do anything for local files 5900 - except maybe rename that service? 5901 5902 Sun Aug 25 21:55:17 BST 2024 5903 5904 clevis-{en,de}crypt-tang are bash scripts that expect PATH to include 5905 jose, curl, cat. Most of the hard work seems to be done by jose 5906 5907 Should we drag in bash (and curl ...) just to run these scripts? 5908 5909 most of what clevis-decrypt-tang is doing is calling jose repeatedly 5910 to do base64 decoding and then json manipulation, then curl, then jose 5911 again for some actual jwk stuff. I think we could mostly rewrite this 5912 in fennel using rxi-json and fetch 5913 5914 5915 Wed Aug 28 09:40:41 BST 2024 5916 5917 we have clevis-decrypt-tang but not encrypt 5918 5919 Wed Aug 28 21:36:47 BST 2024 5920 5921 5922 new TODO 5923 5924 [done, neeeds testing] 1) to finish local secrets, we need a service 5925 and script that gets the file, decrypts it and turns it to 5926 outputs. Easiest way is to use a temp file in /run/${name} and then 5927 use json-to-tree: there's no extra risk to having the plaintext json 5928 there when it's in the same place anyway as fstree 5929 5930 1.5) and test the process and write some docs 5931 5932 2) perhaps we should use /run/services/var/${name} instead of /run/${name} 5933 to avoid surprise conflicts. or we could use the existing mkstate? 5934 mkstate is setting perms 2751 and I don't know if that's important, 5935 but we want 0700 for secrets 5936 5937 [done] 3) http auth - we have netrc file support "for free", so to speak: 5938 fetch-freebsd looks for $NETRC or $HOME/.netrc. If we put the auth 5939 tokens in configuration, they will get embedded into the image and 5940 this will protect against leaked http server logs but not much else. 5941 Scenario: you have a LAN with untrusted devices on it, plus WAPs which 5942 want to get their config from a server. If the server logs leak, other 5943 LAN users still can't use the config URL to fetch your PPP auth data. 5944 5945 I think it just comes down to docs/video now 5946 5947 5948 -=---- 5949 5950 docs! 5951 5952 to cover: 5953 5954 - outputs 5955 - what for 5956 - how to read? 5957 - one-off read in shell 5958 - monitoring in fennel 5959 - how to write 5960 5961 - secrets 5962 - sources 5963 - https 5964 - local/tang 5965 - supported services/attributes 5966 - how to add a new attribute 5967 - how to add a service 5968 - how it works (see outputs) 5969 5970 5971 think this is mostly to go in Configuration. Is there anything for Admin? 5972 5973 5974 5975 Sat Aug 31 17:52:10 BST 2024 5976 5977 Still having trouble with tangc, which I think is just poor coding in 5978 popen2. Hard to test it without access on loaclhost to the tangd on 5979 bordervm 5980 5981 - use a hostname instead of an ip address 5982 - set the hostname somehow on loaclhost 5983 (actually we could just hardcode the url in tangc.fnl) 5984 - export 7654 in qemu somewhow 5985 5986 Mon Sep 2 21:49:05 BST 2024 5987 5988 TODO - Things we haven't done yet, ideas to consider 5989 5990 [done] 1) improve popen2, maybe using coroutines for proper async chat 5991 5992 2) allow on-device not-in-store netrc so it could be kept in 0700. 5993 Could just do authFile = "/mnt/store/blah" 5994 5995 [done] 3) we're not using luaposix on the host so maybe we can drop it in 5996 write-fennel? 5997 5998 [done] 4) add nodefaultroute to default ppp-options 5999 6000 [done] 5) implement if-modified-since in json-to-fstree 6001 6002 6) clean up some copy-paste (e.g. literal_or_output or whatever we call it) 6003 - [done] ppp variants are consolidated, but there's still more to do here 6004 6005 7) remove references to kexecboot 6006 6007 8) performance testing 6008 6009 9) revive ax3200 port and fix ubifs 6010 6011 10) rebuild our wifi APs and lenscap to use levitate and outboard secrets 6012 6013 11) [outside scope] secrets server on arthur, and oidc too? 6014 6015 12) remove errors from ersatz coldplug 6016 6017 13) teach anoia.svc how to write/remove .lock and state 6018 6019 14) log kernel messages 6020 6021 15) log shipping to something useful 6022 6023 16) standardise error messages in fennel. using assert() is not good 6024 for errors like "the file is missing or can't be opened" because 6025 the backtrace is voluminous and usually inaccurate 6026 6027 we could have something like check-ok which looks for the 6028 common multiple-return (nil errmsg) pattern, we'd like a similar 6029 one for the lualinux (nill errno) pattern. 6030 6031 17) fix with nixpkgs unstable 6032 6033 Wed Sep 4 21:45:07 BST 2024 6034 6035 blurb for audit: 6036 6037 Liminix is a Linux/Nix-based OS that can be flashed to consumer WiFi 6038 routers of the kind that OpenWrt usually runs on (usually small MIPS 6039 or ARM SBCs). Its USP is that because it's based on Nix, the 6040 configuration of your device is based on a text file: there's no GUI 6041 or other imperative interface allowing you to make changes that you 6042 will forget you did six months later and have to recreate when you 6043 update to a new version of the system. 6044 6045 tl;dr C, nftables, Lua (Fennel even better), shell, Nix. No specific 6046 timeline from my end (unless nlnet have told you otherwise). IMO, 6047 emphasis on network vulnerabilities rather than anything involving local 6048 escalation: nobody is expected to be logged in locally except for 6049 maintenance purposes in which case they're trusted by definition. 6050 6051 If you want to start by seeing it running, unless you have a 6052 supported device then your best bet would be to build it for Qemu 6053 <https://www.liminix.org/doc/tutorial.html#running-in-qemu>. It boots 6054 to a root console shell (there is no password on the serial console 6055 because if you have that level of physical access on a real device 6056 it's game over anyway) so take a look at the process list and 6057 filesystem and generally poke around. The filesystem is read-only 6058 unless you configure it otherwise. 6059 6060 6061 To do a "static" audit: a rough breakdown of the contents, by volume, would look like this: 6062 6063 1) 95% of it is packages in the Nix package system (and the Linux 6064 kernel). Some of the packages are built with different compilation 6065 options to produce smaller output, and in a few cases I've patched 6066 them, so someone with C experience might be suited to look at those 6067 patches. 6068 6069 ``` 6070 [dan@loaclhost:~/src/liminix]$ find pkgs/ -name \*.patch 6071 pkgs/kernel/phram-allow-cached-mappings.patch # relevant to dev devices not production 6072 pkgs/kernel/mips-malta-fdt-from-bootloader.patch # for qemu only 6073 pkgs/kernel/cmdline-cookie.patch 6074 pkgs/dropbear/add-authkeyfile-option.patch 6075 pkgs/u-boot/0002-virtio-init-for-malta.patch # only used in tests 6076 pkgs/u-boot/0001-add-ubifs-to-boot-targets.patch # only used in tests 6077 pkgs/xl2tpd-exit-on-close.patch 6078 pkgs/qemu/arm-image-friendly-load-addr.patch # only used in tests 6079 pkgs/kernel-backport/gentree-writable-outputs.patch # unused 6080 pkgs/openwrt/make-mtdsplit-jffs2-endian-agnostic.patch 6081 pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch # can be removed 6082 pkgs/kexec-map-file.patch # can be removed 6083 ``` 6084 6085 dropbear (ssh) and xl2tpd (l2tp) are network-accessible. The kernel 6086 is a high-impact target, but cmdline-cookie.patch is the only "production" 6087 patch there so hopefully easy to review 6088 6089 Significant packages with custom config options: 6090 6091 * hostapd (configured for libtommath, internal TLS) 6092 * nftables (--with-mini-gmp) 6093 * openssl "no-threads" and patches to Configure to build on MIPS 6094 6095 My assumption with all of these is that the package authors wouldn't 6096 provide these as configuration knobs if they weren't reasonably confident 6097 they work as advertised, but I am willing to hear otherwise. 6098 6099 2) the device has a firewall using nftables. The user gets to choose 6100 their firewall rules, but the default ruleset 6101 https://gti.telent.net/dan/liminix/src/branch/main/modules/firewall/default-rules.nix 6102 is based on RFC 6092 for IPv6 and "received wisdom" for IPv4: I would 6103 very much like a second pair of eyes on this. 6104 6105 3) Code which is original to Liminix: as far as possible I've used a 6106 high-level language (Fennel, which is a Lisp syntax that transpiles to 6107 Lua) for "original" development. There is one C package 6108 (pkgs/preinit) and some C glue to expose interfaces to Lua. 6109 6110 None of the original code listens to the network (except Unix-domain 6111 sockets). At least, it was never intended to :-) 6112 6113 Highlights: 6114 6115 pkgs/devout : fills the same role as udev (listens to a kernel socket 6116 and a unix domain socket) 6117 6118 pkgs/json-to-fstree : does HTTP GET and POST requests, using a port of the 6119 FreeBSD libfetch code (see pkgs/fetch-freebsd) 6120 6121 pkgs/tangc : is a transliteration from bash script to Fennel of 6122 https://github.com/latchset/clevis/blob/master/src/pins/tang/clevis-decrypt-tang 6123 and 6124 https://github.com/latchset/clevis/blob/master/src/pins/tang/clevis-encrypt-tang 6125 6126 I call this one out specially because it's crypto-adjacent, but all the 6127 actual cryptography happens in "jose" which it invokes as a subprocess 6128 6129 pkgs/min-copy-closure contains some shell scripts which leverage cpio 6130 and ssh to update a running device over the network. My shell 6131 scripting is probably worse than my C, so take a look 6132 6133 4) there is a mechanism to configure the device's secrets (PPP 6134 password, ssh keys, etc) by fetching a JSON file from an HTTPS server, 6135 and then generating configuration files for the various services that 6136 use those secrets. This is mostly Fennel (so, Lua) 6137 6138 6139 5) the init/service supervision system is based on s6/s6-rc (again, C 6140 software). I don't know if this has ever had an external audit, but to 6141 my eyes it looks like it's been written with security in mind. 6142 6143 Thu Sep 5 10:12:11 BST 2024 6144 6145 if-modified-since and fenceposts ... 6146 6147 we set the mtime of "." to last-modified on retrieve. what resolution 6148 is the timestamp? empirically (using stat(1)), tmpfs seems to have 6149 sub-second resolution, so no loss of data 6150 6151 Thu Sep 5 11:15:24 BST 2024 6152 6153 how do we do deadlock-free popen2? need to use select or poll 6154 on the input and output fds, and read/write a chunk when one of them is ready 6155 6156 (subprocess ["/usr/games/advent" "advent"] 6157 { 6158 :on-stdout #(print (ll.read %1)) 6159 :on-stderr #(print "ERR" (ll.read %1)) 6160 :on-stdin #(ll.write %1 "go north\n") 6161 }) 6162 6163 for send/expect things, a single callback would be preferable if 6164 it has a reason it's being called 6165 6166 (subprocess ["/usr/games/advent" "advent"] 6167 (fn talk [stream fd] 6168 (match stream 6169 :out (print (ll.read fd)) 6170 :err (print "ERR" (ll.read fd)) 6171 :in (ll.write %1 "go north\n")))) 6172 6173 because it can be hooked up to a coroutine. The coroutine is then 6174 responsible for doing things in the right order to avoid letting buffers 6175 fill up - probably this is just a matter of dealing with the 6176 subprocess output before sending it more that it can choke on 6177 6178 wat about partial writes? the coroutine is presumably keeping some 6179 kind of state so it can check the return of ll.write when it updates that 6180 state 6181 6182 6183 Fri Sep 6 19:57:37 BST 2024 6184 6185 video editing 6186 6187 * there are ppp credentials onscreen at 13:00 6188 6189 * should finish at 33:00 6190 6191 6192 Sat Sep 7 22:21:12 BST 2024 6193 6194 what is causing these messages? 6195 6196 @400000000001620127a096b3 watch-for-modem-modeswitch /nix/store/4q3swc0mg28ja30anap4id3gics3h7fk-lua-tty-mips-unknown-linux-mus 6197 l/bin/lua: ...lr-uevent-watch-mips-unknown-linux-musl/bin/uevent-watch:4: unexpected symbol near '/' 6198 @400000000001620127a0ec7e watch-for-modem-modeswitch stack traceback: 6199 @400000000001620127a11c42 watch-for-modem-modeswitch [C]: in function 'dofile' 6200 @400000000001620127a14dec watch-for-modem-modeswitch (command line):1: in main chunk 6201 @400000000001620127a41b1c watch-for-modem-modeswitch [C]: in ? 6202 @ 6203 6204 Sun Sep 8 10:15:56 BST 2024 6205 6206 If we could produce logs in JSON then we could push them to zinc (or 6207 elasticsearch, which has the same api). We'd like fields for 6208 timestamp, message, pid, host. 6209 6210 * we can add host when we post to elasticsearch, no need to repeat it 6211 on every field 6212 6213 * there is no (sensible) way to get the pid of the other end of a pipe. 6214 But we could print it from the sender before execing the process. But then 6215 it'll only appear once instead of every entry. Maybe we could log the 6216 logger pid as well, then we can correlate 6217 6218 TBH given that we already have to process the log lines to get them 6219 into zinc, and that we already can unambiguously parse the log line 6220 (provided we disallow whitespace in the service name, and we mandate 6221 that the message is always the final field) there's not much value in 6222 producing a different json. 6223 6224 Actually the logger pid probably won't help us tell when the service 6225 has been restarted, because the logger won't be restarted at the time 6226 time due to fdholder stuff 6227 6228 so perhaps there are no logging changes we can easily/reasonably make 6229 and we should just write a log processor that ships to a collector. 6230 6231 - open connection to zinc (s6-tlsclient) 6232 - send http headers 6233 - while not eof(stdin) 6234 - read line 6235 - split fields 6236 - send command, send data 6237 6238 { "index" : { "_index" : "olympics" } } 6239 {"Year": 1896, "City": "Athens", "Sport": "Aquatics", "Discipline": "Swimming", "Athlete": "HAJOS, Alfred", "Country": "HUN", "Gender": "Men", "Event": "100M Freestyle", "Medal": "Gold", "Season": "summer"} 6240 6241 6242 we can't calculate content-length. maybe we can use chunks 6243 6244 Transfer-Encoding: chunked 6245 6246 size-of-chunk-in-hex CRLF 6247 chunk-data CRLF 6248 6249 0 CRLF 6250 CRLF 6251 6252 to generate test data: 6253 $ nix-shell -p s6 --run " sort --random-sort ~/src/liminix/THOUGHTS.txt | head -1 | sed 's/^/servicename /g' |tr -cd '[a-z0-9 ]' | s6-tai64n" 6254 6255 Sun Sep 8 16:39:55 BST 2024 6256 6257 * how do we add incz to the logging infra and configure it? 6258 * how do we get zinc on loaclhost to be visible to test lan (port forward 6259 on border?) 6260 * shall we rig up a service on loaclhost so that zinc starts at boot? 6261 6262 Mon Sep 9 17:58:46 BST 2024 6263 6264 We can use this as a log processor. However, a log processor doesn't 6265 ship the segment until the log writer has finished with it, therefore, 6266 some latency is introduced. 6267 6268 We can write logs to the network as they are generated. However, what if: 6269 6270 - the network is not available 6271 - the collector is not keeping up 6272 6273 s6-log says "if a processor fails, s6-log will try it again after some 6274 cooldown time.". Laurent says "if a processor fails [if you're using 6275 -b] then the rotation cannot happen, and s6-log will stop reading 6276 until the processor succeeds. Without -b, logs keep accumulating in 6277 RAM, and s6-log may crash if it runs oom before the processor 6278 succeeds" 6279 6280 .... so, maybe we shouldn't use log processors here. 6281 6282 shipping finished log segments outside of the s6-log framework is 6283 quite straightforward. the issue is how to send the in-progress log. 6284 Challenges 6285 6286 - if we are sending the in-progress log, how _not_ to resend all the 6287 same entries when the segment is rotated 6288 - how do we know when the segment is rotated and should start reading 6289 the new file 6290 6291 Maybe 6292 6293 1) we have a logshipper service that listens on a unix socket 6294 2) s6-log is hooked to the logshipper-client logger, which checks 6295 for the unix socket and only writes data to it if it exists. 6296 Probably it should check periodically for the socket to exist 6297 and not just try it on every write 6298 (Would be good if it could tell whether the socket had a 6299 listener or not. Maybe abstract sockets) 6300 6301 6302 (and also writes to stdout so the s6 logging chain is unbroken) 6303 3) when logshipper is ready, it reads all past log entries whose 6304 timestamps are from before when it started, and writes them. 6305 and/or it could write a cookie to the log, then it would know 6306 to stop reading the logs when it encounters its own cookie 6307 6308 Mon Sep 16 19:54:35 BST 2024 6309 6310 incz won't work as-is because it uses stdin/stdout for communicating 6311 with http and reads the logs from a filename. unless we make it use 6312 /proc/self/fd/3 for the filename? Even then, ideally we kind of want 6313 to adapt it for streaming 6314 6315 Wed Sep 18 18:23:38 BST 2024 6316 6317 we can run 6318 6319 socat tcp-listen:19612,reuseaddr,fork | s6-log -b /var/log/clients 6320 6321 on the log collection host (or use openssl-listen if you're going to set up ssl certs) 6322 6323 then our logshipper program can basically be "open socket and cat to s6-tcpclient" (though it would be better if we can add the hostname in the process) 6324 6325 let's make the logging script a config option 6326 6327 6328 pipeline { s6-ipcserver -1 /run/uncaught-logs/shipping } 6329 pipeline { s6-tcpclient loghost:19612 } 6330 fdmove -c 1 7 6331 cat 6332 6333 Wed Sep 18 20:32:54 BST 2024 6334 6335 s6-log will create its directory but the parent must exist. incidentally, 6336 putting /run/uncaught-logs in pseudofiles is pointless because /run is a 6337 mountpoint 6338 6339 ip addr add 10.0.2.15/24 dev lan 6340 6341 s6-rc -d change sshd; s6-rc -u change sshd; 6342 6343 Sun Sep 22 21:13:15 BST 2024 6344 6345 This works for the collector (but note that it collects logs from 6346 *anywhere* that can write to that port, so please firewall responsibly) 6347 6348 6349 systemd.services."s6-log-collector" = { 6350 after = [ "network.target" ]; 6351 wantedBy = [ "multi-user.target" ]; 6352 serviceConfig = { 6353 Type = "exec"; 6354 WorkingDirectory = "/var/log"; 6355 ExecStart = '' 6356 ${pkgs.bash}/bin/sh -c "${pkgs.socat}/bin/socat tcp4-listen:17345,reuseaddr,fork stdout | ${pkgs.s6}/bin/s6-log -b /var/log/remote" 6357 ''; 6358 }; 6359 }; 6360 6361 6362 Tue Sep 24 18:14:42 BST 2024 6363 6364 "Raw" TCP is not the ideal transport for logs because I don't want the 6365 whole internet able to write to my log server, and writing 6366 only-from-the-LAN iptables rules is messy with a gazillion ipv6 6367 addresses to account for. 6368 6369 . SSL with client certificates would be nice, but there is the issue 6370 of how to get the private key onto the device and sign it. My idea is 6371 6372 1) device generates a private key on first boot (or every boot, if no 6373 persistent storage). Private key includes some field with a value that 6374 was set at build time (PSK, effectively) 6375 6376 2) there is an API-driven CA signing thingy that the client can use to 6377 get a cert based on their key. It checks the field for the presence 6378 of the PSK. It should probably expose HTTPS only so that the client 6379 can be sure it's getting signed by the correct CA 6380 6381 6382 3) the log collector refuses connections unless the client is signed by the 6383 local CA 6384 6385 This is quite a lot of work insofar as it would appear to require 6386 writing the CA. 6387 6388 We could alternatively do something much more ad hoc where the client 6389 just writes the PSK to the server when it opens the stream, before 6390 sending any data. We'd have to write the server end, then, instead of 6391 just using socat - but that is probably less work than an API-driven CA. 6392 On the other hand, TLS logs would also be encrypted which is a good thing 6393 if the LAN is not trusted. 6394 6395 Sat Sep 28 16:04:15 BST 2024 6396 6397 OK, so we wrote the CA. 6398 6399 To do HTTPS on the client we need 6400 6401 1) to generate a csr 6402 2) to https it to the server 6403 3) store the generated thingy as a service output 6404 6405 looking at x86-64 sizes for ballpark 6406 6407 -r-xr-xr-x 1 root root 987K Jan 1 1970 /nix/store/s45wy1ssim1dkxzligx09xjp4n0668i2-openssl-3.0.14-bin/bin/openssl 6408 -r-xr-xr-x 1 root root 263K Jan 1 1970 /nix/store/z28bxdnsw2gr1xwx7qj6px9iz5sr84i9-lua-5.3.6-env/lib/lua/5.3/_openssl.so 6409 6410 suggesting that we'd use _less_ disk doing the whole thing in lua than 6411 6412 Sun Sep 29 10:20:49 BST 2024 6413 6414 We need luaossl support for setting attributes in a CSR 6415 6416 https://www.rfc-editor.org/rfc/rfc2986#page-5 6417 6418 Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }} 6419 6420 CRIAttributes ATTRIBUTE ::= { 6421 ... -- add any locally defined attributes here -- } 6422 6423 Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE { 6424 type ATTRIBUTE.&id({IOSet}), 6425 values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type}) 6426 } 6427 6428 I don't understand this 100% but it looks like the raw data is _not_ 6429 the same format as an x509 attribute. See e.g. 6430 https://github.com/golang/go/commit/e78e654c1de0a7bfe0314d6954d42b046f14f1bb#diff-a789286d7e257f148c437404f8cf5d3379688597381ff13352e62ac406be295aL1712 6431 in support of my hypothesis (background: iiuc, "critical" is a boolean flag, 6432 but x509 attributes aren't allowed to be booleans) 6433 6434 https://en.wikipedia.org/wiki/Certificate_signing_request 6435 6436 388:d=2 hl=2 l= 35 cons: cont [ 0 ] 6437 390:d=3 hl=2 l= 33 cons: SEQUENCE 6438 392:d=4 hl=2 l= 9 prim: OBJECT :challengePassword 6439 403:d=4 hl=2 l= 20 cons: SET 6440 405:d=5 hl=2 l= 18 prim: UTF8STRING :loves labours lost 6441 425:d=1 hl=2 l= 13 cons: SEQUENCE 6442 427:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption 6443 6444 6445 (csr:getAttribute "challengePassword") 6446 (csr:getAttributeNames) 6447 (csr:setAttribute "challengePassword" :IA5STRING ["loves labours lost"]) 6448 6449 how do we know the asn1 type of the attribute values? it looks like they're 6450 defined by the object: see e.g. https://www.rfc-editor.org/rfc/rfc2985#page-16 6451 6452 A challenge-password attribute must have a single attribute value. 6453 6454 ChallengePassword attribute values generated in accordance with this 6455 version of this document SHOULD use the PrintableString encoding 6456 whenever possible. If internationalization issues make this 6457 impossible, the UTF8String alternative SHOULD be used. PKCS #9- 6458 attribute processing systems MUST be able to recognize and process 6459 all string types in DirectoryString values. 6460 6461 crypto/asn1/tbl_standard.h: {NID_pkcs9_challengePassword, 1, -1, PKCS9STRING_TYPE, 0}, 6462 include/openssl/asn1.h.in:# define PKCS9STRING_TYPE (DIRSTRING_TYPE|B_ASN1_IA5STRING) 6463 6464 I assume there's something in openssl that will do lookups in this table 6465 to give us the type for the oid, then maybe something in luaossl that 6466 would lua-ize it? 6467 5 6468 6469 or we could put that burden on the caller, as x509.name:add does 6470 6471 Sun Sep 29 20:46:16 BST 2024 6472 6473 6474 OBJ_txt2nid("challengePassword"); works with short/long names 6475 6476 the get call is complicated because there can be multiple 6477 attributes with the same type. There probably aren't but ... 6478 6479 (csr:getAttribute "challengePassword") => multivals attr, index 6480 6481 (csr:getAttribute "challengePassword" index) => multivals attr, index 6482 6483 (csr:addAttribute "challengePassword" :IA5String ["loves labours"]) 6484 6485 (csr:clearAttribute index) 6486 6487 6488 Tue Oct 1 21:55:25 BST 2024 6489 6490 on server, we need to reconfigure socat to give it our CA cert and 6491 expect peer to authenticate 6492 6493 on client, I am not sure if we can persuade s6-tlsclient to use the same 6494 file for cert and private key. perhaps certifix-client could write 6495 two separate files with --out-key and --out-certificate 6496 6497 6498 CAFILE=ca.crt KEYFILE=client.key CERTFILE=client.crt s6-tlsclient -k localhost -y -v localhost 19612 socat 'fd:6!!fd:7' - 6499 6500 6501 socat ssl-l:19612,reuseaddr,fork,cert=server-combined.pem,cafile=ca.crt stdout 6502 6503 suggest creating /var/lib/s6-log-collector/{private,cert} on loaclhost 6504 wherein we keep the server and ca keys, then socat and the signing 6505 server can both see them 6506 6507 6508 ip addr add 10.0.2.15/24 dev lan 6509 6510 Sat Oct 5 22:35:41 BST 2024 6511 6512 We had it working in a VM, and the service is installed on loaclhost 6513 6514 TODO 6515 6516 [done] 1) make a module-based service for client-cert 6517 caCertificateFile 6518 secretFile 6519 subject 6520 url 6521 6522 [done] 2) make the shipping service a consumer-for 6523 6524 [not by much] 3) can we reduce the verbiage in the shipping service somehow? 6525 6526 4) rebuild an actual device with all this stuff 6527 6528 Tue Oct 8 23:50:00 BST 2024 6529 6530 idea: create outputs.update which builds a systemConfiguration and 6531 also a result/install.sh which does min-copy-closure and 6532 restart-services as per liminix-rebuild, then we don't have 6533 to nix-shell liminix-rebuild 6534 6535 nix-build ../liminix/ -I liminix-config=hosts/rotuer.nix --argstr deviceName turris-omnia -A outputs.update-system -o rotuer && ./rotuer/install.sh 6536 6537 this would make it much more straightforward to build a bunch of hosts 6538 using a Makefile 6539 6540 idea 2: when a configuration contains levitate, something similar 6541 but necessarily more "manual" to do the analogous thing 6542 6543 6544 Sun Dec 15 18:55:55 GMT 2024 6545 6546 Where we left off with this, rotuer was crashing randomly or failing 6547 to boot every time we tried to add log shipping, which is not very 6548 ideal. I started doing something with logging to /dev/pmsg0 6549 (CONFIG_PSTORE_PMSG) but I think (there seems not to be anything 6550 written down :-( ) that the gl-ar750 kernel needs it added to kconfig and device tree 6551 6552 https://wiki.postmarketos.org/wiki/User:Knuxify/Enabling_pstore_and_ramoops 6553 6554 we could add a new hardware.dts.dtsi = [] option so that any module 6555 could add a new chunk of dts. (Ideally we'd call it `includes` 6556 but that conflicts with the existing use of `includes` to specify 6557 search path. Maybe rename?) 6558 6559 would we ever use it except in a hardware device definition? 6560 (Or user config?) I guess if we were consistent with names 6561 then we could set up nodes in the device file with status="disabled" 6562 and enable them in the module, except that dt doesn't consistently 6563 use status and in fact there isn't one for reserved-memory 6564 6565 we could use global config to enable pstore_msg and check it in 6566 the device module to enable the needed hw support 6567 6568 Tue Dec 17 23:39:28 GMT 2024 6569 6570 I think we can just stick a tee in the fallback logger pipeline that 6571 writes to /dev/pmsg0 6572 6573 Need to check it's a circular buffer 6574 6575 do we want to do anything about recovering the log on boot? 6576 - we could just copy it to /run/log 6577 - if we have backfilling for shipped logs (we don't yet) 6578 then we might want to ship it - but that may result in duplicate 6579 logs if some of it was shipped before the crash 6580 6581 perhaps we should truncate pmsg0 on orderly shutdown? or maybe it's 6582 good to see the late shutdown logs. 6583 6584 Thu Dec 19 13:40:39 GMT 2024 6585 6586 although we have PSTORE_foo in the omnia kconfig, I think this might 6587 be just because I copied it from RT3200 6588 6589 Thu Dec 19 14:15:43 GMT 2024 6590 6591 Omnia is not in ci.nix, and it's not trivial to add it because there 6592 is no output in the ci.nix configuration that makes sense for omnia. 6593 6594 ... OK, fixed by adding system-configuration as an independent module 6595 and importing in device config 6596 6597 Thu Dec 19 21:59:47 GMT 2024 6598 6599 The build-system shell script in outputs.systemConfiguration 6600 is ugly and requires we do bad things to avoid sucking build 6601 system stuff into the config 6602 6603 I propose we make it a separate derivation. 6604 6605 But first maybe we could improve some names 6606 6607 Sun Dec 22 14:23:02 GMT 2024 6608 6609 MT7622> echo $boot_default 6610 if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; setenv replacevol 1 ; run boot_tftp_for 6611 ever 6612 MT7622> echo $bootcmd 6613 if pstore check ; then run boot_recovery ; else run boot_ubi ; fi 6614 MT7622> echo $boot_ubi 6615 ubi part ubi && run boot_production ; run boot_recovery 6616 MT7622> echo $boot_production 6617 led $bootled_pwr on ; run ubi_read_production && bootm $loadaddr#$bootconf ; led $bootled_pwr off 6618 MT7622> echo $ubi_read_production 6619 ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs 6620 MT7622> echo $ubi_prepare_rootfs 6621 if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi 6622 MT7622> echo $bootconf 6623 config-1 6624 MT7622> run boot_ubi 6625 UBI partition 'ubi' already selected 6626 No size specified -> Using max size (126976) 6627 Read 126976 bytes from volume fit to 0000000048000000 6628 6629 ## Checking Image at 48000000 ... 6630 Unknown image format! 6631 No size specified -> Using max size (7491584) 6632 Read 7491584 bytes from volume recovery to 0000000048000000 6633 ## Loading kernel from FIT Image at 48000000 ... 6634 Using 'config-1' configuration 6635 Trying 'kernel-1' kernel subimage 6636 Description: ARM64 OpenWrt Linux-6.6.45 6637 6638 Sun Dec 22 17:39:56 GMT 2024 6639 6640 From the output above it looks like the device I have plugged in has 6641 an openwrt "recovery" image but not a "production" image 6642 6643 It also looks like it will be quite hard work to persuade it to boot 6644 from usb or anything. It doesn't have any of the extlinux stuff 6645 but it does have uefi for what that's worth 6646 6647 default boot_production command reads a ubi volume called 'fit' and 6648 calls bootm on what it finds 6649 6650 we could define boot_production to ubifsmount liminix; ubifs load 6651 <addr> <filename> (which is a fit) and bootm it. *presumably* we could 6652 do this from the openwrt recovery image 6653 6654 but could we install the whole system using said recovery image? I 6655 expect we could do, it only requires getting a tarball onto it and 6656 unpacking it 6657 6658 however, extlinux is not going to be helpful 6659 (actually it might be a bit, if we ask it to write the fit as 6660 well as/instead of the individual files) 6661 6662 maybe we need separate concepts of "the filesystem contains 6663 stuff we need for boot" and "the stuff we need is the stuff 6664 that extlinux needs" 6665 6666 each bootloader makes an output called bootfiles, and 6667 the bootablerootdir output copies from bootfiles 6668 6669 Mon Dec 23 18:28:50 GMT 2024 6670 6671 it might be worth moving ubi option decls into the hardware module, if 6672 they're hardware-dependent 6673 6674 Tue Dec 24 16:14:50 GMT 2024 6675 6676 Where next? 6677 6678 a config for haproxy would be good 6679 6680 > a connection or request arrives on a frontend, then the 6681 information carried with this request or connection are processed, and 6682 at this point it is possible to write ACLs-based conditions making use 6683 of these information to decide what backend will process the request. 6684 6685 http://docs.haproxy.org/3.1/intro.html#3.4.4 6686 6687 listen foo 6688 bind :443 6689 6690 frontend foo 6691 mode tcp 6692 6693 Thu Dec 26 14:27:26 GMT 2024 6694 6695 What's the plan? 6696 6697 1) build the updater target for rotuer 6698 2) ssh forward through bordervm to install it 6699 3) carve the ngix sni proxy into examples/module-sniproxy 6700 with a fat warning comment 6701 3.5) should we add the examples to ci? 6702 4) see if it builds 6703 5) using curl on bordervm, see if it forwards 6704 5000) swap the real rotuer hardware 6705 6706 6707 Sun Dec 29 18:22:42 GMT 2024 6708 6709 To make sniproxy work it needs dns, which means it needs an upstream. 6710 But if we move the bordervm cable from lan to wan, we won't be able to 6711 rebuild over ssh unless it's sufficiently unbroken for pppoe to be working 6712 6713 ... unless we add a "management" address to the wan interface along with pppoe 6714 6715 to permit wan stuff through the firewall, now that we've fixed the 6716 firewall :embarrassed:, do 6717 6718 /nix/store/*nftables*/bin/nft insert rule table-ip input-ip4 position 19 iifname "wan" jump incoming-allowed-ip4 6719 6720 What else do we need to try rt3200 as prod rotuer? 6721 6722 - pstore logging 6723 - log shipping (maybe via wan to bordervm for now) 6724 - enable sniproxy module 6725 6726 zcat /proc/config.gz | grep PSTORE 6727 6728 Tue Dec 31 22:45:33 GMT 2024 6729 6730 /nix/store/i1khbsqpyx020xrhvfbdazc1bnmirc72-kernel-aarch64-unknown-linux-musl-modulesupport/vmlinux 6731 6732 6733 these 3 derivations will be built: 6734 /nix/store/fvxrvyx64cm86cc4na0584qj9xw6s406-kernel-aarch64-unknown-linux-musl.drv 6735 /nix/store/ksg93bd8x2z1xpfmj24ixm2srg547mjx-dtb-aarch64-unknown-linux-musl.drv 6736 /nix/store/n33ij1npzwjmlsw23sz6yhyrh9hgxwaw-kernel.image-aarch64-unknown-linux-musl.drv 6737 building '/nix/store/fvxrvyx64cm86cc4na0584qj9xw6s406-kernel-aarch64-unknown-linux-musl.drv'... 6738 zcat /proc/config.gz | grep PSTORE 6739 6740 we think tftpboot works (check!) 6741 build the FIT from the updater target with squashfs added in rotuer.nix 6742 boot it with tftp by copy-pasting the boot.scr and changing it 6743 6744 setenv serverip 10.0.0.1 6745 setenv ipaddr 10.0.0.8 6746 tftpboot 0x4007ff28 r2/rootfs; tftpboot 0x41086f28 r2/image; tftpboot 0x4107ff28 r2/dtb 6747 bootm 0x41086f28 - 0x4107ff28 6748 6749 tftpboot works 6750 tftpboot with outputs.uimage works 6751 1458ed2cdeaa6bf4c02c6511a6d7369d /nix/store/4n3jc4n7lsp73yc2ccnmgibc7079rxms-kernel.image-aarch64-unknown-linux-musl 6752 6753 $ grep fit /nix/store/ihi7vski37b9ph4xcyqpabymc5nsngjd-system-configuration-aarch64-unknown-linux-musl/etc/nix-store-paths 6754 /nix/store/5jphs9mnb8hzhvwfi2z8h6cnaz6qx4y6-boot-fit 6755 $ md5sum /nix/store/5jphs9mnb8hzhvwfi2z8h6cnaz6qx4y6-boot-fit/fit 6756 1458ed2cdeaa6bf4c02c6511a6d7369d /nix/store/5jphs9mnb8hzhvwfi2z8h6cnaz6qx4y6-boot-fit/fit 6757 6758 something is changing /persist/boot from a symlink to a directory and I don't know what 6759 ... plot twist: or is it? maybe it's ubimage that makes it a directory 6760 6761 rt3200 has no pmsg-size according to dtc -I fs /proc/device-tree/ -O dts 6762 6763 should be more like 6764 6765 reserved-memory { 6766 ramoops@03f00000 { 6767 compatible = "ramoops"; 6768 reg = <0x03f00000 0x10000>; 6769 pmsg-size = <0x10000>; 6770 }; 6771 }; 6772 }; 6773 6774 Wed Jan 1 14:57:35 GMT 2025 6775 6776 At last I have working persistent logging. 6777 6778 I think we need to do something at boot time to move the persistent logs into 6779 the regular s6 log thing 6780 6781 foreground { 6782 redirfd -w 1 /run/prior-boot2 6783 elglob file /sys/fs/pstore/* cat $file 6784 } 6785 6786 Thu Jan 2 14:31:11 GMT 2025 6787 6788 to change to a previous "generation" we could run any of 6789 "/nix/store/*system-configuration*/bin/install /mnt" from a rescue 6790 system. It would populate /boot and bin/activate 6791 6792 supposing we are in such a rescue system, how do we find *which* 6793 system-configuration is the one we want to revert to? The derivation 6794 should be pure, so if we're going to timestamp anything we have to do 6795 that in the imperative step i.e. update.sh 6796 6797 perhaps a symlink from /persist/configuration/yyyymmddtmmhhss -> /nix/store/eeee-blah 6798 6799 6800 Thu Jan 2 23:38:59 GMT 2025 6801 6802 Stuff we should tidy up: 6803 6804 1) all devices to set credible default output, rootfs, etc 6805 2) expunge remaining references to kexecboot 6806 3) dynamic uid assigment for users 6807 4) lessen logging noise from firewall 6808 5) update.sh --fast sends wlan services for a spin 6809 6810 6811 6812 Fri Jan 3 16:05:10 GMT 2025 6813 6814 * build actual rotuer config and install on rt3200 6815 * find usb ethernet dongle for laptop 6816 * plug it in? 6817 6818 Sat Jan 4 15:32:03 GMT 2025 6819 6820 didn't work so well, because the installer version is coupled with the 6821 dtb and the dtb can't be upgraded without upgrading the kenrel 6822 6823 kernel 6.6.67 builds and boots but seems to have no wired ethernet. 6824 looking through the openwrt config changes ... 6825 6826 +CONFIG_MTD_UBI_NVMEM=y 6827 +CONFIG_NVMEM_BLOCK=y 6828 +CONFIG_NVMEM_LAYOUT_ADTRAN=y 6829 +CONFIG_PHYLIB_LEDS=y 6830 +CONFIG_MTK_REGULATOR_COUPLER=y 6831 +CONFIG_FW_LOADER_SYSFS=y 6832 6833 6834 Sun Jan 5 12:58:52 GMT 2025 6835 6836 We are running with rt3200 and everything appears to work :-) 6837 6838 Sun Jan 5 20:34:18 GMT 2025 6839 6840 what customization do we want from the firewall? 6841 6842 - what's allowed wan->lan 6843 - what's allowed lan->wan 6844 - which dropped packets get logged or don't 6845 6846 plus fix whatever it was RoS found 6847 plus stop hardcoding the interface names 6848 6849 Q: if pppd _makes_ an interface, how do we know what the name of it 6850 is going to be before it's up and passing packets so that we can 6851 have the firewall active before it starts 6852 6853 or could we have ppp service install a "drop everything" firewall 6854 before it starts? what if there's more than one upstream interface, 6855 they shouldn't wipe each other out 6856 6857 so can we have a "default deny" firewall in which every allowed flow 6858 is qualified by the interface name, and any service that brings up an 6859 interface is required to add firewall rules for it according to its 6860 role 6861 6862 or maybe the firewall service could watch for interfaces being added 6863 (and removed) and update the ruleset as appropriate for the interface 6864 role (lan, wan, dmz, management, guest, ???). but how does it know 6865 the role based on the interface name? 6866 6867 Tue Jan 14 19:33:28 GMT 2025 6868 6869 each interface can add its own chain when it comes up, and then we can 6870 figure out some way to jump to the correct chain (vmaps) based on 6871 interface name 6872 6873 % nft add map filter mydict { type ipv4_addr : verdict\; } 6874 % nft add element filter mydict { 192.168.0.10 : drop, 192.168.0.11 : accept } 6875 % nft add rule filter input ip saddr vmap @mydict 6876 6877 _but_ we might be better off declaring static "zones" (lan, world, 6878 dmz, guest, etc etc) with a map for each, and then replace the hardcoded 6879 interface names with a map lookup 6880 6881 % nft add map filter ifzone { type ifname : string ; } 6882 6883 6884 zones { type ifname: ipv4_addr\; } 6885 ... and presumably we also need identical maps for nat and 6886 ... any other chain type where we need to distinguish 6887 ... inside from outside 6888 % nft add element nat porttoip { 80 : 192.168.1.100, 8888 : 192.168.1.101 } 6889 6890 % nft add rule ip nat postrouting snat to tcp dport map @porttoip 6891 6892 Mon Jan 20 20:32:58 GMT 2025 6893 6894 1) maybe we can add a type="chain" or type="set" attribute to the 6895 firewallgen input, then we cna have sets in the default firewall rules 6896 6897 2) then we convert the default firewall rules to use sets instead of 6898 hardcoding ifname 6899 6900 3) then find a nice place to hook "new interface is available" and 6901 add it to the appropriate zone (separately for ip4 and ip6, gah) 6902 6903 4) and how do we find 6904 6905 # nft add element ip table-ip lan { int } 6906 # nft add element ip table-ip wan { ppp0 } 6907 # nft add element ip6 table-ip6 lan { int } 6908 # nft add element ip6 table-ip6 wan { ppp0 } 6909 6910 Tue Jan 28 21:51:46 GMT 2025 6911 6912 Going back and forth on the firewall stuff, in respect of where to aim 6913 for with "general" vs "useful". Specifically, if we have hooks 6914 every time interfaces are added/removed that 6915 expect some specific sets to (a) exist and (b) have particular 6916 semantics, there is necessary coupling between those hooks and the 6917 firewall definition. So, practically speaking, the "a new interface 6918 appears" rules need to be bundled with the firewall ruleset 6919 6920 Which also means that the firewall needs to know which zone the 6921 interface is assigned to, which is a problem if it can't tell from the 6922 name (for example wg0 wg1 wg2 ... could be wan or lan or dmz or 6923 anything) 6924 6925 So the service that owns the interface needs to communicate "another 6926 one for the lan zone" to the firewall and it can't do that by adding 6927 to sets directly unless it knows what the sets are called. Implying we 6928 need an interface between the interface service that knows "new 6929 interface ppp7 added in wan zone" and the firewall service that knows 6930 how to accommodate this. For extra credit this actually should be more 6931 like pubsub: the interface shouldn't really have to know the firewall 6932 exists. 6933 6934 outputs? 6935 6936 - what if each interface wrote a "zone" output and the firewall 6937 subscribed to them? would need the firewall to know which of all the 6938 available services were interfaces 6939 6940 - a zone service that every interface in the zone depends on. it 6941 doesn't do much in itself but it means the interface updown scripts 6942 know a service directory where they can touch lan-zone/eth0 or 6943 whatever. This could work. The firewall service definition specifies 6944 the zone services and uses inotify watcher thingies to update 6945 interface sets when contents change. 6946 6947 Wed Jan 29 17:19:24 GMT 2025 6948 6949 1) make a zone service defn that can be instantiated for each zone. 6950 it should create $output/interfaces 6951 6952 2) add a `zone` attribute to interface definitions, causing 6953 - the zone service to be added to the dependencies 6954 - the interface "up" script to include writing to the zone/interfaces output 6955 6956 2b) any other service that creates an interface (e.g. ppp) needs to also 6957 have `zone` and do the same 6958 6959 3) firewallgen to be able to make sets 6960 6961 4) firewall service to watch the zone outputs 6962 6963 Fri Jan 31 17:11:16 GMT 2025 6964 6965 Do we need zone services? I think we could put zones in the outputs of 6966 the firewall service? 6967 6968 Sun Feb 2 20:59:56 GMT 2025 6969 6970 What's the smallest first step? 6971 6972 - [done] how can we make firewallgen output sets (or could we 6973 make the firewall service tack them on afterwards) 6974 6975 - make a longrun that watches its own zones output and updates the 6976 appropriate sets 6977 6978 The sticking point is that if you give the firewall `rules` instead of 6979 `extraRules` then the longrun may or may not work depending on (1) 6980 whether you made the zone sets; (2) whether your rules use 6981 them. Conclusion: if you supply `rules` then you also have to say 6982 whether you want the longrun or not. So add a param 6983 watchForInterfaceUpdates which defaults true 6984 6985 Mon Feb 3 21:12:55 GMT 2025 6986 6987 the thing that updates sets has to know they exist, so the interface watcher 6988 service must live in the firewall module 6989 6990 the firewall service defn should return the firewall service after 6991 adding the interface watcher as a dependency of it. Or: the watcher 6992 should make the sets and then the firewall service could depend on _it_. 6993 That would mean that the firewall service would fail if it used sets 6994 that the watcher didn't make, is that good or bad or indifferent? 6995 6996 the interface services have to know about the watcher as well in order 6997 to write into its outputs, so it can't be hidden inside the module 6998 6999 maybe the watcher service should _be_ the firewall service. 7000 7001 we could add a "notify" param to an interface which would be an output 7002 reference to (the firewall service / zones / lan ) that the interface would 7003 write its ifname into when the service is up 7004 7005 Wed Feb 5 00:14:29 GMT 2025 7006 7007 another thought: the firewall service could have params to say 7008 which interface services are in which zones 7009 7010 we'd have to ensure that the interface services did not end up as 7011 dependencies of the firewall 7012 7013 then the firewall could 7014 7015 - create the sets 7016 - watch each interface service for the ifname output and add it to the right zone 7017 7018 Sun Feb 9 21:33:57 GMT 2025 7019 7020 nft update set @lan 7021 7022 echo 'flush set table-ip lan; add element table-ip lan { eth0,lo }' | nft -f - 7023 7024 Tue Feb 11 18:30:09 GMT 2025 7025 7026 outstanding for 1.0: 7027 7028 1) security audit fedback 7029 7030 a) ask ROS if I can ship their report, with a response doc 7031 showing the commits that address each finding/non-finding 7032 b) firewall rules: icmp rate limit, DNS, doc for icmpv6 packet dropping 7033 c) look over env var inputs and parse them properly instead of 7034 string glommeration 7035 7036 2) docs: 7037 - for each device, add "finishedness" status and link to build status 7038 - generally read them over and spruce up 7039 - porting guide 7040 7041 3) some kconfig magic to generate minimal kconfig files so that 7042 device modules don't end up as copy-pastes of the openwrt defconfig 7043 7044 7045 --- 7046 7047 apparently 5% of available bandwidth is a reasonable rate limit for 7048 icmp 7049 7050 % nft add rule filter input limit rate over 10 mbytes/second drop 7051 7052 but nftables has no way to get interface bandwidth and indeed nor does 7053 the device generally: the 1000Mb/s ethernet interface might be 7054 connected to a 70Mb/s pppoe upstream and how would it know? So the 7055 site operator needs to say somewhere what the upstream bandwidth is. 7056 7057 Sun Feb 16 22:16:29 GMT 2025 7058 7059 we probably didn't need to write that service, we could have used the 7060 thing that makes templated config files _and_ if we somehow contrive 7061 to write the interface bandwidth as an interface output we could get 7062 that the same way 7063 7064 if only I could remember how it worked :-) 7065 7066 ---- 7067 7068 * watch-output watches only _one_ service and is called with a list of 7069 outputs inside that service, so not exactly what we need. we can 7070 extend it easily enough to watch multiple services using poll() if we 7071 can figure out the syntax we want. Luckily all the places that call it 7072 go through modules/secrets/subscriber.nix so it's easy enough to change 7073 existing uses 7074 7075 we could do 7076 watch-outputs -r foo /nix/store/blah/.outputs/ifname /nix/store/eee/.outputs/ifname ... 7077 7078 or 7079 watch-outputs -r foo /nix/store/blah:ifname /nix/store/eee:ifname /nix/store/eee:bandwidth 7080 7081 or 7082 7083 watch-outputs -r foo /nix/store/blah:ifname /nix/store/eee:ifname:bandwidth 7084 7085 which I quite like insofar as it's shorter but has no other real merit 7086 7087 then we need to decide how to represent an output reference in a firewall rule. 7088 Since each rule is basically text already, might just put the handlebars straight in 7089 7090 let qq = builtins.toJSON ; 7091 in "icmp6 limit rate over {{ tonumber(output(${qq (intf "service")}, ${qq (intf "bandwidth")})) / 20 }} bytes/second drop" 7092 7093 probably we should do a separate rule for each interface in the wan zone 7094 7095 Sun Feb 23 00:34:34 GMT 2025 7096 7097 looks like we have no tests for anything involving watched services or subscribers, 7098 or if we do I can't see what 7099 7100 Thu Feb 27 20:47:03 GMT 2025 7101 7102 - use output-template to write firewall rule file 7103 - wrap firewall in svc.secrets.subscriber.build (c.f. e745991) with zones as 7104 watched services 7105 - put the handlebars in the firewall config 7106 7107 we have uncommitted changes to watch-outputs that I'm relunctant to 7108 commit until I have some way to see if they're working. the pppoe test 7109 will check both firewall zones so _should_ start to fail with the 7110 current watch-outputs (because only one service) and then pass when we 7111 put the new one in 7112 7113 Fri Feb 28 01:00:03 GMT 2025 7114 7115 Well, it works at least well enough to pass the test. There is an awful hack 7116 though, because nftables doesn't accept "elements = { }" as valid syntax 7117 for a set with no elements, so we post-process the file to wipe those lines 7118 7119 I wonder if we could instead create the set empty and then use the "other" 7120 nftables format to generate commands that add the elements. If it's 7121 all in the same file (or included files) it will continue to be atomic 7122 7123 Other options 7124 7125 - is the nftables json format any better? we will have to rebuild it 7126 with json support, may be bugger 7127 - write lua bindings to libnftables 7128 7129 Fri Feb 28 23:31:06 GMT 2025 7130 7131 adding json would add 76 + 88k to the image, but I think it would also 7132 mean we have to rewrite all the default rules in json format 7133 7134 7135 7136 with json 7137 [dan@loaclhost:~/src/liminix]$ du result/ 7138 20 result/share/doc/nftables/examples 7139 24 result/share/doc/nftables 7140 28 result/share/doc 7141 12 result/share/man/man3 7142 16 result/share/man/man5 7143 44 result/share/man/man8 7144 76 result/share/man 7145 60 result/share/nftables 7146 168 result/share 7147 36 result/etc/nftables/osf 7148 40 result/etc/nftables 7149 44 result/etc 7150 76 result/bin 7151 8 result/include/nftables 7152 12 result/include 7153 8 result/lib/pkgconfig 7154 1172 result/lib 7155 1476 result/ 7156 7157 [dan@loaclhost:~/src/liminix]$ du /nix/store/l0zsvldsskiv52b4c9b21ziq5z1qr7vn-jansson-mips-unknown-linux-musl-2.14/ 7158 84 /nix/store/l0zsvldsskiv52b4c9b21ziq5z1qr7vn-jansson-mips-unknown-linux-musl-2.14/lib 7159 88 /nix/store/l0zsvldsskiv52b4c9b21ziq5z1qr7vn-jansson-mips-unknown-linux-musl-2.14/ 7160 7161 without: 7162 [dan@loaclhost:~/src/liminix]$ du result/ 7163 20 result/share/doc/nftables/examples 7164 24 result/share/doc/nftables 7165 28 result/share/doc 7166 12 result/share/man/man3 7167 16 result/share/man/man5 7168 44 result/share/man/man8 7169 76 result/share/man 7170 60 result/share/nftables 7171 168 result/share 7172 36 result/etc/nftables/osf 7173 40 result/etc/nftables 7174 44 result/etc 7175 76 result/bin 7176 8 result/include/nftables 7177 12 result/include 7178 8 result/lib/pkgconfig 7179 1096 result/lib 7180 1400 result/ 7181 7182 7183 Sat Mar 1 23:43:17 GMT 2025 7184 7185 I don't think json is going to help because either we'd have to do 7186 7187 elements = map (f: "{{ lookup(f, \"ifname\") }}") zones.${zone} 7188 7189 and there would be null elements in the places for the interfaces that don't exist 7190 yet, or we'd have to write actual json syntax at runtime, at which point why don't 7191 we write the trad nftables syntax instead? 7192 7193 let's write a firewall .nftables file consisting of the zone set 7194 elements plus an "include" directive for the rest of the firewall. NOTE THAT 7195 we may still need to template the rest of the firewall if we want to have 7196 other variables (rate limits) in it, because the rules for that need to be 7197 inserted ahead of the rules for accepting icmp, and there's no way to 7198 do that without 7199 7200 Sun Mar 9 21:46:30 GMT 2025 7201 7202 OK, we have updating firewall zones that are good neough to pass the test 7203 (and may even work ...). We need 7204 7205 * to write a rule with a rate limit for incoming icmp6 for each interface, 7206 capping it to 5% of the interface bandwidth 7207 7208 * some place to say what the bandwidth is, which I am thinking we could do with 7209 a passthru attribute of some kind on services 7210 7211 { 7212 run = "mdcnvdfngkj"; 7213 name = "dtret"; 7214 properties.bandwidthKbps = 80000; 7215 } 7216 7217 and insert logic in the up/run commands to copy the properties to 7218 outputs. Or we could use `data` or `env` directories, which means 7219 that the properties would be there even while the service is down, if 7220 that's a concern, but would need a different way to look them up. And 7221 also, would we allow them to change? What if there's some kind of interface 7222 where we _can_ interrogate the bandwidth at runtime? 7223 7224 Mon Mar 10 21:00:17 GMT 2025 7225 7226 The idea that occured to me at lunchtime is "what if we made the 7227 (svc:output ) method fall back to properties if no output was present". 7228 To do this, we'd have to 7229 7230 (1) arrange for /nix/store/eeee-service/.properties to exist 7231 7232 - add properties attribute to service functions 7233 - write them to .properties in liminix-tools/services/builder.sh 7234 - make sure they get passed through whne provided to all the service 7235 builder functions 7236 7237 [done] (2) pass the store directory to svc.open instead of ..../.outputs 7238 7239 (3) make service:output look in both places 7240 7241 (4) write the damn firewall rule 7242 7243 Mon Mar 17 21:13:36 GMT 2025 7244 7245 Argh why is it never simple? 7246 7247 We need to write a rate-limiting firewall rule for each interface to 7248 restrict icmp on that interface. This is not easy to reconcile with 7249 putting them in default-rules because how do we generate multiple 7250 array elements by config file templating? 7251 7252 There are two things in my mind now: 7253 7254 1) could we have some better way of manipulating the firewall rules 7255 such that the rules from different modules are composable 7256 7257 this is complicated somewhat by ordering: if every rule in a chain is 7258 "drop" or "accept" then it's easy to add another, but if the same 7259 chain does first one then the other, doing the other and then the one 7260 will not work 7261 7262 today we do e.g. 7263 7264 input-ip6 7265 -> reject-bogons 7266 -> accept non-bogus-icmp 7267 -> process per-zone allowlist 7268 -> allow established,related on wan 7269 -> allow all on lan (so why did we need an allowlist?) 7270 7271 could we express this in a less sequential form? the 7272 specification of what's allowed 7273 7274 7275 input-ip6 for wan: is input-ip6 with the wan allowlist 7276 input-ip6 for lan: is input-ip6 with the lan allowlist 7277 7278 7279 input-ip6 for ppp0: is input-ip6 with the wan allowlist with a rate 7280 limit for icmp 7281 7282 ssh module wishes to modify the allowlist for lan/wan/both so that 7283 it includes port 22 7284 7285 am wondering if we could do default deny and _all_ the rules 7286 (except for bogons) are allows 7287 7288 maybe we have the concept of "subtraction": a rule can be an allow 7289 preceded by some number of drops which (at least by convention; this is 7290 probably hard to enforce) are "carve outs" of the packets that are 7291 being allowed. 7292 7293 ... it's hard to express the forward-ipv6 in these terms, though. 7294 we end up with "some drops and then multiple accepts" 7295 7296 we have 7297 (and (not (or drop1 drop2 ...)) (or accept1 accept2 ...)) 7298 7299 and to add ssh we need to break into the second clause instead of 7300 composing at the top level 7301 7302 (and (not (or drop1 drop2 ...)) 7303 (or accept1 accept2 accept-ssh...)) 7304 7305 7306 then the icmp bogons composite rule is "drop weird icmp and then 7307 allow what's left" 7308 7309 (side note: maybe we could use a map to do interface name -> bandwidth 7310 for rate limiting) 7311 7312 7313 7314 7315 a composite rule might be a bunch of denies and then an allow 7316 for anything the 7317 7318 2) some kinda syntax for referencing outputs (or properties) that's not 7319 just string interpolation 7320 7321 7322 ---- 7323 7324 I think we could address the immediate problem by writing a rule for 7325 rate-limiting that looks up the rate in a map, and some maps (with 7326 extraText) that get the rates from service properties? And that would 7327 suffice for addressing the RoS audit, at least 7328 7329 Tue Mar 18 18:48:22 GMT 2025 7330 7331 Unless the interface exists, we do not (at least, may not) know its 7332 name because that's an output. So the fact that it has a permanent 7333 property is not per se terribly useful 7334 7335 limit rate 50000 bytes / minute accept 7336 7337 7338 nft add rule ip nat postrouting snat to ip saddr map { 192.168.1.0/24 : 10.0.0.1, 192.168.2.0/24 : 10.0.0.2 } 7339 7340 nft add rule table-ip input-ip4 ip daddr 2.2.2.3/32 limit rate iifname map { "eth0": 10, "ppp0": 20 } kbytes/second accept 7341 7342 nft add map 'table-ip intf-limits { typeof 5000000 ; elements = { lan: 50000000, ppp0: 3500000 } ; }' 7343 7344 OK, we can't do rate lookup in a map because the nftables grammar only 7345 supports a numeric literal for limit_rate_bytes. so we're back to writing 7346 a collection of rules, one for each interface with an ifname output, 7347 that sets the limit for that interface 7348 7349 * we could do this all in one element of a rules list, with newlines 7350 between each actual rule 7351 7352 * we could add extraText to the ruleset syntax - but does it go at the 7353 start of the rules or the end or somewhere in the middle? this is 7354 almost worse 7355 7356 * we could pick up where we left off on march 17 and redesign the 7357 firewall module 7358 7359 gonna be option 1 isn't it? 7360 7361 Tue Mar 25 00:13:31 GMT 2025 7362 7363 the logic for watch-outputs is not correct 7364 7365 (1) when we first read outputs, how do we know if the controlled 7366 service is up to date wrt those outputs? we should perform the action 7367 for the first time after reading initial state and before waiting 7368 7369 (2) it isn't writing the new outputs back into trees, so will refresh 7370 continually after the fisrt change 7371 7372 set tree to empty for each service 7373 loop: 7374 for each service 7375 read new tree 7376 if different 7377 do action 7378 set tree = new tree 7379 wait for changes 7380 next iteration 7381 7382 Fri Mar 28 23:25:49 GMT 2025 7383 7384 the ppp connection doesn't come back after a&a reset the lns, this has 7385 happened twice now. Possibly-relevant log entries: 7386 7387 7388 2025-03-27 01:10:20.189135502 rotuer dhcp6c.prefix.dhcp6c.wan.link.pppoe.int.link.a.192.168.8.1 Executed "ip address change 2001:8b0:de3a:40de::1/64 dev int valid_lft 5682 preferred_lft 5682", exit code true 7389 2025-03-27 01:10:20.189182063 rotuer dhcp6c.addr.dhcp6c.wan.link.pppoe.wan.link.pppoe Executed "ip address change 2001:8b0:1111:1111:0:ffff:d9a9:14de/128 dev ppp0 valid_lft 5682 preferred_lft 2082", exit code true 7390 7391 # am assuming these are just normal updates from remote dhcp6 7392 7393 2025-03-27 01:11:19.179528428 rotuer wan.link.pppoe rcvd [LCP TermReq id=0x0] 7394 2025-03-27 01:11:19.179542189 rotuer wan.link.pppoe LCP terminated by peer 7395 2025-03-27 01:11:19.179545869 rotuer wan.link.pppoe Connect time 54273.0 minutes. 7396 2025-03-27 01:11:19.179550109 rotuer wan.link.pppoe Sent 3022278191 bytes, received 2202783935 bytes. 7397 2025-03-27 01:11:19.183056872 rotuer wan.link.pppoe sent [LCP TermAck id=0x0] 7398 # ppp died 7399 2025-03-27 01:11:19.183071352 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: carrier => 0 event on 7400 2025-03-27 01:11:19.183076072 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RS (Network unreachable) 7401 2025-03-27 01:11:19.183081592 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Starting RELEASE transaction (timeout 4294967295s, max rc 5) 7402 2025-03-27 01:11:19.183086312 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Send RELEASE message (elapsed 0ms, rc 0) 7403 2025-03-27 01:11:19.183091992 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RELEASE message to ff02::1:2 (Network unreachable) 7404 2025-03-27 01:11:19.183096392 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: (re)starting transaction on ppp0 7405 7406 # dhcp6c noticed no ppp so tried to release address. it can't because 7407 # ppp is down. how did it know it was time to say byebye? 7408 7409 2025-03-27 01:11:19.198178337 rotuer int.link.a.192.168.8.1.dnsmasq dnsmasq-dhcp[631]: router advertisement on 2001:8b0:de3a:40de::, old prefix for int 7410 2025-03-27 01:11:19.199068668 rotuer dhcp6c.prefix.dhcp6c.wan.link.pppoe.int.link.a.192.168.8.1 Executed "ip address del 2001:8b0:de3a:40de::1/64 dev int", exit code true 7411 # our prefix delegation is no longer ours 7412 7413 2025-03-27 01:11:19.199381632 rotuer dhcp6c.addr.dhcp6c.wan.link.pppoe.wan.link.pppoe ip: RTNETLINK answers: Address not available 7414 2025-03-27 01:11:19.199401552 rotuer dhcp6c.addr.dhcp6c.wan.link.pppoe.wan.link.pppoe /nix/store/anz85qjb2qw084f5xhpx4qndp260xr9i-lua-tty-aarch64-unknown-linux-musl/bin/lua: ...ch64-unknown-linux-musl-0.1/share/lua/5.3/anoia/init.lua:93: Error executing "ip address del 2001:8b0:1111:1111:0:ffff:d9a9:14de/128 dev ppp0" (exit) 7415 7416 this is the ip6 address that ppp negotiated. is this command failing because 7417 it was removed already? 7418 7419 [....] 7420 2025-03-27 01:11:25.545493523 rotuer wan.link.pppoe Script /nix/store/9n2314z07z1g9dd3sqr0kndinjv0vma3-ip6-up started (pid 10346) 7421 2025-03-27 01:11:25.551376155 rotuer wan.link.pppoe Script /nix/store/9n2314z07z1g9dd3sqr0kndinjv0vma3-ip6-up finished (pid 10346), status = 0x0 7422 2025-03-27 01:11:25.552949055 rotuer wan.link.pppoe sh: write error: Broken pipe 7423 2025-03-27 01:11:25.553494501 rotuer wan.link.pppoe Script /nix/store/jkvnjhiqjahydmns33z5lni1hf9sl3x8-ip-up finished (pid 10345), status = 0x1 7424 2025-03-27 01:11:26.596566491 rotuer wlan0.link.hostapd wlan0: STA-OPMODE-SMPS-MODE-CHANGED 96:75:33:6c:6b:86 static 7425 2025-03-27 01:11:26.656620107 rotuer wlan0.link.hostapd wlan0: STA-OPMODE-SMPS-MODE-CHANGED 96:75:33:6c:6b:86 off 7426 2025-03-27 01:11:27.189590642 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RS (Network unreachable) 7427 7428 it's supposed to send only one notification (each up-script only sends 7429 if an output the other has created is there). 7430 7431 ~~maybe we're not wiping the outputs when it terminates?~~ checked, we are. 7432 7433 7434 2025-03-27 01:11:27.279458704 rotuer int.link.a.192.168.8.1.dnsmasq dnsmasq-dhcp[631]: no address range available for DHCPv6 request via int 7435 2025-03-27 01:11:27.557594914 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send SOLICIT message to ff02::1:2 (Network unreachable) 7436 2025-03-27 01:11:31.193583256 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RS (Network unreachable) 7437 7438 Hypothesis 7439 7440 1) when the remote end bounces ppp, s6 restarts the ppp process 7441 but this doesn't cause dependent services to notice, so odhcpc, 7442 which is bound to the interface, doesn't see that we now have a 7443 new interface but with the same name as the old one 7444 7445 and/or 7446 7447 2) there is a race between acquire-*.fnl starting and the 7448 prefix/address outputs being updated so that we miss the first 7449 value change. 7450 7451 Tue Apr 1 21:29:03 BST 2025 7452 7453 what to do with the web site? 7454 7455 * we could add the "community" and "development" pages to the manual, 7456 but we probably don't want the blog entries to be in the liminix git 7457 repo because workflow 7458 7459 * if we do that, the ->html conversion in the manual needs to have 7460 better nav 7461 7462 links we want on the homepage 7463 7464 "I am new to Liminix and want to find if it's for me" 7465 - introduction/tutorial (how do i use it) 7466 - hardware list (is my device supported) 7467 - which includes how to install the system 7468 7469 "I am ready to run, and need a reference for stuff" 7470 [this is a merge of the existing config and admin sections] 7471 - what is a module, what is a service 7472 - how to do secrets 7473 - list of service and module options 7474 - using a running system: services and logs 7475 (move controlled services stuff up a level, its not runtime) 7476 - updating/changing the configuration 7477 writable fs: use outputs.updater 7478 readonly fs: use levitate 7479 7480 "I want to stay in touch/find out what's new" 7481 - blog 7482 - mailing lists 7483 - irc 7484 - CoC 7485 7486 "I want to contribute" 7487 Developer manual, plus 7488 - mailing lists 7489 - git repo 7490 - how to send patches 7491 - also transplant "module implementation" here 7492 - porting 7493 - how to bring up 7494 - what we look for in a new device patch (kconfig style 7495 7496 7497 perhaps we can get rid of generic "installation" in the manual 7498 and have each method transcluded into the hardware device descriptions 7499 7500 7501 7502 see also: 7503 1) "Continuous updates" is misleading as it gives the impression 7504 downstreams would be restarted if an upstream restarts. Not true. 7505 We should describe secrets subscriber here 7506 7507 7508 ----- 7509 7510 leaving us with these titles remaining 7511 7512 * tutorial 7513 * hardware 7514 * reference (could just call it "manual") 7515 * development 7516 * blog 7517 7518 and these which get moved 7519 7520 * configuration/admin -> tutorial 7521 * Module options -> sublevel in Reference 7522 * outputs -> transcluded in hardware 7523 (might also add as an appendix, so that porters can see all in one place) 7524 7525 maybe also footer link for 7526 - CoC 7527 - privacy policy 7528 - ? 7529 7530 source for all of these except blog and landing page is the doc/ folder 7531 7532 landing page will have some big rectangles that essentially duplicate 7533 the menu items 7534 7535 ---- 7536 7537 to get there from here: 7538 7539 1) we need to know the heading titles for a ToC to use as drop-down 7540 menus in the template, so will have to pass twice over the documents. 7541 Note that the same template is used for each doc so we can't expect the 7542 titles of the other docs to be provided by pandoc as variables 7543 7544 (but maybe we could pass them in on the command line so as not to 7545 rewrite template.json?) 7546 7547 note that we also need to use the template with the blog files, 7548 although for those we also need title and date frontmatter 7549 7550 if the blog sources the template at build time to make pages, it's 7551 always going to be out of sync with the manual. 7552 7553 would javascript be acceptable? 7554 7555 should we just monorepo the two things together? we could run jekyll as 7556 part of the doc target, or even pandoc the markdown directly 7557 7558 apparently myhtic.telent.net is running an rsync server? 7559 7560 1) it needs doc/ removing from its path 7561 path = "/var/www/blogs/www.liminix.org/_site/doc"; 7562 in hosts/myhtic.nix 7563 (and moving to rmeote) 7564 7565 2) make install should add doc to the path, for the manual, 7566 and not for the blog entries which should be in yyyy/mm/dd folders 7567 7568 Sun Apr 6 17:48:33 BST 2025 7569 7570 This is all getting quite involved and I'm not convinced it's the 7571 simplest way forward 7572 7573 - convert the manual to asciidoctor and make it have a sidebar 7574 - use --embedded flag to splice in header/footer 7575 7576 - the landing page can have big display-sized links to different 7577 manual sections and to the blog 7578 7579 - get rid of the blog content that's not actually posts, merge it into 7580 the manual 7581 7582 - the blog also needs the header/footer spliced in 7583 7584 site nav has three options: "home", "news", "manual" 7585 - unless we want to put the manual deeplinks from the homepage in 7586 there as well 7587 - if we do that, need to indicate somehow that they're part of the 7588 manual, not distinct from it 7589 7590 do we need to move urls around? maybe not so much 7591 7592 what to do about the "hardware" page? 7593 7594 - do we really expect the reader to scroll through every device 7595 that's not theirs? 7596 - there's going to be a lot of repeated text for similar devices 7597 - "generic" instructions might go in the body of the manual with 7598 devices as an appendix. However, you have to read the relevant 7599 appendix section to make sense of the generic instructions anyway. 7600 If only we had hypertext 7601 7602 7603 Mon Apr 7 23:49:40 BST 2025 7604 7605 DOC TODO 7606 7607 [done] * need to explain the mailing lists and IRC somewhere 7608 7609 [done] * markup the devices section as an appendix. 7610 7611 [done] * docs for outputs? maybe appendix ii 7612 7613 [done] * move "module implementation" from admin to dev (maybe?) 7614 7615 [done] * CI: move from website to Development 7616 7617 [done] * move info on the Community page to Introduction 7618 7619 [halfarsed done] * write doc for porting to new hw 7620 - how to bring up 7621 - what we look for in a new device patch (kconfig style etc) 7622 7623 [done] * add rsyncy thing to rmeote 7624 7625 * we still have serial connection guff in both installation and 7626 development sections 7627 7628 * convert all the embedded rst to adoc 7629 7630 * remove pages that are not posts from jekyll blog 7631 7632 * landing page 7633 7634 * "Continuous updates" is misleading as it gives the impression 7635 downstreams would be restarted if an upstream restarts. Not true. 7636 We should describe secrets subscriber here 7637 7638 * some basic css to make it not the same as (I assume) every single 7639 other asciidoc site 7640 7641 Mon Aug 11 23:36:29 BST 2025 7642 7643 Reviewing notes from March: I *think* 53c6d506cf1a3fbc8df0e298cf6b988c808fdbe5 7644 was intended to fix the bug where the internet stops working at 1am sometimes, 7645 but I'm guessing it was never deployed 7646 7647 Review how we do the deploy. I'm guessing it was 7648 7649 make -C ~/src/telent-nixox-config rotuer install-rotuer 7650 7651 Consider doing something like /etc/os-release (q.v. os-release(5) ) so 7652 we can tell what version of what sw a particular box is runningg 7653 7654 Wed Aug 27 21:03:04 BST 2025 7655 7656 NAME=Liminix 7657 ID=liminix 7658 PRETTY_NAME=Liminix 7659 VERSION=1.0 7660 VERSION_ID=1.0 7661 7662 os-release doesn't have a good field for the configuration and nixos 7663 doesn't seem to add one. I have BUILD_ID "25.05.805252.b43c397f6c21" 7664 but doesn't change it when config changes (maybe it's the nixos channel version?) 7665 7666 we'd like to record 7667 7668 nixpkgs git commit 7669 liminix git commit 7670 hash of the toplevel 7671 or some other derivation that's common to every liminix install type 7672 7673 Sat Oct 4 17:23:03 BST 2025 7674 7675 Log shipping, how do we do log shipping 7676 7677 1) victorialogs accepts log messages over http in jsonline format 7678 2) we should put https in front of it though because reasons 7679 3) we already wrote certifix, though 7680 4) apparently we also have log shipping? logtap tees stdin to {stdout,socket} 7681 and the config logging.shipping.service is a longrun that sends its input 7682 to whatever the log collector is 7683 7684 so we need 7685 7686 a) a log shipping service that opens an http(s) connection to victorialogs 7687 b) setup victorialogs to proxy ssl/authnz (probably just use socat here) 7688 7689 pkgs/incz and s6-tlsclient probably do most of (a) already 7690 7691 cat pkgs/logshippers/test-data | result/bin/victorialogsend http://localhost:9428/insert/jsonline 7692 7693 Sun Oct 5 23:35:35 BST 2025 7694 7695 Fucking leap seconds. 7696 7697 On my router we have 7698 7699 # echo | s6-tai64n ;date 7700 @4000000068e2f0d3257dc09b 7701 Sun Oct 5 22:26:54 UTC 2025 7702 epoch timestamp 1759703214 7703 7704 #x0000000068e2f0d3 is 1759703251 so this timestamp is the actual real 7705 number of atomic seconds since TAI epoch time. It is 37 seconds ahead 7706 of the time shown by date, because UTC was defined as TAI-10 in 1972, 7707 and there have been 27 leap seconds since 7708 7709 So to format our TAI64 timestamp as UTC, we must deduct from it 10 + the 7710 number of leap seconds that had occured by whatever time it was 7711 7712 Mon Oct 6 22:26:05 BST 2025 7713 7714 1) We need to make a logging.shipping.service that runs something like 7715 7716 ${pkgs.logshippers}/bin/victorialogsend http://loghost:9428/insert/jsonline | socat -t 4 -v - tcp-connect:localhost:9428" 7717 7718 but using the s6 thingies instead of socat 7719 7720 2) then see if logs are arriving in realtime 7721 7722 3) then we need to dick around with tls 7723 7724 4) then figure out backfilling logs when the shipper is not running 7725 7726 * backfilling should operate only when the loghost is reachable 7727 * maybe the backfill service can depend on whatever service is supposed 7728 to achieve network connectivity 7729 * probably we prefer some actual test for reachability of that specific 7730 host. maybe it's on the lan we're the rotuer for and hasn't done dhcp yet 7731 * it should send the oldest->newest segment by doing whatever the 7732 logshipper service does, and stop when it gets to the "online" cookie 7733 7734 * if the live shipper dies, how do we know that more backfill will be needed 7735 when the loghost comes back 7736 7737 - we need to detect when its down 7738 - log shipper can periodically checkpoint to disk the last time it wrote 7739 - backfill ckecks for checkpoint existence and skips forward until 7740 it gets to logs later than the checkpoint 7741 - if the shipper has been down and then up, its new instance cannot 7742 touch the checkpoint until the backfill is done 7743 7744 bottom line is you can't have more than one log writer unless the progress 7745 of each log writer is tracked separately 7746 7747 Tue Oct 7 17:44:13 BST 2025 7748 7749 ${s6-networking}/bin/s6-tcpclient loghost 9428 ${pkgs.logshippers}/bin/victorialogsend http://loghost:9428/insert/jsonline 7750 7751 need to get the logs from gl-ar750 -> bordervm -> loaclhost 7752 7753 bordervm can see 10.0.2.10 so it needs to forward between a listening 7754 port on 10.0.0.1 to 7755 7756 Our use of s6-ipcserverd is weird. After some reveng it looks like what 7757 we're doing is 7758 7759 dup() fd 1 onto fd 12 7760 run a forking server that listens for socket connections 7761 fork a child for each conection with stdio connected to the socket 7762 in the child 7763 dup() fd 12 to fd 1 so we're writing to the parent's stdout 7764 cat stdin to stdout which is actually the parent's stdout 7765 7766 this is non-obvious but perhaps it works? except that we're not 7767 actually getting any output from it that I can see, so perhaps it 7768 doesn't work? 7769 7770 there's also the question of what happens when the shipper dies because 7771 it can't contact the peer, because we would like something to notice that 7772 log shipping broke - and do we need to remove the socket before logcat 7773 fills up its send buffer and stalls out? 7774 7775 * we could write a tiny C program that calls accept() on fd 0 and 7776 use it in place of s6-ipcserverd 7777 7778 * we could give up on having the log shipper be a service and 7779 it could just be a command pipeline that gets ipcserver prepended to it, 7780 but then we're still forking 7781 7782 * we could decree that the shipper has to open and listen the socket itself 7783 7784 is the s6-rc pipeliney thing really worth having here? 7785 7786 would a fifo (named pipe) be a better fit for the problem here? 7787 7788 * shipper can open it non-blocking for read 7789 * logtap can open it non-blocking for write (and check ENXIO) 7790 - or nb for read+write and check for errors on writes 7791 7792 is there any use case for either end to block? it won't hurt if the 7793 shipper blocks until logtap 7794 7795 1) this is a significant change to logtap as the fd for the fifo will 7796 always be >= 0, and we check connectedness based on the return value from 7797 write() 7798 7799 we should try and keep some memory of whether it was (dis)connected 7800 recently (1) so we don't copy every single message to the fifo when we 7801 know there's nothing there (is this actually bad though?) and (2) 7802 because we can print the fifo state into the logs and thus have an 7803 accurate record of which messages were sent contemperaneously vs not 7804 so that backfill can deal with gaps 7805 7806 2) but it makes the shipper simple because it can open the fifo as if 7807 it were a regular file 7808 7809 7810 Wed Oct 8 07:15:39 AM BST 2025 7811 7812 A fifo instead of a unix socket would be a better fit and avoid the 7813 forking, but it means basically rewriting logtap 7814 7815 * if there was an error writing the fifo because the peer is not 7816 connected, we don't want to spam it but instead to wait before 7817 retrying. and log to stdout that it failed 7818 7819 * when we do the first write (and only the first write) after a 7820 backoff expires, we should log to stdout if it is successful 7821 7822 fifo state is [in_timeout, uncertain, good] 7823 7824 #define FIFO_STATE_GOOD (-1) 7825 #define FIFO_STATE_TIMEOUT_EXPIRED (0) 7826 #define FIFO_STATE_TIMEOUT_MAX (50) /* ? probably going to depend on log volume */ 7827 7828 7829 7830 it's ok for logtap to make the fifo when it starts: it's started by 7831 s6-svcan-log which is part of the early services that are running before the 7832 service manager starts. https://skarnet.org/software/s6-linux-init/overview.html 7833 7834 , include-what-you-use pkgs/logtap/logtap.c # this is handy 7835 7836 7837 Wed Oct 8 07:45:02 AM BST 2025 7838 7839 For posterity, here is how we make a rotuer test board send logs to 7840 victorialogs on loaclhost 7841 7842 services.wan-address-for-logs = 7843 let 7844 interface = config.hardware.networkInterfaces.wan; 7845 addr = svc.network.address.build { 7846 inherit interface; 7847 family = "inet"; 7848 address = "10.0.0.10"; 7849 prefixLength = 24; 7850 }; 7851 in 7852 svc.network.route.build { 7853 target = "10.0.0.1"; 7854 inherit interface; 7855 via = "10.0.0.10"; 7856 metric = 1; 7857 dependencies = [ addr ]; 7858 }; 7859 7860 logging.shipping = { 7861 enable = true; 7862 service = longrun { 7863 name = "victorialogsend"; 7864 run = '' 7865 ${pkgs.s6-networking}/bin/s6-tcpclient 10.0.0.1 9428 ${pkgs.logshippers}/bin/victorialogsend http://loaclhost:9428/insert/jsonline 7866 ''; 7867 dependencies = [services.wan-address-for-secrets]; 7868 }; 7869 }; 7870 7871 and also need to forward conections from bordervm to the endpoint 7872 exposed by qemu 7873 7874 [liminix@border:~]$ socat tcp-listen:9428,fork,reuseaddr tcp-connect:10.0.2.10:9428 7875 7876 However it does not appear to to be stopping when the log shipper 7877 connection is down :-( 7878 7879 7880 @4000000068e681261d2ff716 rotuer victorialogsend s6-tcpclient: fatal: unable to connect to a suitable IP address 7881 @4000000068e6812722751fbc rotuer victorialogsend s6-tcpclient: warning: unable to connect to 10.0.0.1 port 9428: Connection refused 7882 @4000000068e68127227c00d0 rotuer victorialogsend s6-tcpclient: fatal: unable to connect to a suitable IP address 7883 [endless repeats] 7884 7885 hypothesis: some kind of buffery thing going on in the s6-rc pipeline 7886 where it would rather try to respawn the consumer than signal to the 7887 producer that it's broken, it may be that a pipeline is the wrong model here 7888 7889 ... 7890 7891 ok, it stopped eventually but it was quite eventual. When I restarted, 7892 it logged all the buffered messages as well. Is this actually bad? 7893 7894 given that the producer part of this pipeline has reduced to "cat" 7895 maybe we could just(tm) make log shippers accept a pathname to read from 7896 7897 options: 7898 * log shipper becomes a script not a service, so it can accept a pathname 7899 * do magic to the service to prepend stdin redirection to its `run` 7900 attr 7901 * just hardcode the pathname 7902 * environment variable for the pathname? 7903 7904 7905 hint: print a file one line per second with pv 7906 cat THOUGHTS.txt | nix-shell -p pv --run " pv --quiet --line-mode --rate-lim 7907 it 1" | ./result/bin/logtap /tmp/feefifo backfill 7908 7909 7910 Thu Oct 9 21:40:05 BST 2025 7911 7912 TODO 7913 7914 - make it go over tls 7915 - try it on the annex wireless extender or somewhere 7916 - backfilling 7917 - deploy rotuer 7918 7919 system.service.tls-certificate.certifix-client 7920 7921 Sat Oct 11 00:28:36 BST 2025 7922 7923 https://serverfault.com/a/976551 7924 tcpdump ether[0x0c:2] == 0x8863 or ether[0x0c:2] == 0x8864 7925 7926 we're getting weird problems where ppp fails to work when I add the 7927 certifix client 7928 7929 7930 Sun Oct 12 23:03:50 BST 2025 7931 7932 for posterity: 7933 7934 1) we never found out what the weird problems were, I am wondering if 7935 there's some kind of contention or race condition when pppoe and ip 7936 are happening on the same interface 7937 7938 2) openssl won't run on mips 24kc unless compiled with soft-float, 7939 because that cpu has no fpu (the kernel fpu support is enabled but 7940 maybe it is not sufficent) 7941 7942 3) certifix won't run before the time is set correctly because the ca 7943 cert has before and after times and 1970 is not in that range. we 7944 might consider touching a file in /persist on rebot, for devices that 7945 have /persist 7946 7947 4) although certifix-client works just fine(* almost) on loaclhost, it 7948 hangs indefinitely on gl-ar750. On loaclhost it prints this 7949 7950 40A70C42647F0000:error:0A000126:SSL routines::unexpected eof while reading:ssl/record/rec_layer_s3.c:693: 7951 7952 which I interpret to mean that certifix is not closing the ssl thingy 7953 properly. (OpenSSL 3 feature to prevent truncation attacks) 7954 7955 7956 Mon Oct 13 12:23:35 BST 2025 7957 7958 Missing ssl close_notify is probably not what's causing the certifix-client 7959 to stall, though it bears considering 7960 7961 A truncation attack is: can a mitm alter the outcome by dropping 7962 packets so that an endpoint gets a partial message (or no message) 7963 that it interprets as a full message? if we expect close_notify then 7964 the answer is always "no" because any exchange that doesn't have a 7965 close_notify can be rejected as not a full exchange. Conversely, if we 7966 have ways at the upper protocol level of checking that a full message 7967 was sent (e.g. content-length, or chunked encoding) then we don't need 7968 the close_notify beause we know already if we got the whole thing. 7969 7970 The problem is also not the weird iptables forwarding stuff on 7971 bordervm, because I tried disabling that and using socat instead which 7972 hangs in the same way 7973 7974 Could we replace the use of fetch-freebsd here with s6-tlsclient and 7975 some shonky homerolled http header generation? 7976 7977 7978 Mon Oct 13 17:55:30 BST 2025 7979 7980 could we convert certifix-client to use s6-tlsclient instead of fetch? 7981 7982 (would it help?) 7983 7984 Sun Oct 19 20:41:13 BST 2025 7985 7986 > (would it help?) 7987 7988 It seems to behave better. 7989 7990 Now we need to hook all the things up: specifically, to put an https 7991 proxy in front of victorialogs that checks the client certificate is 7992 signed by the correct CA 7993 7994 This (probably) means that the server certificate for the proxy also 7995 needs to be signed by the same CA. Or does it? Maybe it doesn't 7996 7997 Mon Oct 20 17:56:56 BST 2025 7998 7999 * we need to install the ca cert onto the device (certifix-client 8000 already does) 8001 8002 * log service to reference outputs of certifix-client 8003 8004 * ok, *technically* we don't need to use our own CA to sign the cert 8005 for victorialogs, but if we use letsencrypt then we do need to put 8006 the letsencrypt root/intermediate certs on the device 8007 8008 question: are there other things a standard ca bundle would be 8009 useful for? they'd have to be convincing, it's 500k 8010 8011 so ... 8012 8013 1) we teach certifix to sign for server use as well as client use, or 8014 8015 2) (given that certifix and victorialogs are on the same box) we make the 8016 victorialogs cert in a cronjob on the server, or 8017 8018 3) we use openssl commands to generate a CSR with extendedKeyUsage=serverAuth 8019 requested extension and make certifix not overwrite that if if exists 8020 8021 openssl req -config <(printf '[req]\nprompt=no\nattributes=attrs\ndistinguished_name=DN\n[DN]"C=GB\nST=London\nO=Telent\nCN=loghost\n[attrs]\nchallengePassword=loves labours lost') -newkey rsa:2048 -addext "extendedKeyUsage = serverAuth" -addext "subjectAltName = DNS:loghost.lan,DNS:loghost,DNS:loghost.telent.net" -nodes -keyout private/loghost.key --out certs/loghost.csr 8022 8023 test with openssl req -noout -in certs/loghost.csr -text 8024 8025 curl --cacert certs/ca.crt -v -H 'content-type: 8026 application/x-pem-file' --data-binary @certs/loghost.csr 8027 https://localhost:9191/sign -o loghost.crt 8028 8029 Tue Oct 21 22:19:45 BST 2025 8030 8031 Once we have per-tag RSS fields in ww.telent.net we can import them to 8032 jekyll using https://import.jekyllrb.com/docs/rss/ 8033 8034 Wed Oct 22 18:18:10 BST 2025 8035 8036 How should we do backfill? 8037 8038 * we can find out which logs were written live because there 8039 are START and STOP markers 8040 8041 * the first time backfill runs, we assume that no logs were shipped 8042 except the ones written live 8043 8044 * if the backfill process starts but is interrupted, we need a way for 8045 it to pick up where it left off. 8046 8047 So I think we need some kind of timestamp that says "logs older than 8048 this have been written" 8049 8050 backfill isn't really a service, it's just a process that runs until 8051 it's done 8052 8053 if backfill gets all the way to the present day without encountering a 8054 START line ... something weird is happening. whatever network outage 8055 is stopping the live logging should, we assume, stop the backfull as well. 8056 8057 backfill will want to read past a START line (but not write the 8058 live-logged lines) to see if there was a subsequent STOP line 8059 8060 if the backfill process writes to the fifo, atomic writes are 8061 gauranteed only if it writes <= PIPE_BUF bytes at a time. So we could 8062 get partial/overlapping log lines if a log message exceeds 4k 8063 8064 the backfill can spawn a separate instance of the shipping command and 8065 provide it a file consisting of the logs to be shipped. If it does 8066 this with a fifo then it can get feedback on whether the shipper is 8067 working and use that to update the backfill progress timestamp 8068 8069 there may be more than one message with the same timestamp, so it's 8070 not actually possible to pinpoint the last message sent with a 8071 timestamp alone. we could ... not care too much? 8072 8073 timestamp semantics are "I was about to start sending messages 8074 beginning with timestamp t": we update it for each message before 8075 sending 8076 8077 Fri Oct 24 19:41:16 BST 2025 8078 8079 backfill process 8080 * make a fifo 8081 * spawn second copy of log shipper with LOG_FIFO set to that fifo 8082 * send messages on stdin to fifo unless we saw them already 8083 8084 we could put this and the live shipper in an s6-rc bundle 8085 8086 we could get rid of cfg.socket as a configurable option - it's not a 8087 socket anyway, and we need two of them 8088 8089 I don't think we need customisable stop/start marker either. it 8090 doesn't have a timestamp, so there's no local context required to 8091 know that it's not a log line. We could just do 8092 8093 # started-log-shipping 8094 # stopped-log-shipping 8095 8096 Sat Oct 25 19:39:24 BST 2025 8097 8098 > we could put [backfill] and the live shipper in an s6-rc bundle 8099 8100 except that backfill is not a persistent process, it's a one-off 8101 that we want to kick off when the shipper starts, and will terminate 8102 some time thereafter 8103 8104 Could we: (1) add reading notification to logshipper; (2) have a 8105 oneshot backfill service tha depends on it? 8106 8107 Sat Oct 25 22:11:14 BST 2025 8108 8109 8110 # s6-rc -u change log-shipper-backfill-sink 8111 /nix/store/6a2v6596knn2dnbxf3qc4nfsipd1qlh5-log-shipper-backfill-source-up: line 4: mkfifo: not found 8112 /nix/store/6a2v6596knn2dnbxf3qc4nfsipd1qlh5-log-shipper-backfill-source-up: line 5: /nix/store/rphrcqc6b2ajpfy38gs95c3c5rq71jpi-logtap-m 8113 ips-unknown-linux-musl/bin/backfill: not found 8114 s6-rc: warning: unable to start service log-shipper-backfill-source: command exited 127 8115 8116 but also: 8117 8118 # s6-rc -da list 8119 certifix-C=GB-ST=London-O=Telent-OU=devices-CN=rotuer 8120 log-shipper-backfill-source 8121 log-shipper-backfill-sink 8122 8123 why did log-shipper-live start? 8124 8125 TIL: bundles can't have dependencies 8126 8127 Sun Oct 26 15:33:30 GMT 2025 8128 8129 * we need a mkfifo command, 8130 * we need to turn backfill.fnl into something we can run 8131 8132 idea: write a fennelc command. It could do 8133 8134 * turn fennel into lua 8135 * require-as-include for files in cwd 8136 * resolve paths for other required packages? 8137 8138 https://fennel-lang.org/api#search-the-path-for-a-module-without-loading-it 8139 8140 ... or a much simpler variant would be to set {LUA_C?}PATH as 8141 whatever it is in the build environment and then do fennel --compile 8142 8143 we could write it in fennel if we can avoid getting hopelessly lost in 8144 nested fennels 8145 8146 Sun Oct 26 23:01:48 GMT 2025 8147 8148 Unrelated: have we done something stupid and firewally that's blocking 8149 our dhcp6 service discovery? 8150 @4000000068fea8cc24a62eae rotuer dhcp6c.wan.link.pppoe odhcp6c[632]: Send RENEW message (elapsed 1753088ms, rc 8) 8151 8152 .... 8153 8154 oneshots won't work here (we just realise after trying it). The 8155 backfill source and sink have to be started together if they're going 8156 to work in tandem, bu the service manager doesn't consider a oneshot 8157 ready until the "start" script has completed - and the sink won't 8158 start until the source has finished. 8159 8160 So either (a) we launch the backfill as a oneshot that backgrounds, or 8161 (b) we launch it from the live shipper as a child, or (c) as a longrun 8162 that goes to sleep when it runs out of input. 8163 8164 When the backfill dies, it could be because it failed immediately, 8165 or it failed after writing some of the backlog, or it succeeded to 8166 write all the backlog. In the failure case we want it to be restarted 8167 (perhaps when conditions allow). In the success case, we want the 8168 service manager to regard backfill as "happy". If live logging fails, 8169 there will subsequently be more lines to backfill. I think we can 8170 make backfill depend on live,so it will be killed off if live dies. 8171 8172 Mon Oct 27 21:30:43 GMT 2025 8173 8174 What shall we do with 1970s timestamps? 8175 8176 - the collector will refuse them, but even if it didn't, are we going to 8177 go back to 1970 to look at them? 8178 8179 - if we log them with time=now, backfilled log messages may appear to 8180 have been sent after live messages that actually succeeded them 8181 8182 - we could figure out boot time = current time - uptime when the backfill 8183 process starts, and rewrite log line timestamps < 1991 as we send them. 8184 There might be some backwardsness as we catch up to whatever time the 8185 ntp syncs, but maybe if we subtract a few seconds that won't be too 8186 bad. We should add a field to the logs saying that the time was estimated 8187 8188 - maybe we could add boot time to the log payload 8189 8190 /proc/uptime "(including time spent in suspend)" 8191 8192 Tue Oct 28 19:35:57 GMT 2025 8193 8194 There's even a case for writing the logs with CLOCK_BOOTTIME instead 8195 of using TAI 8196 8197 Tue Oct 28 23:31:22 GMT 2025 8198 8199 backfill still trades in line-based s6-like logs so is not 8200 well-placed to add metadata 8201 8202 Thu Oct 30 21:25:10 GMT 2025 8203 8204 wap profile is not bringing up dhcpc on boot. 8205 8206 :00:16.843749368 outsdie int.link.dhcpc udhcpc: started, v1.36.1 8207 :00:17.136242303 outsdie int.link.dhcpc udhcpc: broadcasting discover 8208 :00:23.272561448 outsdie int.link.dhcpc udhcpc: broadcasting discover 8209 [....] 8210 :01:16.416571674 outsdie s6-svlisten1: fatal: timed out 8211 :01:16.419442464 outsdie s6-rc: warning: unable to start service int.link.dhcpc: command exited 99 8212 :01:16.424668369 outsdie ifwait.wlan0.link-running Executed "s6-rc -b -d change int.link.member.wlan0.link", exit code true 8213 :01:16.428327895 outsdie ifwait.lan.link-running Executed "s6-rc -b -d change int.link.member.lan.link", exit code true 8214 :01:16.456176883 outsdie int.link.dhcpc udhcpc: no lease, failing 8215 :01:16.588522107 outsdie ifwait.wlan0.link-running adding wlan0.link to int.link 8216 8217 it starts int.link.dhcpc quite early, listed dependencies include 8218 int.link 8219 lan.link 8220 wlan0.link 8221 ifwait.wlan0.link-running 8222 ifwait.lan.link-running 8223 8224 *but* the foo-running dependencies are service controllers: i.e. they 8225 are "ready" when started and control another service to go up/down 8226 when the link is running. We do it this way because otherwise 8227 services for unplugged lan ports hang forever/time out and this makes 8228 s6-rc sad 8229 8230 int.link.dhcpc has a timeout of 60 seconds and nothing else related 8231 to lan.link seems to happen while it is running. This is a problem 8232 because until lan.link gets added to the bridge, no dhcp can succeed 8233 8234 hypothesis: udhcpc is somehow starving the system of netlink events 8235 so they only arrive when it's finished. seems unlikely as we run it 8236 several times during the 60s. Even adding a sleep 5 before udhcpc 8237 doesn't change things, so hypothesis invalidated imo 8238 8239 hypothesis: s6-rc won't start anything else while waiting for 8240 int.link.dhcpc to announce it's ready, and the -running service is 8241 using it to launch the scripts that add members to the bridge. 8242 ~Doesn't seem to be, adding more prints before launching s6-rc and they 8243 don't happen.~ NOPE: I used the wrong variable name in the debug 8244 print, this probably is what's happening, on the assumption that 8245 s6-rc holds the database lock while it waits for a process to indicate 8246 readiness. 8247 8248 bottom line: we can't launch dhcp client as a permanent service if it 8249 is dependent on a controlled service, which it is if adding bridge 8250 members is implemented as controlled services, which it is because 8251 members can't be added until their links are running, which is slow 8252 for wlan and maybe never for unplugged ethernet 8253 8254 Fri Oct 31 19:59:44 GMT 2025 8255 8256 An interim solution might be: remove readiness notification from 8257 dhcpc. This 8258 8259 * would not be great for downstreams because they might have to retry 8260 a few times 8261 8262 * _but_ would fix our current problem where there's a deadlock because 8263 the thing that would unblock dhcpc would be able to actually run 8264 8265 A more general solution might be something like the uevent-rule 8266 service which uses netlink to subscribe to "bridge gets a member 8267 interface" events and starts dhcp when that happens. 8268 8269 I am concerned, a bit, that the service graph of the system becomes 8270 illegible if any random service can start other services imperatively 8271 and there's no way to introspect it. 8272 8273 Also I can't remember if it's possible to define controlled services 8274 (or dependents of controlled services) in config.services without 8275 having s6-rc try to bring them up at boot 8276 8277 ---- 8278 8279 the interim solution doesn't seem to actually work :-( 8280 8281 1970-01-01 00:00:17.013450601 outsdie s6-rc: info: service route-37a8eec1ce19-417851ee9cfd: starting 8282 1970-01-01 00:00:18.154198328 outsdie cat: can't open '/nix/store/2cl121rpxc89x26wddysfmxn0n98rmxm-int.link.dhcpc/.outputs/router': No such file or directory 8283 1970-01-01 00:00:18.165965321 outsdie s6-rc: warning: unable to start service route-37a8eec1ce19-417851ee9cfd: command exited 8284 8285 I am guessing that oneshots don't get restarted on failure as longruns 8286 do 8287 8288 Sun Nov 2 10:34:03 GMT 2025 8289 8290 Sometimes we need to restart or signal a service when the output of 8291 some other service has changed, so that it can reread its config 8292 8293 Sometimes the upness of a servive depends on some state 8294 - output of another service 8295 - other state like sysfs or netlink 8296 8297 These are different things: the first case doesn't actually affect the 8298 service tree (except momentarily, if a whole tree is restarted) 8299 whereas the second case grafts/prunes things in and out of it. 8300 8301 Is the first case a _subset_ of the second? 8302 8303 there's difference in the responsibilities, because a service can't 8304 (in this scheme) decide for itself that it should be started or 8305 stopped - that decision rests with a parent. However, it can 8306 decide to restart itself 8307 8308 If we accept that dhcp acquisition shouldn't block the service lock, 8309 it follows that services can't depend on "dhcpc is running" to 8310 signify "we have a lease". 8311 8312 dhcp-lease-watcher: looks for outputs from dhcp and starts/stops 8313 dhcp-lease-acquired when that happens. 8314 8315 Everything else depends on dhcp-lease-acquired instead of just dhcp 8316 8317 *the issue*: there's logic/policy in dhcp-lease-watcher: rules for 8318 when to start/stop the controlled service tree based on the 8319 outputs being watched. This means we can't use watch-outputs 8320 unchanged, because it signals the subscriber on _every_ change. 8321 8322 As far as I can tell/remember, every extant controller service is a 8323 handcrafted longrun 8324 8325 How would it look if dhcp-lease-watcher were in fennel? A lot like 8326 modules/dhcp6c/acquire-wan-address.fnl 8327 8328 longrun { 8329 name = "watch-${name}"; 8330 run = pkgs.runCommand "fennelc" {} "fennelc -o $out ${./dhcp-watcher.fnl}"; 8331 } 8332 8333 Sun Nov 2 23:32:01 GMT 2025 8334 8335 services.dhcp-lease-acquired is being started at boot instead of when 8336 its controller starts it 8337 8338 # s6-rc-db -d dependencies default |grep int.link-lease-acquired 8339 int.link-lease-acquired 8340 8341 seems wrong. but yet: 8342 8343 # s6-rc-db -d dependencies controlled 8344 int.link-lease-acquired 8345 8346 ====== 8347 8348 # s6-rc-db all-dependencies int.link-lease-acquired 8349 controlled 8350 int.link-lease-acquired 8351 int.link-lease-acquired-log 8352 s6rc-fdholder 8353 s6rc-oneshot-runner 8354 # s6-rc-db -d all-dependencies int.link-lease-acquired 8355 certifix-C=GB-ST=London-O=Telent-OU=devices-CN=outsdie 8356 resolvconf 8357 route-37a8eec1ce19-eb94cb82bf2a 8358 int.link-lease-acquired 8359 log-shipper-backfill-source 8360 ntp 8361 log-shipper-live 8362 log-shipper-backfill-sink 8363 8364 the 'default' service transitively depends on int.link-lease-acquired 8365 presumably because log-shipper is in the default service list. but 8366 why? 8367 8368 Mon Nov 3 18:39:21 GMT 2025 8369 8370 logshipper is a bundle, and bundles don't have 8371 dependencies. defaultStart needs to also exclude any service which is 8372 a bundle and has a controlled service in its contents 8373 8374 we need to 8375 8376 - move the dhcp watcher into the dhcpc code not the wap profile 8377 8378 - fix the ipv6 duplicate local route 8379 8380 # ip -6 route 8381 2001:8b0:de3a:40de::/64 dev lan metric 256 expires 5926sec 8382 2001:8b0:de3a:40de::/64 dev int metric 256 expires 5925sec 8383 8384 assumption: we're getting these routes with RA and they're not 8385 being removed when the lan link is added to the int bridge 8386 8387 Mon Nov 3 22:05:17 GMT 2025 8388 8389 It does actually work to flush the routes (as in c5e0567) but it just 8390 takes a very long time for the bridge to notice another RA packet. 8391 8392 ---- 8393 8394 I think we should 8395 (1) make ipv4 dhcp client a regular module like the ipv6 one 8396 (2) hide the services for the daemon and the watcher, and provide only 8397 the lease-acquired service as a thing (copy the outputs to it) 8398 (3) find out if we can use the nixos "deprecated" stuff to warn people 8399 off using the original dhcp4 service 8400 8401 ---- 8402 8403 yay 8404 8405 Wed Nov 5 18:12:44 GMT 2025 8406 8407 https://www.remlab.net/ndisc6/ could use this to trigger ipv6 8408 autoconfig on bridge 8409 8410 does a bridge always want to have ipv6 autoconfig? 8411 8412 Thu Nov 6 20:25:49 GMT 2025 8413 8414 - we would like a mechanism for setting sysctls declaratively 8415 8416 - and for the bridge module to use this to set accept_ra=0 on its 8417 member interfaces 8418 8419 - noting that the nodes in /proc/sys won't exist until the 8420 interfaces are created (which is _probably_ boot time but may be later) 8421 8422 ... so maybe a single global attrset for all sysctls isn't the right 8423 way to go, as we can't apply them all at the same time 8424 8425 the thing that makes the interface also has to apply the sysctls to 8426 it before bringing it up 8427 - noting that the liminix service name may not be the ifname 8428 if we don't know ifname at compile time (e.g. if the kernel assigns it) 8429 then "declarative" becomes problematic 8430 8431 - noting that interfaces can get renamed 8432 8433 annoyingly, there isn't a single parent directory for all the sysctls 8434 that go with an interface 8435 8436 $ find /proc/sys -name eth1 8437 /proc/sys/net/ipv4/conf/eth1 8438 /proc/sys/net/ipv4/neigh/eth1 8439 /proc/sys/net/ipv6/conf/eth1 8440 /proc/sys/net/ipv6/neigh/eth1 8441 8442 config.hardware.networkInterfaces.lan = 8443 sysctl.ipv6 = { 8444 accept_ra = 0; 8445 }; 8446 sysctl.ipv4 = { 8447 arp_accept = 0; 8448 }; 8449 }; 8450 8451 there is copy/paste reuse(sic) between modules/bridge/primary.nix and 8452 modules/network/link.nix 8453 8454 Fri Nov 7 21:48:12 GMT 2025 8455 8456 we can't just add stuff to config.hardware.networkInterfaces.lan 8457 because it's not a set of module options, it's a service. 8458 8459 Sat Nov 8 15:54:50 GMT 2025 8460 8461 OK, maybe we should make ipv6 autoconfig default _off_ and have a 8462 service to turn it on 8463 8464 - dhcp6c can require it 8465 - wap profile can require it (it should probably be using dhcp6c 8466 anyway) 8467 - router configurations shouldn't be using it anyway 8468 8469 ... but actually is there any reason not to enable configuring the 8470 link-local address? In practical terms it's really the global address 8471 (RA/RS) we want to avoid picking up 8472 8473 https://docs.manage.security.cisco.com/cdfmc/c_ipv6_addressing.html 8474 > For bridge group member interfaces, when you configure the global 8475 address on the BVI, the Firewall Threat Defense device automatically 8476 generates link-local addresses for member interfaces. 8477 8478 but 8479 8480 https://wiki.freebsd.org/crest/the-correct-way-to-configure-bridges-in-freebsd-for-ipv6-and-ipv4 8481 > From IPv6's point of view a bridge interface is a single link (just 8482 like multiple hosts connected to a physical Ethernet switch), but if 8483 there are IP addresses configured on the member interfaces of a 8484 FreeBSD bridge the kernel considers these interfaces as their own 8485 links with associated link scope. This will cause IPv6 to break. The 8486 only correct configuration is to have no IP addresses configured on 8487 the member interfaces. The IP addresses belong exclusively on the 8488 bridge interface itself. 8489 8490 https://vyos.dev/T2367 8491 > Bridge members should not have any addresses. If a bridge member has 8492 an address, the outgoing packets from the router for those addresses 8493 will leave the interface directly, but all packets coming in to the 8494 router will be directed up to the bridge. While this technically may 8495 work if rp_filter is disabled, it's asymmetric behavior that's not 8496 wanted. 8497 8498 https://github.com/vyos/vyos-1x/pull/371 8499 > Several protocols require a link-local address to work, including 8500 RA, NDP, SLAAC, DHCPv6, and OSPF 8501 8502 https://man.freebsd.org/cgi/man.cgi?bridge(4) 8503 > When the if_bridge interface has IPv6 addresses, IPv6 addresses on the 8504 member interface will be automatically removed before the interface is 8505 added. 8506 8507 8508 so I think we should default to not having any autoconfig at all, 8509 then add services 8510 - neighbor solicition/advertisement 8511 - router solicitation (depend on previous) 8512 - slaac (depends router solicitation) 8513 - dhcp6c (depends on RS (expect RA packets without A header bits)) 8514 8515 when would we want ND without RS? seems it would be a niche case 8516 8517 NDP is applicable to hosts and to routers but they play different 8518 roles: hosts send RS and routers send RA. That said, dnsmasq will 8519 deal with RA sending. 8520 8521 decision time: we start with 8522 8523 proc/sys/net/ipv6/conf/all/autoconf = 0 8524 8525 then we make a service something like 8526 svc.ipv6-autoconfig { 8527 interface = ...interfaces.lan; 8528 role = "host"; # or "router"; controls accept_ra 8529 enable = true; # default 8530 sysctl = { 8531 # other sysctls that override 8532 }; 8533 }; 8534 8535 we can make dhcp6c depend on this, or we can include it directly 8536 if we're on a slaac-only network. do we need a slaac service for 8537 discoverability? 8538 8539 Sun Nov 9 11:47:47 GMT 2025 8540 8541 the 'address' service manually sets outputs/ifname so it can be used 8542 in place of a link service. (1) autoconfig should do the same; (2) 8543 some more general means of delegation would be cool. Ideally it would 8544 also update if a service changes its outputs 8545 8546 maybe rather than scattering watch-outputs everywhere, this could be 8547 implemented in the `output`/`output_path` logic itself. Note that the 8548 fennel stuff reimplements outputs so the logic would have to be 8549 duplicated there. 8550 8551 Sun Nov 9 11:53:46 GMT 2025 8552 8553 Apropos of nothing, we might potentially be able to improve 8554 performance by having a lua server process that was able to fork 8555 for each new lua script. 8556 8557 * client conects on unix socket, passes stdin/out/err fds 8558 * check the client credentials 8559 * dup the passed fds onto 0/1/2 8560 * client sends argv as n_args\0arg\0arg2\0argn\0\0 8561 * server unpacks into scriptname, arg array; forks, runs the 8562 script 8563 * on script exit, server closes client connection 8564 * client exits 8565 8566 This would be better if it could pass _all_ open fds (because e.g. 8567 forked service might want to send readiness notification on fd 10). 8568 I think the easiest way to do that is readdir() on /proc/self/fd 8569 8570 It might be necessary to pass some environment variables as well? Or 8571 maybe it would be more reasonable to say that scripts are started 8572 with a vanilla environment 8573 8574 Sun Nov 9 23:01:47 GMT 2025 8575 8576 * There are ways to make interfaces that aren't the link service 8577 (e.g. bridges, ppp) so we should do the sysctl thing at boot. Maybe 8578 add a sysctl config option that's turned into an early service - 8579 i.e. it runs before any s6-rc stuff 8580 8581 * wap profile needs to add autoconfig service on bridge controller 8582 (does it? dhcpc depends on it). 8583 8584 * wap profile to get params for ipv4.enable and ipv6.enable 8585 8586 * dnsmasq service potentially should depend on autoconfig with { role = 8587 "router" }. Or maybe we add this to the forwarding service? 8588 8589 * do we need to change the ppp services? I don't think so because 8590 dhcp6c will run on that link anyway 8591 8592 Mon Nov 10 21:26:28 GMT 2025 8593 8594 it's actually quite hard to do this at boot, because the semantics of 8595 conf/all are very woolly and the only reliable way to e.g. set 8596 accept_ra=0 is to iterate over each interface name. Which is a 8597 problem to do declaratively if you don't kow what they're called. 8598 8599 Plan C: we'll set the sysctls for each interface when we bring it up, 8600 which will cover interfaces that exist at boot time, *and* we'll set 8601 the default values at startup, which will cover cases where the 8602 interface is created after boot (ppp, wwan, bridge etc) 8603 8604 we can't toggle accept_ra in for forwarding module because ipv6 8605 forwarding setting is system-wide not per-interface. so probably we 8606 should do it in dhcp6c 8607 8608 Fri Nov 14 08:12:08 GMT 2025 8609 8610 thinking about the lua interpreter server and wondering if we could 8611 write it in rust as an exercise 8612 8613 Sat Dec 13 21:46:15 GMT 2025 8614 8615 what do we need to do? 8616 8617 - find out why wlan5's not in the bridge 8618 (because we forgot to add it to the bridge) 8619 8620 - see if it works properly after that 8621 8622 - get rid of debug output 8623 8624 - build it properly, do video 8625 8626 - do rotuer 8627 8628 8629 Wed Mar 18 21:35:12 GMT 2026 8630 8631 After much neglect followed by some cursing, liminix now builds with 8632 nixpkgs-unstable, and I can get back to figuring out what I was doing 8633 8634 It looks like I'd finished making it ship logs to victorialogs, but maybe 8635 we should think about where victorialogs is hosted because the log server 8636 shouldn't be depending on the router to get an IP address if the router 8637 is depending on the log server to work correctly 8638 8639 admittedly the router doesn't (iirc) have a _hard_ dep on working logs, 8640 but if it's suffering in some way and this means that the victorialogs 8641 service isn't running either, then we don't have logs to diagnose the 8642 problem. 8643 8644 So, loghost needs a static ip address, and to be able to answer 8645 local requests even without an upstream connection. 8646 8647 It would be good if there were also a way to connect to it in such a 8648 situation that doesn't involve going out to the shed 8649 8650 Also, the log streaming client probably wants to hardcode an ip address 8651 for loghost.telent.net 8652 8653 Sat Apr 4 20:24:25 BST 2026 8654 8655 ht8232 rsz #206 8656 8657 TODO 8658 8659 - add levitate to outsdie 8660 - could we add levitate to the tftpboot image 8661 and use it to write the final image 8662 - levitate chroots to a subdir of /run so won't interfere 8663 with phram shenanigans 8664 8665 - single source of truth for wifi secrets 8666 8667 - flash outsdie onto gl-ar750 8668 - unplug it from the serial port 8669 - upgrade kernel and audit firewall on lenscap 8670 - upgrade kernel on old outsdie 8671 - find omnia, plug in, upgrade kernel and install rotuer config 8672 8673 8674 ## agenix 8675 8676 agenix stores secrets as age-encrypted files in the nix store, using 8677 ssh hostkeys as the keys. this might not be ideal if our hosts are 8678 generating their keys at first boot because it means they won't get 8679 secrets until after they've booted and the pubkey sucked out and 8680 the secrets re-encrypted and pushed to the device a second time. 8681 a device with a wired ethernet doesn't need wireless keys for to be 8682 ssh-accessible but this does sound potentially chicken-eggy 8683 8684 we'd need to create a keypair for the device at provisioning time, and 8685 bake the private key into the image, but not put it in the nix 8686 store. Perhaps we could append it to the image when we write it? 8687 8688 I think agenix is tied also to the nixos-rebuild lifecycle, so each 8689 device would have to be rebuilt to notice when a secret is rotated 8690 8691 ## sops 8692 8693 seems to be tailored to the EC2 use case where a hypervisor is 8694 available to say that the new instance is actually the new instance and not 8695 an interloper 8696 8697 8698 ## certifix w/sops 8699 8700 we could use a certifix cert to identify the device to the secrets 8701 service, using the pre-shared secret 8702 8703 * the master secrets list should be encrypted at rest, but this means 8704 we don't have to encrypt it for each device, just for the humans and 8705 for the https server process 8706 8707 * or we could put an age shared private key into the image, then the 8708 server process wouldn't need to contain all the decrypted secrets 8709 8710 8711 Tue Apr 14 13:16:01 BST 2026 8712 8713 we already have a client key that's used for logs. if we ignore the 8714 maybe-questionable mechanism for ensuring it belongs to the client 8715 (which is maybe solvable depending on the client hardware/flashing 8716 mechanism/filesystem etc) we can use it for authenticating access to 8717 secrets. we can keep our secrets using SOPS, encrypted using 8718 ~whatever~ (age?) , and have an https server that checks the client 8719 cert. the https server can have a private key which the sops yaml file 8720 is encrypted for 8721 8722 > aside: we cold improve the ca signing policy if 8723 > 8724 > * we had a way of making a temporary unique-per-client shared key that 8725 > the CA could check, and embedding it into the client image (preferably 8726 > without putting it in the nix store). For example, appending it to 8727 > the image and then having the client scan /dev/mtd3 starting from the 8728 > end of its filesystem to find it. Finding the end of the filesystem 8729 > would be fs-dependent (squashfs vs jffs2 vs ubifs vs ...) 8730 > 8731 > * or we could rely on functioning uboot with a writable env 8732 > 8733 > * or we could embed some other existing unique info into the csr. MAC 8734 > address? 8735 8736 on the server side we need 8737 8738 - https and client cert checking 8739 - store the decrption key securely (e.g. with mlock()) 8740 - decrypt the secrets file and serve it on request 8741 8742 could we use some kind of fancy loopback mounts/private filesystem 8743 namespace thing to good effect here? I don't think we have a problem 8744 that they'd solve 8745 8746 we can't just decrypt at startup unless we're going to restart every 8747 time the file changes, which doesn't seem less complicated as it adds 8748 some kind of inotify to the thigs that can go wrong. 8749 8750 fennel or rust? 8751 8752 Wed Apr 29 11:52:58 BST 2026 8753 8754 I left the slops code on my laptop, but we can fake it using nginx for 8755 testing purposes 8756 8757 Need 8758 8759 - a source of secrets accessible through bordervm, preferably at the 8760 same url as it would be in "prod" 8761 8762 - for outsdie, x 8763 8764 Tue Jul 28 13:11:59 BST 2026 8765 8766 I have very little recollection of any of this. Apparently I wrote 8767 something called slops, and I am guessing from context that it serves 8768 http requests for secrets but only to clients that present particular 8769 client certs. 8770 8771 * certifix-client module will create a client cert by talking to 8772 certifix (corollary: it needs to be able to see certifix, which 8773 therefore ideally should be accessible through bordervm and also 8774 directly (and should not be down when the internet is broken?)) 8775 8776 * secrets.outboard uses json-to-fstree to populate outputs based on 8777 the result of http-ing a json file. currently it uses basic auth 8778 8779 maybe https+basic auth is sufficient? 8780 8781 in other news, once again our dropbear patches no longer apply 8782 8783 Thu Jul 30 21:12:55 BST 2026 8784 8785 let's use certifix-client to make a client cert, and a vhost on 8786 localhost with mTLS that hosts a json file 8787 8788 * need to check that libfetch can present a client cert 8789 8790 * it would be cool if the file was encrypted at rest, but maybe this is a 8791 solved problem if we use fscrypt or something 8792 8793 * let's face it, slops basically did this, leveraging sops/age for the 8794 decryption 8795 8796 so what we need is 8797 8798 X a new name for slops [undercroft] 8799 X a derivation that builds it 8800 X a module that runs it 8801 - needs to put sops on $PATH 8802 X install the module on a vhost on loaclhost 8803 - undercroft terminates tls, so unless we're going to 8804 make nginx do tcp proxying, perhaps let's just put it on another port 8805 * check that libfetch can present a client cert 8806 * a secrets file 8807 - wifi password 8808 - rsync password for backblaze 8809 - pppoe/l2tp 8810 - ssh auth keys? 8811 - root password? 8812 8813 * a proxy of some kind on bordervm so that hosts on the bordervm lan 8814 can get certs and secrets 8815 - bordervm needs to forward 19614 to loaclhost 8816 - and add entry for undercroft.lan in /etc/hosts 8817 * certifix-client 8818 8819 Fri Jul 31 21:12:23 BST 2026 8820 8821 Apparently I am blocked on trying to figure out a hierarchy for 8822 the secrets yaml. Might just go with "service" names as top-level 8823 keys 8824 8825 wifi: 8826 psk: 75a7a8b786c876c98 8827 backblaze: 8828 password: abc123 8829 aaisp: 8830 pppoe: 8831 username: aa123 8832 password: Weird Caps Thing 8833 8834 It may be an idea to teach undercroft to turn the yaml into json, esp. if 8835 we want to use yaml features like comments, anchors etc 8836 8837 * we can put age pubkeys into the yaml file 8838 * undercroft's private key should live with undercroft 8839 * we would like to version-control the yaml, and not put the 8840 private key in the same place 8841 8842 could/should we use idiomatic systemd unit folders for storing this? 8843 8844 /var/lib/undercroft/age.key 8845 /var/lib/undercroft/secrets/secrets.yaml 8846 /var/lib/undercroft/secrets/.git 8847 8848 undercroft also needs to have a cert. in theory we could make it 8849 get one from certifix but maybe we'll just do that by hand 8850 8851 /var/lib/undercroft/server.key 8852 /var/lib/undercroft/server.crt 8853 8854 Sun Aug 2 09:33:27 BST 2026 8855 8856 if loaclhost is offline and rotuer is trying to get secrets, 8857 this will break. before we can make rotuer use secrets, we 8858 need to move them from loaclhost to something that will be 8859 available at least as must as rotuer is 8860 8861 in theory this is arthur, although I note that it's still using dhcp 8862 8863 note that we need network access for loaclhost to be able to rebuild 8864 an image for rotuer, so maybe we just accept that it has to be 8865 loaclhost and we'll need to bring it into the house if the cable is 8866 severed. we already gave it static ip addresses 8867 8868 [nix-shell:/var/lib/undercroft]# SOPS_AGE_KEY_FILE=sops.age sops decrypt secrets/secrets.enc.yaml 8869 8870 mkdir tls 8871 openssl req -newkey rsa:2048 -addext "subjectAltName = DNS:undercroft.lan,DNS:undercroft,DNS:undercroft.telent.net" -nodes -keyout tls/server.key --out tls/server.csr 8872 openssl x509 -req -in tls/server.csr -days 365 -CA ../certifix/certs/ca.crt -CAkey ../certifix/private/ca.key -CAcreateserial -copy_extensions copyall -out tls/server.crt 8873 8874 Tue Aug 4 14:33:30 BST 2026 8875 8876 @400000000000003f3102e06b outsdie int.link.dhcpc-watcher s6-rc-up-tree int.link.dhcpc-lease-acquired blocks ( ) start ( resolvconf, certifix-C=GB-ST=London-O=Telent-OU=devices-CN=outsdie, route-37a8eec1ce19-7a1b596e5bea, int.link.dhcpc-lease-acquired, ntp, log-shipper-live, log-shipper-backfill-source, log-shipper-backfill-sink ) 8877 @400000000000003f393d1c05 outsdie int.link.dhcpc-lease-acquired dhcp lease acquired 192.168.8.47 8878 @400000000000004000abb334 outsdie int.link.dhcpc-watcher cat: can't open '/nix/store/z10dib5kld10c1sl4hqxyswazwln3df6-int.link.dhcpc-lease-acquired/.outputs/dns': No such file or directory @400000000000004023f24c50 outsdie ntp 1970-01-01T00:00:54Z chronyd version 4.8 starting (+CMDMON +REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND -NTS -SECHASH +IPV6 -DEBUG) 8879 @40000000000000402bda2bda outsdie ntp 506 Cannot talk to daemon 8880 @40000000000000402c0753a9 outsdie ntp 1970-01-01T00:00:54Z Running with root privileges 8881 8882 8883 8884 @400000000000005e16aad2c7 outsdie int.link.dhcpc-watcher s6-svlisten: fatal: timed out 8885 @400000000000005e170835bf outsdie int.link.dhcpc-watcher s6-rc: warning: unable to start service ntp: command exited 99 8886 @400000000000005e1758e25b outsdie int.link.dhcpc-watcher 8887 @400000000000005e175963d4 outsdie int.link.dhcpc-watcher ERROR s6-rc -b -u change certifix-C=GB-ST=London-O=Telent-OU=devices-CN=outsdie exited 1 8888 @400000000000005e17b1aaf0 outsdie int.link.dhcpc-watcher /nix/store/y444jvx67dzwrfsljzryz7rlaqmjl2xy-lua-tty-mips-unknown-linux-musl/bin/lua: ...mips-unknown-linux-musl-0.1/share/lua/5.3/anoia/init.lua:105: Error executing "s6-rc-up-tree "int.link.dhcpc-lease-acquired"" 8889 8890 looks like up-tree of dhcpc-watcher times out waiting for somthing 8891 (what?) to start and then takes everything down, but isnt killing ntpd 8892 properly 8893 8894 Tue Aug 4 21:05:53 BST 2026 8895 8896 would be good to have a way to add entries to /etc/hosts 8897 8898 Wed Aug 5 23:39:03 BST 2026 8899 8900 # find /run/services/outputs/secret-service/ 8901 /run/services/outputs/secret-service/ 8902 /run/services/outputs/secret-service/state 8903 /run/services/outputs/secret-service/.outputs 8904 /run/services/outputs/secret-service/.outputs/aaisp 8905 /run/services/outputs/secret-service/.outputs/aaisp/pppoe 8906 /run/services/outputs/secret-service/.outputs/aaisp/pppoe/username 8907 /run/services/outputs/secret-service/.outputs/aaisp/pppoe/password 8908 /run/services/outputs/secret-service/.outputs/wifi 8909 /run/services/outputs/secret-service/.outputs/wifi/psk 8910 8911 json-to-fstree calls svc:open which expects the service store 8912 directory and appends .outputs, but instead we are calling it while 8913 cwd = the actual outputs folder 8914 8915 this is broken the same way in outboard and in tang 8916 8917 the "run" script created by s6 script builder.sh does not interpolate 8918 $out when it is written nor does it have it available when it runs. 8919 8920 nor does (placeholder "out") show the right value: 8921 trace: helo /1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9 8922 but even if I prefix /nix/store that doesn't map to an 8923 existent store directory 8924 8925 the cheap fix would be for svc.open to only prepend .outputs if that 8926 directory exists, though that is going to break the .properties stuff 8927 8928 Mon Aug 10 21:26:54 BST 2026 8929 8930 so what we need is 8931 8932 X a new name for slops [undercroft] 8933 X a derivation that builds it 8934 X a module that runs it 8935 - needs to put sops on $PATH 8936 X install the module on a vhost on loaclhost 8937 - undercroft terminates tls, so unless we're going to 8938 make nginx do tcp proxying, perhaps let's just put it on another port 8939 X check that libfetch can present a client cert 8940 X a secrets file 8941 - wifi password 8942 - rsync password for backblaze 8943 - pppoe/l2tp 8944 - ssh auth keys? 8945 - root password? 8946 8947 * a proxy of some kind on bordervm so that hosts on the bordervm lan 8948 can get certs and secrets 8949 - bordervm needs to forward 19614 to loaclhost 8950 - and add entry for undercroft.lan in /etc/hosts 8951 - haven't done this because outsdie is plugged into the lan anyway 8952 X certifix-client 8953 8954 * the secrets are arriving on outsdie. in theory all we need do is 8955 make it actually use them in its config 8956 8957 * add levitate to outsdie 8958 - could we add levitate to the tftpboot image 8959 and use it to write the final image 8960 - levitate chroots to a subdir of /run so won't interfere 8961 with phram shenanigans 8962 8963 * flash outsdie onto gl-ar750 8964 * unplug it from the serial port 8965 8966 - upgrade kernel and audit firewall on lenscap 8967 - upgrade kernel on old outsdie 8968 - find omnia, plug in, upgrade kernel and install rotuer config 8969 8970 8971 8972 8973 Mon Aug 10 21:29:04 BST 2026 8974 8975 This is interesting: 8976 8977 # LOG-SHIPPING-START 8978 8979 @400000006a7a2b4d151e2ae1 outsdie ntp 2026-08-10T19:48:56Z Selected source 185.51.192.62 (pool.ntp.org) 8980 @400000006a7a318b019b18e1 outsdie int.link.dhcpc udhcpc: sending renew to server 192.168.8.1 8981 # LOG-SHIPPING-STOP 8982 8983 @400000006a7a318b028436d0 outsdie int.link.dhcpc udhcpc: lease of 192.168.8.47 obtained from 192.168.8.1, lease time 3600 8984 # LOG-SHIPPING-START 8985 8986 8987 there's only one log entry that would be missed but why is it stopping 8988 anyway? is there backfill? 8989 8990 Wed Aug 12 22:21:41 BST 2026 8991 8992 we can't reference secrets as inputs to the wap profile because it doesn't 8993 know how to make its hostap services dependent on the secret service 8994 8995 ... which is odd because apparently I did write all the stuff for 8996 doing that and it should do. 8997 8998 the hostapd service is made by svc.secrets.subscriber, which updates its 8999 dependencies to include the secrets-service being watched 9000 9001 when the watcher starts, it uses s6-svwait to wait for hostapd - but 9002 this is part of its run and there is no readiness notification, so no 9003 dependency loop - and then calls watch-outputs to restart that service 9004 as needed. 9005 9006 which bit is supposed to cause the hostapd to not start in the first place 9007 until it has secrets? surely because it is dependent on a controlled service 9008 it should not be started nutil that service is started with up-tree 9009 9010 Sat Aug 15 15:18:01 BST 2026 9011 9012 outboard.nix doesn't do readiness notification, so perhaps hostapd is 9013 just starting too quickly? but i don't think that's it as hostapd is 9014 not started by the s6-rc-up-tree that brings up outboard, it gets 9015 kicked off way earlier 9016 9017 Mon Aug 17 13:09:34 BST 2026 9018 9019 a795591e218335ae964630d194f541d585ec71e5 9020 9021 secrets subscriber: depend on watched service(s) 9022 9023 if the secrets service has a long chain of dependencies (e.g https 9024 fetch which requires ntp which requires network ...) but the 9025 restartable service (e.g. hostapd) would otherwise start at boot, it 9026 might have timed out of its restart loop before we even get to the 9027 point that it could start successfully with a valid config 9028 9029 hand on heart: I am unsure if this is the right thing. maybe 9030 secrets-consuming services should be required to provide placeholders 9031 if there is no secret yet? the consequnce of adding this dependency is 9032 if the secrets service fails, then the services depending on it will 9033 all be brought down 9034 9035 if we're going to change this behaviour we probably should make it 9036 easier to provide the placeholders. outputRef could have a third parameter 9037 9038 outputRef config.services.secrets "wpa_passphrase" "not the real wifi secret" 9039 9040 output-template.fnl already has a default arg, although it doesn't 9041 look at it until after it does (assert (svc.open service-path)) [which 9042 may or may not be ok, watch-fsevents may be confused by dangling 9043 symlink but we're not calling it] 9044 9045 Mon Aug 17 13:29:59 BST 2026 9046 9047 ~I am mystified how services.sshd.authorizedKeys is supposed to work~ 9048 9049 sshd service blows up with hard-to-understand error messages if the 9050 yaml shape is not what it is expecting. should be 9051 9052 ssh: 9053 authorized_keys: 9054 root: 9055 - blah 9056 - blah 9057 - blah 9058 9059 Ideally we would have something on the sops server documenting the 9060 schema, but it would be nice also to display an error message that 9061 says what the problem is 9062 9063 Mon Aug 24 11:24:19 BST 2026 9064 9065 the mystery LOG-SHIPPING-STOP continues to happen. it could be related 9066 to dhcp renews or it could be coincidental that they're the only other 9067 things in the log on a quiescent device 9068 9069 9070 wha gwan? if it's getting a NAK then it's quite reasonable for it to 9071 stop logging, but why is it getting a NAK? it gets the same address 9072 reissued whrn it sends discover 9073 9074 9075 9076 [12:27:17.566] @400000006a8c2ab9235068a2 outsdie int.link.dhcpc udhcpc: sending renew to server 192.168.8.1 9077 [12:27:17.566] # LOG-SHIPPING-STOP 9078 9079 [12:27:17.566] @400000006a8c2ab923c7f48e outsdie int.link.dhcpc udhcpc: received DHCP NAK 9080 [12:27:17.567] @400000006a8c2ab9247de66c outsdie int.link.dhcpc dhcp-notify nak 9081 [12:27:17.567] @400000006a8c2ab924922421 outsdie int.link.dhcpc received NAK on int 9082 [12:27:17.568] @400000006a8c2ab9269291fa outsdie int.link.dhcpc dhcp-notify deconfig 9083 [12:27:18.544] @400000006a8c2aba2aca54d0 outsdie log-shipper-live s6-tcpclient: fatal: unable to resolve loghost.telent.net: no available DNS server 9084 [12:27:19.520] @400000006a8c2abb302f1ab1 outsdie log-shipper-live s6-tcpclient: fatal: unable to resolve loghost.telent.net: no available DNS server 9085 [12:27:20.569] @400000006a8c2abc27c0ecaa outsdie int.link.dhcpc udhcpc: broadcasting discover 9086 [12:27:20.570] @400000006a8c2abc27ddc586 outsdie int.link.dhcpc udhcpc: broadcasting select for 192.168.8.47, server 192.168.8.1 9087 [12:27:20.571] @400000006a8c2abc2869f44b outsdie int.link.dhcpc udhcpc: lease of 192.168.8.47 obtained from 192.168.8.1, lease time 3600 9088 [12:27:20.571] @400000006a8c2abc28c97faf outsdie int.link.dhcpc dhcp-notify bound 9089 [12:27:20.572] @400000006a8c2abc308b097b outsdie int.link.dhcpc-watcher event ip= 192.168.8.47 9090 9091 Mon Aug 24 12:56:33 BST 2026 9092 9093 looks like we're playing with two ip addresses somehow? 9094 9095 9096 # grep 192.168.8 /run/log/* 9097 [12:56:22.212] /run/log/current:@400000000000003b1a9e9ed2 outsdie int.link.dhcpc udhcpc: broadcasting select for 192.168.8.48, server 192.168.8.1 9098 [12:56:22.213] /run/log/current:@400000000000003b214ca3c6 outsdie int.link.dhcpc udhcpc: lease of 192.168.8.48 obtained from 192.168.8.1, lease time 3600 9099 [12:56:22.214] /run/log/current:@400000000000003b29b11916 outsdie int.link.dhcpc-watcher event ip= 192.168.8.48 9100 [12:56:22.215] /run/log/current:@400000000000003b385c37d5 outsdie int.link.dhcpc-lease-acquired dhcp lease acquired 192.168.8.48 9101 [12:56:22.215] /run/log/current:@400000006a8c23d50e5060a8 outsdie int.link.dhcpc-watcher event ip= 192.168.8.48 9102 [12:56:22.257] /run/log/current:@400000006a8c2ab9235068a2 outsdie int.link.dhcpc udhcpc: sending renew to server 192.168.8.1 9103 [12:56:22.257] /run/log/current:@400000006a8c2abc27ddc586 outsdie int.link.dhcpc udhcpc: broadcasting select for 192.168.8.47, server 192.168.8.1 9104 [12:56:22.257] /run/log/current:@400000006a8c2abc2869f44b outsdie int.link.dhcpc udhcpc: lease of 192.168.8.47 obtained from 192.168.8.1, lease time 3600 9105 [12:56:22.257] /run/log/current:@400000006a8c2abc308b097b outsdie int.link.dhcpc-watcher event ip= 192.168.8.47 9106 [12:56:22.283] /run/log/current:@400000006a8c2abd0a6d2801 outsdie int.link.dhcpc-watcher event ip= 192.168.8.47 9107 9108 we start and swiftly kill three or four udhcp processes at startuo 9109 time (why so short timeout?) I wonder if there's a race here meaning we 9110 get > 1 address allocated and configure the wrong one 9111 9112 Mon Aug 24 21:28:59 BST 2026 9113 9114 the udhcp service times out a few times because it starts before any 9115 member has been added to the bridge. perhaps there's some way of 9116 making it dependent on the bridge having carrier, but that would 9117 depend on carrier detection for bridges being implemented as "at least 9118 one member has carrier" 9119 9120 it takes 9 seconds to time out, which is 3 tries * 3 seconds delay 9121 9122 It's notable that we're trying for a renew every 30 minutes which is 9123 half the lease time, so it's not the lease expiring that causes 9124 shipping to stop. and we don't even run anything on renew, so it can 9125 only be something in udhcp 9126 9127 # kill -USR1 390 # renew lease 9128 9129 this does _not_ cause shipping to stop. except occasionally it does, 9130 just not reliably. I tihnk it may be only if a little while has elapsed since the previous renew 9131 9132 9133 # sleep 60 ; kill -USR1 390 # this seems to do it. 9134 9135 # sleep 30 ; kill -USR1 390 # this didn't 9136 9137 Mon Sep 7 12:31:47 BST 2026 9138 9139 literal_or_output is defined differently in hostapd vs ppp 9140 9141 what it does: for use with output-template, when its param is a string 9142 or a number then it just prints the param, but when passed a lambda 9143 it assumes that lambda was made by outputRef and it transforms it into 9144 "{{ output(${toJSON (o "service")}, ${toJSON (o "path")})") }}" 9145 which output-template will expand. This feels almost as precarious an 9146 evaluation model as TeX 9147 9148 - potentially we could remove the conditional if we said e.g. that 9149 the `output` function returns its constant second arg if passed 9150 nil as the first arg. this isn't making a huge difference though 9151 9152 - we could have a function that encapsulated literal_or_output and the 9153 invocation of output-template, then we would DRY up {{{ }}}. 9154 however .. its input would be the template and we would still 9155 need to figure out how to represent that 9156 ["# some text\n" 9157 "username = " (outputRef secrets "name") "\n" 9158 ... 9159 ] 9160 9161 - we could define __toString on the outputRef attrset. arguably this 9162 is a bit cheeky because we would be defining it globally, but if the 9163 expansion is "{{{ blah }}}" then it only is appropriate in a 9164 quite limited context 9165 9166 why did we decide to make outputRef a function anyway and not a 9167 attrset?