lix-os/README.md

305 lines
14 KiB
Markdown
Raw Permalink Normal View History

2020-11-26 00:47:44 +01:00
lix-os
======
a linux distribution for people who like to breathe manually.
this repository contains documentation and helper scripts only. a working linux
distribution with overlayfs support (preferably alpine linux) and an internet
connection are required to bootstrap lix os. no images or binaries supplied.
## about
lix os uses the lix package manager to construct a musl-based linux distribution
from source code. it is entirely composed of simple, short-as-possible,
POSIX-compliant shell scripts. packages are built in chroots constructed from
package directories stacked on top of each other using overlayfs. only
dependencies explicitly listed in a package's 'deps' file are accessible during
the package's build process. there is no concept of "indirect dependencies."
packages are made available to the user by soft-linking package directory
contents into the system root. packages can be added to or removed from the
system root without uninstalling the packages entirely.
lix os is the work of a single maintainer and follows the same "use fewer
packages" ethos that kiss linux does. it also tries to use the smallest
packages possible in order to create a more readily audited codebase. some
exceptions are made in the name of usefulness and self-hosting. generally,
suckless utilities are preferred to bsd utilities, and bsd utilities are
preferred to gnu utilities due to the preferred in each case generally having a
smaller, easier to understand codebase. this does mean lix os's versions of
common linux utilities may have fewer options than one may be used to, but many
of these missing options are either not commonly used or can be compensated for
with a short shell script or function.
in the opinion of lix os' maintainer, it would be ideal if everyone knew and
understood every line of code executing on their machines. while a perfect
realisation of this dream is unlikely, lix hopes to make the problem less
intractable. lix os will never ship binaries.
## caveats
lix os is an experimental linux distribution. it may not work. it may catch on
fire. it may be abandoned at a moment's notice for another experiment. it will
not stop you from doing something stupid and destroying your system. it is not
and probably never will be "production ready."
nonetheless, the original author currently uses it daily, and it may serve the
needs of others as well.
## goals
1) be self-hosting.
2) keep a small codebase.
3) remain easy to understand, customize, and fork.
4) never offer more packages than one maintainer can keep up to date.
5) build from sources obtained directly from their authors, whenever possible.
6) support fully open source platforms. (e.g., Talos II.)
## system hygiene
lix helps lix os maintain system hygiene by:
1) building and installing in chroots with only package dependencies available.
2) patching, configuring, and building in an overlay over package source code.
3) mounting an overlay over each chroot before entering the chroot at all.
4) symlinking installed files from package overlays into the filesystem.
all changes during the patching, configuration, building, and installation
phases go into overlays maintained by `lyr`. this helps keep the source code in
`src`'s repository pristine and makes it very simple to activate and deactivate
packages. it also makes it easy to see what packages have supplied each file in
one's system root.
## fork at will
lix os is simple. if one disagrees with a decision, one should fork away. it
should never take more than a single person to maintain a lix os-based
distribution and all its packages.
## _largely_ statically linked
there are some blockers to achieving a fully statically linked system.
### 1) smartcard support
smartcard support is not possible at this time without dynamic linking. in
particular, 'scdaemon' from gnupg loads 'libpcsclite.so' at runtime. there is
not yet a way to make it statically link the library instead. accordingly, the
executables provided by the 'gnupg' package in lix os are linked statically and
do not support smartcards, while the executables in the 'gnupg-dynamic' and
'pcsc-lite' packages are linked dynamically and do support smartcards.
### 2) gui desktops
both x11 and wayland have dependencies which use meson as their build system.
meson requires core python3 modules which use dynamic loading (e.g., ctypes),
which means python3 must be dynamically linked in order to use the meson build
system. the default python3 package is statically linked and does not contain
many "core" modules due to their dependency on the 'dlopen' function. if a gui
is desired, the 'python3-dynamic' package will have to be built.
removing the meson dependency is possible. oasis linux has pioneered this
approach. the best approach would probably be to either port the packaging or
the build system from oasis linux. preferably the latter, since doing so would
better enable collaboration between our projects, but in theory a lua script
could also be written to translate oasis linux packaging into shell scripts
for those unwilling to learn the lua-based build system.
mesa and wayland also tend to not work well when statically linked. in theory
it should be possible to statically link these components, but in practice it
seems to result in segmentation faults. there is probably a way to fix this
problem, but in the meantime a graphical desktop will require dynamic linking.
### 3) gettext
the `kbd` package, which supplies console utilities like `setfont`, requires
`gettext`, and `gettext` will not build statically due to symbol collisions
during while linking. this is also probably fixable, but it would be better to
remove the `gettext` dependency entirely if possible.
## why prefer static linking?
because it should be possible to quantify all of a program's possible states at
compile time, barring hardware-level shenanigans, from source code. a user
should never be put in the position where they are surprised by a change in a
program's behavior because a dynamically loaded library was updated. this seems
analogous to lexical versus dynamic scoping in programming languages.
there are ways to achieve these sorts of guarantees without sacrificing dynamic
loading, but they are more complicated than simply linking statically.
also, no dynamic loader means no dynamic loader vulnerabilities.
## bootstrapping
below is the "official path," but please try other ways and report back.
it is assumed that each step is executed from the same directory. e.g., `~`.
if one prefers to use the linux kernel which contains propietary firmware blobs,
replace references to `linux-libre` below with `linux-kernel`.
01. create an alpine linux installation. leave the target device untouched as
it will need to be formatted and partitioned.
02. install the bootstrap dependencies:
`apk add build-base coreutils cmake git curl rsync cryptsetup autoconf
automake libtool flex bison ncurses-dev`
03. install a command-line text editor and set the EDITOR variable. (optional)
e.g., `apk add vim && export EDITOR='vim'`
(n.b.: if this step is skipped, `vi` will be used.)
(n.b.: in a later step, libraries get moved. if this breaks the text
editor, try another one. if all else fails, `vi` is known to work in alpine
linux and comes installed by default.)
04. clone the lix-os repository and its subrepositories, and install them.
- `git clone http://git.fuwafuwaqtlkkxwc.onion/yafox/lix-os`
- `cd lix-os`
- `make`
- `make install`
05. create a bootstrap layer
- `mkdir bootstrap`
- `./lix-os/utilities/mkbootstrap.sh bootstrap`
06. supply a linux kernel configuration file (strongly recommended but optional)
- `mkdir linux-mnt`
- `lix --bootstrap=bootstrap pull linux-libre`
- `lix --bootstrap=bootstrap mount linux-mnt linux-libre`
- copy the desired kernel configuration file to `linux-mnt/mnt/src/.config`
(n.b.: many distributions have their own kernel configuration files which
can be borrowed. lix os' maintainer keeps a public collection of examples
in the linux-confs repository.)
- `lix umount linux-mnt linux-libre`
- `rmdir linux-mnt`
07. prepare the destination device.
- create an ext4 partition to be used as the boot partition. 100MB should
be plenty of space, assuming no firmware blobs are to be built into the
kernel.
- create a LUKS-encrypted ext4 partition to be used as the root partition.
- create whatever other partitions desired.
08. build lix os
- `"$utilsdir/bootstrap.sh" bootstrap`
(n.b.: some packages will require interactive configuration via text
editor. if a package fails, bootstrapping can be resumed from the same
point later by passing the name of the package as a second argument. e.g.,
`"$utilsdir/bootstrap.sh" bootstrap <failing package>`. this script just
loops over the dependencies of the `lix-os` package, `add`s them, and then
`up`s them in the `lix-os` chroot. it's a short script. don't be afraid
to examine and modify it.)
09. copy lix os to the target medium
- `mkdir -p lix-os-tgt/boot`
- mount the root partition to `lix-os-tgt`
- mount the boot partition to `lix-os-tgt/boot`
- mount any other partitions created earlier under `lix-os-tgt`.
- `"$utilsdir/install.sh" lix-os-tgt`
(n.b.: this script copies the contents of the lix-os chroot and the `src`
and `lyr` repos in /usr/var to the target directory given, then chroots
in to the target directory and replaces all the real files with softlinks
to the appropriate targets in the `lyr` repository via `lix up -f`.)
10. make last-minute configuration changes to boot-related partitions
- edit the contents of `lix-os-tgt/boot/grub/grub.cfg` to point to the
correct partitions.
- make any additional last-minute configuration changes desired.
at this point, if one wishes to skip using symlinks to link installed packages
into the system root instead of having them copied from the package chroots, one
may skip to the last step.
11. unmount boot-related partitions
- `umount lix-os-tgt/boot`
- unmount any additional partitions which should not have their files
converted to symlinks to locations in lyr's layer repository.
(n.b.: this is one of the "sharp edges" of lix os which will one day be
"filed down" in one way or another. in the meantime, take care to not
`lix up` or `lix dn` anything with files related to booting.
`linux-libre`, `lix-os-bootloader`, and `lix-os-initramfs` immediately
come to mind as such packages.)
12. convert everything else to symlinks
- `"$utilsdir/softlink.sh" lix-os-tgt`
13. clean up and reboot into lix os
- unmount the target partitions under and at `lix-os-tgt`.
- `rmdir lix-os-tgt/boot lix-os-tgt`
- reboot into the target medium and make sure lix os works!
## post-bootstrap
if all has gone well and the system is booting, make a backup of your kernel
and initramfs. mount the boot partition and copy `vmlinux` to `vmlinux.bak`
and `initramfs.igz` to `initramfs.igz.bak`. this will allow for recovery by
changing boot parameters to boot with the backup files if one misconfigures
one's kernel or initramfs in the future.
## editing lix-os-initramfs
the contents of the initramfs file can be examined and modified using the
`lix mount` command, the same as every other lix package.
## architecture
lix os tries to be compositional in its architecture, which should make it
easier to replace components as desired. this flexibility comes at the
expense of having all files relevant to a package in a single location. an
earlier iteration of lix os (called 'mu os' in ignorance of the other linux
distribution by that same name) opted for a tighter integration of parts, which
was nice in some ways but which also tended toward creating a monolith.
ideally the lix package manager should be composed of simple tools that behave
in a clear, consistent manner and which have no notions about each other.
## helpful scripts
there are also a number of helpful scripts in the `lix-os-utilities` repository.
for example, the `versions` utility, which itself also depends on `vercmp` and
`shsort`, may be useful for keeping a lix os system up to date. `versions`
returns a sorted list of available package versions pulled directly from each
project maintainer's website and/or its mirrors. lix does not use it directly,
but the author does use it periodically to update `src` packages.
## roadmap
- switch to a "-static-pie" build. (on ppc64le, this currently produces
"read-only segment has dynamic relocations" errors.)
- replace libressl with bearssl.
- replace all gnu tools with simpler alternatives.
[oasis linux](https://github.com/oasislinux/oasis) has the right idea here,
but work needs to be done to add ppc64le support to cproc and the like.
- capture file changes to "upped" packages to their own layer.
- capture file changes to /etc (and others?) to their own layer.
- allow unprivileged creation of user-specific lix environments, like nix os.
- find a way to encourage running processes under least-privileged, package-
specific user accounts whenever possible without literally making the choice
for the user.
- consider separating state in /var into files deterministically generated and
files constituting non-deterministic input. (e.g., 'src' tarballs versus
private keys.)
- consider moving to a model that does require a 'root' user.
- consider merging the overlayfs approach with the autotools-free, lua-as-make
approach of oasis linux.
it would be nice to create something like nix os that captures changed state
rather than requiring state be constructed from a configuration file in a
domain-specific language.
## inspiration
the development of lix os was inspired and aided by the examples set by the
following projects:
- gobo linux and nix os, for sandboxing and separation of state.
- sabotage linux, for general approach and code-centricity.
- kiss linux, for embracing a "remove as many packages as possible" ethos.
- alpine linux, for patches and scripts showing how to build things with musl.
- oasis linux, for helping inform userland choices.
- sta.li, for demonstrating a simplified LHFS.
- openbsd, for providing alternatives such as libressl and doas.
- musl-cross-make, for building a ppc64le-and-musl-targeting c compiler.
- suckless.org, for providing and highlighting simple alternatives, and for
setting me down this path in the first place.