diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cd51f10 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,93 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +## Code of Conduct + +### Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +### Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +### Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, +available at [http://contributor-covenant.org/version/2/0][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/2/0/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..082c41a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM debian:buster-20200607-slim + +LABEL maintainer="thomas.schaffter@gmail.com" + +ARG user=builder + +# Install Git and the build dependencies +# hadolint ignore=DL3008 +RUN apt-get update -qq -y && apt-get install --no-install-recommends -qq -y \ + apt-transport-https \ + bc \ + bison \ + build-essential \ + ca-certificates \ + cpio \ + dpkg-dev \ + fakeroot \ + flex \ + git \ + kmod \ + libssl-dev \ + libc6-dev \ + libncurses5-dev \ + make \ + rsync \ + && update-ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Create user and set work directory +RUN useradd -m $user +USER $user +WORKDIR /home/$user + +# Copy script that builds the kernel +COPY --chown=$user:$user build-kernel.sh . +RUN chmod +x build-kernel.sh + +ENTRYPOINT ["bash", "build-kernel.sh"] +CMD ["--help"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..64431a6 --- /dev/null +++ b/README.md @@ -0,0 +1,138 @@ +# Hardened kerrnel for Raspberry Pi + +## Overview + +This repository provides a dockerized tool to cross-compile the [Linux kernel for +Raspberry Pi](https://www.raspberrypi.org/documentation/linux/kernel/building.md) +with enhanced security. + +## Features + +- Dockerized tool to cross-compile the kernel with a single command +- Hardened kernel features + - Enable Audit + - Enable SELinux + +## Builder options + +Run the folllowing command to see the options of the builder: + +```console +$ docker run --rm tschaffter/raspberry-pi-kernel-hardened +Cross-compiling hardened kernels for Raspberry Pi +Usage: build-kernel.sh [--kernel-branch ] [--kernel-defconfig ] [--kernel-localversion ] [-h|--help] + --kernel-branch: Kernel branch to build (default: '') + --kernel-defconfig: Default kernel config to use (default: '') + --kernel-localversion: Kernel local version (default: '') + -h, --help: Prints help +``` + +## Build the hardered kernel + +### Identify the kernel version to build + +Go to the GitHub repository of the [Linux kernel of Raspberry Pi](https://github.com/raspberrypi/linux) +and identify the name of the branch or tag that you want to build. + +Examples: + +- The branch `rpi-4.19.y` +- The tag `raspberrypi-kernel_1.20200527-1` + +### Identify the default configuration to use + +Go to the page [Kernel building](https://www.raspberrypi.org/documentation/linux/kernel/building.md) +of the Raspberry Pi website to identify the default build configuration to use +for the target Pi. + +Examples: + +- `bcmrpi_defconfig` for Raspberry Pi 1, Pi Zero, Pi Zero W, and Compute Module +- `bcm2709_defconfig` for Raspberry Pi 2, Pi 3, Pi 3+, and Compute Module 3 +- `bcm2711_defconfig` for Raspberry Pi 4 + +Check the above documentation to make sure that these examples are up-to-date. + +### Cross-compile the kernel + +The command below builds the branch `rpi-4.19.y` for the Raspberry Pi 4 +(`bcm2711_defconfig`). Because this branch is not stable, we include today's +date to the value of `--kernel-localversion` (`4.19.y-20200614-hardened`). + +Once installed, the full kernel name will be + +```console +$ uname -a +Linux raspberrypi 4.19.127-4.19.y-20200614-hardened+ #1 SMP Sun Jun 14 15:06:51 UTC 2020 armv7l GNU/Linux +``` + +This command builds kernel: + +```console +$ docker run \ + --rm \ + -v $PWD/output:/output \ + tschaffter/raspberry-pi-kernel-hardened \ + --kernel-branch rpi-4.19.y \ + --kernel-defconfig bcm2711_defconfig \ + --kernel-localversion 4.19.y-20200614-hardened +Cloning into '/home/builder/tools'... +Installing cross compiler toolchain +Checking out files: 100% (19059/19059), done. +Getting kernel source code +Cloning into '/home/builder/linux'... +... + +Moving .deb packages to /output +SUCCESS The kernel has been successfully packaged. + +INSTALL +sudo dpkg -i linux-*-4.19.y-20200614-hardened*.deb +sudo sh -c "echo 'kernel=vmlinuz-4.19.127-4.19.y-20200614-hardened+' >> /boot/config.txt" +sudo reboot + +ENABLE SELinux +sudo apt-get install selinux-basics selinux-policy-default auditd +sudo sh -c "echo ' selinux=1 security=selinux' >> /boot/cmdline.txt" +sudo touch /.autorelabel +sudo reboot +sestatus +``` + +## Install the kernel + +Copy the Debian packages `$PWD/output/*.deb` to the target Raspbery Pi, for +example using `scp`, then follow the instructions given at the end of the build +command. + +## Notes + +- The builder uses all the CPU cores available to the Docker container. By default, +that is all the CPU cores of the host. Use +[Docker runtime options](https://docs.docker.com/config/containers/resource_constraints/#cpu) +to limit the usage of CPU cores by the builder. + +- The builder clones two GitHub repositories, the cross-compiler toolchain and +the source code of the kernel, unless their target directories already exist +(`/home/builder/tools` and `/home/builder/linux`). When running the dockerized +builder, you can mount volumes that points to these two directories to specify +a different toolchain and kernel source code. + +```console +$ git clone tools +$ git cllone linux +$ docker run \ + --rm \ + -v $PWD/output:/output \ + -v $PWD/tools:/home/builder/tools \ + -v $PWD/linux:/home/builder/linux \ + tschaffter/raspberry-pi-kernel-hardened \ + --kernel-branch rpi-4.19.y \ + --kernel-defconfig bcm2711_defconfig \ + --kernel-localversion 4.19.y-20200614-hardened +``` + +## Contributing change + +Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on how to +contribute to this project. diff --git a/build-kernel.sh b/build-kernel.sh new file mode 100755 index 0000000..4e8a4c1 --- /dev/null +++ b/build-kernel.sh @@ -0,0 +1,242 @@ +#!/bin/bash +# +# @tschaffter +# +# Cross-compiles the Raspberry Pi kernel with SELinux support and other +# hardening features enabled. +# +# Example: +# +# ./build-kernel.sh \ +# --kernel-branch rpi-4.19.y \ +# --kernel-defconfig bcm2711_defconfig \ +# --kernel-localversion 4.19.y-20200607-hardened +# +# Notes: +# +# - Identify kernel branch or tag from https://github.com/raspberrypi/linux +# - Identify --kernel-defconfig value from https://www.raspberrypi.org/documentation/linux/kernel/building.md +# - The value of --kernel-localversion will be returned by `uname -a` +# +# ARG_OPTIONAL_SINGLE([kernel-branch],[],[Kernel branch to build],['']) +# ARG_OPTIONAL_SINGLE([kernel-defconfig],[],[Default kernel config to use],['']) +# ARG_OPTIONAL_SINGLE([kernel-localversion],[],[Kernel local version],['']) +# ARG_HELP([The general script's help msg]) +# ARGBASH_GO() +# needed because of Argbash --> m4_ignore([ +### START OF CODE GENERATED BY Argbash v2.8.1 one line above ### +# Argbash is a bash code generator used to get arguments parsing right. +# Argbash is FREE SOFTWARE, see https://argbash.io for more info +# Generated online by https://argbash.io/generate + + +die() +{ + local _ret=$2 + test -n "$_ret" || _ret=1 + test "$_PRINT_HELP" = yes && print_help >&2 + echo "$1" >&2 + exit ${_ret} +} + + +begins_with_short_option() +{ + local first_option all_short_options='h' + first_option="${1:0:1}" + test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0 +} + +# THE DEFAULTS INITIALIZATION - OPTIONALS +_arg_kernel_branch="" +_arg_kernel_defconfig="" +_arg_kernel_localversion="" + + +print_help() +{ + printf '%s\n' "Cross-compiling hardened kernels for Raspberry Pi" + printf 'Usage: %s [--kernel-branch ] [--kernel-defconfig ] [--kernel-localversion ] [-h|--help]\n' "$0" + printf '\t%s\n' "--kernel-branch: Kernel branch to build (default: '')" + printf '\t%s\n' "--kernel-defconfig: Default kernel config to use (default: '')" + printf '\t%s\n' "--kernel-localversion: Kernel local version (default: '')" + printf '\t%s\n' "-h, --help: Prints help" +} + + +parse_commandline() +{ + while test $# -gt 0 + do + _key="$1" + case "$_key" in + --kernel-branch) + test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 + _arg_kernel_branch="$2" + shift + ;; + --kernel-branch=*) + _arg_kernel_branch="${_key##--kernel-branch=}" + ;; + --kernel-defconfig) + test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 + _arg_kernel_defconfig="$2" + shift + ;; + --kernel-defconfig=*) + _arg_kernel_defconfig="${_key##--kernel-defconfig=}" + ;; + --kernel-localversion) + test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 + _arg_kernel_localversion="$2" + shift + ;; + --kernel-localversion=*) + _arg_kernel_localversion="${_key##--kernel-localversion=}" + ;; + -h|--help) + print_help + exit 0 + ;; + -h*) + print_help + exit 0 + ;; + *) + _PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1 + ;; + esac + shift + done +} + +parse_commandline "$@" + +# OTHER STUFF GENERATED BY Argbash + +### END OF CODE GENERATED BY Argbash (sortof) ### ]) +# [ <-- needed because of Argbash + +# The argument --kernel-branch must be specified. +if [ -z "$_arg_kernel_branch" ]; then + echo "The argument --kernel-branch is missing." + exit 1 +fi + +# The argument --kernel-defconfig must be specified. +if [ -z "$_arg_kernel_defconfig" ]; then + echo "The argument --kernel-defconfig is missing." + exit 1 +fi + +# The argument --kernel-localversion must be specified. +if [ -z "$_arg_kernel_localversion" ]; then + echo "The argument --kernel-localversion is missing." + exit 1 +fi + +_workdir=$(pwd) +_tools_dir=$_workdir/tools +_kernel_src_dir=$_workdir/linux +_ccprefix="$_tools_dir/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-" +_output_dir=/output + + +# Check that the output directory exists and is writable +test -d $_output_dir || die "Output directory $_output_dir does not exist" 1 +test -w $_output_dir || die "Output directory $_output_dir is not writable" 1 + + +# Install toolchain +if [ -d $_tools_dir ]; then + echo "Using exsiting cross compiler toolchain $_tools_dir" +else + echo "Installing cross compiler toolchain" + git clone https://github.com/raspberrypi/tools $_tools_dir \ + || die "ERROR: Unable to clone the cross compiler toolchain" 1 +fi + + +# Get the kernel source code +if [ -d $_kernel_src_dir ]; then + echo "Using existing kernel source dir $_kernel_src_dir" +else + echo "Getting kernel source code" + git clone \ + --branch $_arg_kernel_branch \ + --depth=1 \ + https://github.com/raspberrypi/linux \ + $_kernel_src_dir \ + || die "Unable to clone kernel source code" 1 +fi + + +cd $_kernel_src_dir + +_kernel_version=$(make kernelversion) + +echo "Kernel version is $_kernel_version" +echo "Kernel local version is $_arg_kernel_localversion" + +echo "Cleaning up the directory" +make mrproper + +echo "Creating initial .config" +make ARCH=arm CROSS_COMPILE=$_ccprefix $_arg_kernel_defconfig \ + || die "Unable to create initial .config" 1 + +echo "Setting kernel local version" +./scripts/config --set-str CONFIG_LOCALVERSION "-$_arg_kernel_localversion" + +echo "Enabling Audit" +./scripts/config --enable CONFIG_AUDIT +./scripts/config --enable CONFIG_AUDIT_LOGINUID_IMMUTABLE + +echo "Enabling Security" +./scripts/config --enable CONFIG_SECURITY +./scripts/config --enable CONFIG_SECURITY_NETWORK + +echo "Enabling SELinux" +./scripts/config --enable CONFIG_SECURITY_SELINUX +./scripts/config --enable CONFIG_SECURITY_SELINUX_BOOTPARAM +./scripts/config --set-val CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE 1 +./scripts/config --disable CONFIG_SECURITY_SELINUX_DISABLE +./scripts/config --enable CONFIG_SECURITY_SELINUX_DEVELOP +./scripts/config --enable CONFIG_SECURITY_SELINUX_AVC_STATS +./scripts/config --set-val CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE 1 +# ./scripts/config --disable CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX +./scripts/config --enable CONFIG_DEFAULT_SECURITY_SELINUX +./scripts/config --disable CONFIG_DEFAULT_SECURITY_DAC +./scripts/config --set-str CONFIG_DEFAULT_SECURITY "selinux" + +# Validate config changes +make ARCH=arm CROSS_COMPILE=$_ccprefix olddefconfig + +# Alternatively, update config using menuconfig (interactive) +# make ARCH=arm CROSS_COMPILE=$_ccprefix menuconfig + +echo "Building kernel and generating .deb packages" +DEB_HOST_ARCH=armhf make ARCH=arm CROSS_COMPILE=$_ccprefix deb-pkg -j$(($(nproc)+1)) \ + || die "Unable to build or package kernel" 1 + +ls -al + +echo "Moving .deb packages to $_output_dir" +mv $_workdir/*.deb /output + + +echo "SUCCESS The kernel has been successfully packaged." +echo "" +echo "INSTALL" +echo "sudo dpkg -i linux-*-${_arg_kernel_localversion}*.deb" +echo "sudo sh -c \"echo 'kernel=vmlinuz-${_kernel_version}-${_arg_kernel_localversion}+' >> /boot/config.txt\"" +echo "sudo reboot" +echo "" +echo "ENABLE SELinux" +echo "sudo apt-get install selinux-basics selinux-policy-default auditd" +echo "sudo sh -c \"echo ' selinux=1 security=selinux' >> /boot/cmdline.txt\"" +echo "sudo touch /.autorelabel" +echo "sudo reboot" +echo "sestatus" + +# ] <-- needed because of Argbash