Monday, 28 March 2016

chromium - FATAL:setuid_sandbox_client.cc(126)] Check failed: IsFileSystemAccessDenied()

Testing running chromium in Docker I had the following stack trace :
[0329/035746:FATAL:setuid_sandbox_client.cc(126)] Check failed:
 IsFileSystemAccessDenied().
#0 0x55e51a82299e <unknown>
#1 0x55e51a8007ee <unknown>
#2 0x55e51a8934fe <unknown>
#3 0x55e51a7f3f3b <unknown>
#4 0x55e51a7f21a6 <unknown>
#5 0x7fe5e8984710 <unknown>
#6 0x55e51a7f34f9 <unknown>

[1:1:0329/035746:ERROR:nacl_fork_delegate_linux.cc(315)] Bad NaCl helper
 startup ack (0 bytes)
[1:1:0329/035746:FATAL:setuid_sandbox_client.cc(126)] Check failed: 
IsFileSystemAccessDenied().
This is because chromium should not be run as root. From the development list this is by design.

Sunday, 27 March 2016

Arch Linux pacstrap options

When creating a base Arch Linux image from scratch for Docker I found the documentation for pacstrap to be limited.
Pacstrap is installed as part of the arch-install-scripts and I eventually found the following options running as the root user
# pacstrap -h
usage: pacstrap [options] root [packages...]

  Options:
    -C config      Use an alternate config file for pacman
    -c             Use the package cache on the host, rather than the target
    -d             Allow installation to a non-mountpoint directory
    -G             Avoid copying the host's pacman keyring to the target
    -i             Avoid auto-confirmation of package selections
    -M             Avoid copying the host's mirrorlist to the target

    -h             Print this help message

pacstrap installs packages to the specified new root directory. If no packages
are given, pacstrap defaults to the "base" group.
The docker script I used was :
spawn pacstrap -C $PACMAN_CONF -c -d -G -i $ROOTFS base haveged 
$PACMAN_EXTRA_PKGS --ignore $PKGIGNORE

Build Arch Linux base image for Docker

As there is no official Docker Hub build for Arch Linux I thought this would be a good oppertunity to find out how images are created from scratch for OS’s. IMHO its good practice to create your own images to ensure you know exactly the providence of what is installed in your container.
As a starting point I used the mkimage-arch.sh script on the Docker github contrib area.
Script must be run as root
if [ “$(id -u)” != “0” ]; then
printf "This script must be run as root\n" 
exit 1
fi
There are two required packages : expect + pacstrap which can be installed with :
$ pacman -S arch-install-scripts expect
The script checks for this as follows :
hash pacstrap &>/dev/null || {
    printf "Could not find pacstrap. Run pacman -S arch-install-scripts"
    exit 1
}

hash expect &>/dev/null || {
    printf "Could not find expect. Run pacman -S expect"
    exit 1
}
The || is similar to && except it tells the shell only to evaluate the expression after it when the first expression fails.

The hash command affects the way the current shell environment remembers the locations of utilities found. If run without any parameters it shows the path of all commands run since the hash was last reset (hash -r) e.g.
$ hash
hits    command
   1    /usr/bin/git
   1    /usr/bin/vim
   3    /usr/bin/cat
   1    /usr/bin/touch
   1    /usr/bin/mv
   1    /usr/bin/mkdir
   3    /usr/bin/man
  13    /usr/bin/ls
The hash table is a feature of bash that prevents it from having to search $PATH every time you type a command by caching the results in memory. For this use case we use it as a way to test if the command is available.
Next we set the language as UTF-8 :
export LANG="C.UTF-8"
Create a temporary root file system with mktemp :
ROOTFS=$(mktemp -d /tmp/rootfs-archlinux-XXXXXXXXXX)
mktemp creates a temporary directory (or file) based on the template provided to randomize the name. Each X value is replaced with a random string.

Set permissions
chmod 755 "$ROOTFS"
Define the packages to not install for minimal image
PKGIGNORE=(
    cryptsetup
    device-mapper
    dhcpcd
    iproute2
    jfsutils
    linux
    lvm2
    man-db
    man-pages
    mdadm
    nano
    netctl
    openresolv
    pciutils
    pcmciautils
    reiserfsprogs
    s-nail
    systemd-sysvcompat
    usbutils
    vi
    xfsprogs
)
Expanding an array without an index only gives the first element. The $IFS is a special shell variable which stands for Internal Field Separator.
IFS=','
PKGIGNORE="${PKGIGNORE[*]}"
unset IFS
printf "%s""\nPackages not to be installed : $PKGIGNORE\n"
Set pacman.conf to provided conf file
PACMAN_CONF='./arch-docker-pacman.conf'
Define the mirror for pacman :
PACMAN_MIRRORLIST='Server = https://mirrors.kernel.org/archlinux/\$repo/os/\$arch'
Set basic variables to create image
PACMAN_EXTRA_PKGS=''
EXPECT_TIMEOUT=60
ARCH_KEYRING=archlinux
DOCKER_IMAGE_NAME=archlinux
Export pacman mirror
export PACMAN_MIRRORLIST
Use expect to auto reply to pacstrap
expect <<EOF
    set send_slow {1 .1}
    proc send {ignore arg} {
        sleep .1
        exp_send -s -- \$arg
    }
    set timeout $EXPECT_TIMEOUT

     spawn pacstrap -C $PACMAN_CONF -c -d -G -i $ROOTFS base haveged $PACMAN_EXTRA_PKGS --ignore $PKGIGNORE
    expect {
        -exact "anyway? \[Y/n\] " { send -- "n\r"; exp_continue }
        -exact "(default=all): " { send -- "\r"; exp_continue }
        -exact "installation? \[Y/n\]" { send -- "y\r"; exp_continue }
    }
