## Introduction In #1302 we noticed that `pmb.chroot.user()` does not escape commands properly: When passing one string with spaces, it would pass them as two strings to the chroot. The use case is passing a description with a space inside to `newapkbuild` with `pmboostrap newapkbuild`. This is not a security issue, as we don't pass strings from untrusted input to this function. ## Functions for running commands in pmbootstrap To put the rest of the description in context: We have four high level functions that run commands: * `pmb.helpers.run.user()` * `pmb.helpers.run.root()` * `pmb.chroot.root()` * `pmb.chroot.user()` In addition, one low level function that the others invoke: * `pmb.helpers.run.core()` ## Flawed test case The issue described above did not get detected for so long, because we have a test case in place since day one, which verifies that all of the functions above escape everything properly: * `test/test_shell_escape.py` So the test case ran a given command through all these functions, and compared the result each time. However, `pmb.chroot.root()` modified the command variable (passed by reference) and did the escaping already, which means `pmb.chroot.user()` running directly afterwards only returns the right output when *not* doing any escaping. Without questioning the accuracy of the test case, I've escaped commands and environment variables with `shlex.quote()` *before* passing them to `pmb.chroot.user()`. In retrospective this does not make sense at all and is reverted with this commit. ## Environment variables By coincidence, we have only passed custom environment variables to `pmb.chroot.user()`, never to the other high level functions. This only worked, because we did not do any escaping and the passed line gets executed as shell command: ``` $ MYENV=test echo test2 test 2 ``` If it was properly escaped as one shell command: ``` $ 'MYENV=test echo test2' sh: MYENV=test echo test2: not found ``` So doing that clearly doesn't work anymore. I have added a new `env` parameter to `pmb.chroot.user()` (and to all other high level functions for consistency), where environment variables can be passed as a dictionary. Then the function knows what to do and we end up with properly escaped commands and environment variables. ## Details * Add new `env` parameter to all high level command execution functions * New `pmb.helpers.run.flat_cmd()` function, that takes a command as list and environment variables as dict, and creates a properly escaped flat string from the input. * Use that function for proper escaping in all high level exec funcs * Don't escape commands *before* passing them to `pmb.chroot.user()` * Describe parameters of the command execution functions * `pmbootstrap -v` writes the exact command to the log that was executed (in addition to the simplified form we always write down for readability) * `test_shell_escape.py`: verify that the command passed by reference has not been modified, add a new test for strings with spaces, add tests for new function `pmb.helpers.run.flat_cmd()` * Remove obsolete commend in `pmb.chroot.distccd` about environment variables, because we don't use any there anymore * Add `TERM=xterm` to default environment variables in the chroot, so running ncurses applications like `menuconfig` and `nano` works out of the box |
||
---|---|---|
aports | ||
helpers | ||
keys | ||
pmb | ||
test | ||
.gitignore | ||
.travis.yml | ||
CONTRIBUTING.md | ||
LICENSE | ||
MANIFEST.in | ||
README.md | ||
pmbootstrap.py | ||
setup.cfg | ||
setup.py |
README.md
pmbootstrap
Introduction | Security Warning | Supported Devices | |
Sophisticated chroot/build/flash tool to develop and install postmarketOS.
For in-depth information please refer to the postmarketOS wiki.
Requirements
- 2 GB of RAM recommended for compiling
- Linux distribution (
x86
,x86_64
, oraarch64
)- Windows subsystem for Linux (WSL) does not work! Please use VirtualBox instead.
- Kernels based on the grsec patchset do not work (Alpine: use linux-vanilla instead of linux-hardened, Arch: linux-hardened is not based on grsec)
- On Alpine Linux only:
apk add coreutils
- Python 3.4+
- OpenSSL
Usage
Assuming you have a supported device, you can build and flash a postmarketOS image by running through the following steps. For new devices check the porting guide.
First, clone the git repository and initialize your pmbootstrap environment:
$ git clone https://github.com/postmarketOS/pmbootstrap
$ cd pmbootstrap
$ ./pmbootstrap.py init
While running any pmbootstrap command, it's always useful to have a log open in a separate window where further details can be seen:
$ ./pmbootstrap.py log
It's now time to run a full build which will create the boot and system images:
$ ./pmbootstrap.py install
Once your device is connected and is ready to be flashed (e.g. via fastboot), you can run a flash of the kernel (boot) and system partitions:
$ ./pmbootstrap.py flasher flash_kernel
$ ./pmbootstrap.py flasher flash_system
After a reboot, the device will prompt for the full-disk encryption password, which you typed in the install step (unless you have disabled full-disk encryption with --no-fde
). Once the partition has been unlocked it is possible to connect via SSH:
$ dhclient -v enp0s20f0u1
$ ssh user@172.16.42.1
Development
Testing
Install pytest
(via your package manager or pip) and run it inside the pmbootstrap folder.