EOF
Remove manual files to save space
arch-chroot "$ROOTFS" /bin/sh -c 'rm -r /usr/share/man/*'
Use haveged to generate random numbers and feed linux random device. You must run pacman-key –init before first using pacman; the local keyring can then be populated with the keys of all official Arch Linux packagers with pacman-key –populate archlinux.
arch-chroot "$ROOTFS" /bin/sh -c "haveged -w 1024; pacman-key --init; pkill haveged; pacman -Rs --noconfirm haveged; pacman-key --populate $ARCH_KEYRING; pkill gpg-agent"
Set local timezone to UTC
arch-chroot "$ROOTFS" /bin/sh -c "ln -s /usr/share/zoneinfo/UTC /etc/localtime"
Set locale to ‘en_US.UTF-8 UTF-8’
echo 'en_US.UTF-8 UTF-8' > "$ROOTFS"/etc/locale.gen
arch-chroot "$ROOTFS" locale-gen
Set pacman mirrorlist
arch-chroot "$ROOTFS" /bin/sh -c "echo $PACMAN_MIRRORLIST > /etc/pacman.d/mirrorlist"
udev is a device manager for the Linux kernel. Udev primarily manages device nodes in the /dev directory and also handles all user space events raised while hardware devices are added into the system or removed from it, including firmware loading as required by certain devices.
udev doesn’t work in containers, rebuild /dev
DEV=$ROOTFS/dev
rm -rf "$DEV"
mkdir -p "$DEV"
mknod -m 666 "$DEV"/null c 1 3
mknod -m 666 "$DEV"/zero c 1 5
mknod -m 666 "$DEV"/random c 1 8
mknod -m 666 "$DEV"/urandom c 1 9
mkdir -m 755 "$DEV"/pts
mkdir -m 1777 "$DEV"/shm
mknod -m 666 "$DEV"/tty c 5 0
mknod -m 600 "$DEV"/console c 5 1
mknod -m 666 "$DEV"/tty0 c 4 0
mknod -m 666 "$DEV"/full c 1 7
mknod -m 600 "$DEV"/initctl p
mknod -m 666 "$DEV"/ptmx c 5 2
ln -sf /proc/self/fd "$DEV"/fd
tar root file system and import image to Docker.
tar --numeric-owner --xattrs --acls -C "$ROOTFS" -c . | docker import - "$DOCKER_IMAGE_NAME"
Options for tar used :
--numeric-owner  Always use numbers for user/group names.
--xattrs         Enable extended attributes support
--acls           Enable the POSIX ACLs support
-C               Change to directory
-c               Create new archive 
Test new image
docker run --rm -t $DOCKER_IMAGE_NAME echo Success
Delete temp root file system
rm -rf "$ROOTFS"
Running the script should finish with
tar: ./etc/pacman.d/gnupg/S.gpg-agent: socket ignored
sha256:82b0356924efb95e5483417dd4f0cef85fce7afbacb75c18632de7bc45edd796
Success
Script available on my docker repository on github.

Sunday, 13 March 2016

Python Monary Unable to find cmonary shared library libcmonary.so

Monary provides a Python interface for fast column queries from MongoDB. I set-up on Arch Linux as follows :
 $ pacman -S libmongoc python-numpy
 $ pip install pkgconfig
 $ pip install monary
Running the Monary package produces the following error :
RuntimeError: ('Unable to find cmonary shared library: ', 'libcmonary.so')
With some research I found this is an open issue with Monary.
To work around the issue open :
$ sudo vim /usr/lib/python3.5/site-packages/monary/monary.py
And update around line 68 to :
else:
    cmonary_fname = "libcmonary.cpython-35m-x86_64-linux-gnu.so"
I also found errors with the WriteConcern import which as a workaround in the monary.py file I updated to :
sys.path.append('/usr/lib/python3.5/site-packages/monary')
from write_concern import WriteConcern