Control: tags -1 +wontfix

Le mercredi 01 janvier 2020 à 02:05:37+0100, Johannes 'josch' Schauer a écrit :
> Package: lxc
> Version: 1:3.1.0+really3.0.4-2
> Severity: normal
> 
> Hi,
> 
> when booting into a system started with unprivileged lxc, I'm getting
> the following errors:
> 
> [    5.818300] audit: type=1400 audit(1577840118.455:15): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=388 comm="(md-udevd)" 
> flags="rw, rslave"
> [    5.842226] audit: type=1400 audit(1577840118.479:16): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=389 comm="(md-udevd)" 
> flags="rw, rslave"
> [    5.875326] audit: type=1400 audit(1577840118.511:17): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=390 comm="(md-udevd)" 
> flags="rw, rslave"
> [    5.894556] audit: type=1400 audit(1577840118.531:18): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=391 comm="(md-udevd)" 
> flags="rw, rslave"
> [    5.919489] audit: type=1400 audit(1577840118.555:19): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=392 comm="(md-udevd)" 
> flags="rw, rslave"
> [    6.368326] audit: type=1400 audit(1577840119.003:20): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=398 comm="(modprobe)" 
> flags="rw, rslave"
> [    6.390053] audit: type=1400 audit(1577840119.027:21): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=400 comm="(d-logind)" 
> flags="rw, rslave"
> [    6.400681] audit: type=1400 audit(1577840119.035:22): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=405 comm="(modprobe)" 
> flags="rw, rslave"
> [    6.406682] audit: type=1400 audit(1577840119.043:23): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=406 comm="(d-logind)" 
> flags="rw, rslave"
> [    6.416232] audit: type=1400 audit(1577840119.051:24): apparmor="DENIED" 
> operation="mount" info="failed flags match" error=-13 
> profile="lxc-container-default-cgns" name="/" pid=409 comm="(modprobe)" 
> flags="rw, rslave"
> 
> These errors keep repeating. The only way to silence them I found so far
> is to disable apparmor. But maybe the default lxc apparmor profile could
> be adjusted to prevent these errors?
> 
> To reproduce the problem, please see the attached shell script which you
> can run without superuser privileges if you have
> kernel.unprivileged_userns_clone set to 1. After running the script, the
> errors above can be seen in qemu.log.
> 
> Thanks!
> 
> cheers, josch

> #!/bin/sh
> 
> # Copyright 2019 Johannes 'josch' Schauer <jo...@debian.org>
> #
> # Permission is hereby granted, free of charge, to any person obtaining a copy
> # of this software and associated documentation files (the "Software"), to 
> deal
> # in the Software without restriction, including without limitation the rights
> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> # copies of the Software, and to permit persons to whom the Software is
> # furnished to do so, subject to the following conditions:
> #
> # The above copyright notice and this permission notice shall be included in 
> all
> # copies or substantial portions of the Software.
> 
> set -exu
> 
> # Reasons to use qemu:
> #   - can also be run with autopkgtest backends that do not support 
> #   - can be run without superuser privileges outside of autopkgtest
> 
> if [ -z ${AUTOPKGTEST_TMP+x} ]; then
>       # if AUTOPKGTEST_TMP is not set, then this script is probably not
>       # executed under autopkgtest, choose unshare mode for mmdebstrap so
>       # that this script can be run without superuser privileges
>       MODE="unshare"
> else
>       # since AUTOPKGTEST_TMP is set, we assume that this script is executed
>       # under autopkgtest --> switch to the temporary directory
>       cd "$AUTOPKGTEST_TMP"
>       # We have to use root mode on salsa ci because:
>       #  - unshare mode fails because /sys is mounted read-only
>       #    and kernel.unprivileged_userns_clone is not set to 1
>       #  - fakechroot mode fails because of #944929
>       #  - proot mode produces wrong permissions
>       MODE="root"
> fi
> 
> # setting up /etc/fstab, /etc/hostname and /etc/hosts is required to raise
> # network interfaces
> if [ ! -e debian-unstable-host.tar ]; then
> mmdebstrap --mode=$MODE --variant=apt \
>       
> --include=openssh-server,systemd-sysv,ifupdown,netbase,isc-dhcp-client,udev,policykit-1,linux-image-amd64,lxc,uidmap,apparmor,bridge-utils,procps,iptables,iptables-persistent,psmisc
>  \
>       --customize-hook='echo host > "$1/etc/hostname"' \
>       --customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \
>       --customize-hook='echo "/dev/vda1 / auto errors=remount-ro 0 1" > 
> "$1/etc/fstab"' \
>       --customize-hook='cat /etc/resolv.conf > "$1/etc/resolv.conf"' \
>       unstable debian-unstable-host.tar
> fi
> 
> # we prepare a second tarball now instead of later inside qemu because
> # running mmdebstrap without kvm just wastes cpu cycles
> if [ ! -e debian-unstable-container.tar ]; then
> mmdebstrap --mode=$MODE --variant=apt 
> --include=dbus,udev,systemd-sysv,policykit-1 \
>       --customize-hook='echo container > "$1/etc/hostname"' \
>       --customize-hook='cat /etc/resolv.conf > "$1/etc/resolv.conf"' \
>       unstable - \
>       | python3 -c '
> import tarfile
> import sys
> 
> with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as in_tar, \
>     tarfile.open(fileobj=sys.stdout.buffer, mode="w|") as out_tar:
>     for member in in_tar:
>         member.uid += 1000000
>         member.gid += 1000000
>         if member.isfile():
>             with in_tar.extractfile(member) as file:
>                 out_tar.addfile(member, file)
>         else:
>             out_tar.addfile(member)
> ' > debian-unstable-container.tar
> fi
> 
> 
> echo 'net.ipv4.ip_forward=1' > sysctl.conf
> 
> cat << END > rules.v4
> *nat
> :PREROUTING ACCEPT [0:0]
> :INPUT ACCEPT [0:0]
> :OUTPUT ACCEPT [0:0]
> :POSTROUTING ACCEPT [0:0]
> -A PREROUTING -i eth0 -p tcp -m tcp --dport 8002 -j DNAT --to-destination 
> 10.0.0.2:8003
> -A POSTROUTING -o eth0 -j MASQUERADE
> COMMIT
> *filter
> :INPUT ACCEPT [0:0]
> :FORWARD ACCEPT [0:0]
> :OUTPUT ACCEPT [0:0]
> COMMIT
> END
> 
> # extlinux config to boot from /dev/vda1 with predictable network interface
> # naming and a serial console for logging
> cat << END > extlinux.conf
> default linux
> timeout 0
> 
> label linux
> kernel /vmlinuz
> append initrd=/initrd.img root=/dev/vda1 net.ifnames=0 console=ttyS0
> END
> 
> # network interface config
> # we can use eth0 because we boot with net.ifnames=0 for predictable interface
> # names
> cat << END > interfaces
> auto lo
> iface lo inet loopback
> 
> auto eth0
> iface eth0 inet dhcp
> 
> auto lxcbridge
> iface lxcbridge inet static
>   address 10.0.0.1
>   netmask 255.255.255.0
>   bridge_stp off
>   bridge_waitport 0
>   bridge_fd 0
>   bridge_ports none
> END
> 
> echo 'root:1000000:65536' >> subuid
> echo 'root:1000000:65536' >> subgid
> 
> cat << END > config
> lxc.include = /usr/share/lxc/config/common.conf
> lxc.include = /usr/share/lxc/config/userns.conf
> lxc.uts.name = container
> lxc.rootfs.path = dir:/srv/container
> lxc.idmap = u 0 1000000 65536
> lxc.idmap = g 0 1000000 65536
> lxc.net.0.ipv4.address.address = 10.0.0.2/24
> lxc.net.0.hwaddr = ee:ec:fa:e9:56:7d
> lxc.net.0.type = veth
> lxc.net.0.flags = up
> lxc.net.0.link = lxcbridge
> lxc.net.0.name = eth0
> lxc.net.0.mtu = 1500
> lxc.net.0.ipv4.gateway = 10.0.0.1
> lxc.start.auto = 1
> lxc.tty.max = 1
> END
> 
> if [ ! -e id_rsa ]; then
> ssh-keygen -q -t rsa -f ./id_rsa -N ""
> fi
> 
> # use guestfish to prepare the host system
> #
> #  - create a single 2G partition and unpack the rootfs tarball into it
> #  - copy the public key into .ssh/authorized_keys for the root user
> #  - copy in extlinux.conf and /etc/network/interfaces
> #  - unpack the tarball of the container into /srv/container
> #  - put a syslinux MBR into the first 440 bytes of the drive
> #  - install extlinux and make partition bootable
> #
> # useful stuff to debug any errors:
> #   LIBGUESTFS_BACKEND_SETTINGS=force_tcg
> #   libguestfs-test-tool || true
> #   export LIBGUESTFS_DEBUG=1 LIBGUESTFS_TRACE=1
> guestfish -N host.img=disk:2G -- \
>       part-disk /dev/sda mbr : \
>       mkfs ext2 /dev/sda1 : \
>       mount /dev/sda1 / : \
>       tar-in debian-unstable-host.tar / : \
>       mkdir /root/.ssh : \
>       upload id_rsa.pub /root/.ssh/authorized_keys : \
>       chown 0 0 /root/.ssh/authorized_keys : \
>       copy-in extlinux.conf / : \
>       copy-in interfaces /etc/network : \
>       copy-in sysctl.conf /etc : \
>       copy-in rules.v4 /etc/iptables : \
>       copy-in subuid /etc : \
>       copy-in subgid /etc : \
>       mkdir /var/lib/lxc/container : \
>       copy-in config /var/lib/lxc/container : \
>       mkdir /srv/container : \
>       tar-in debian-unstable-container.tar /srv/container : \
>       upload /usr/lib/SYSLINUX/mbr.bin /mbr.bin : \
>       copy-file-to-device /mbr.bin /dev/sda size:440 : \
>       rm /mbr.bin : \
>       extlinux / : \
>       sync : \
>       umount / : \
>       part-set-bootable /dev/sda 1 true : \
>       shutdown
> 
> 
> # start the host system
> # redirect tcp connections on port 10011 localhost to the host system port 22
> # redirect all output to a file
> # run in the background
> qemu-system-x86_64 \
>       -enable-kvm \
>       -no-user-config \
>       -object rng-random,filename=/dev/urandom,id=rng0 -device 
> virtio-rng-pci,rng=rng0 \
>       -m 1G \
>       -net nic,model=virtio \
>       -nographic \
>       -serial mon:stdio \
>       -net 
> user,hostfwd=tcp:127.0.0.1:10022-:22,hostfwd=tcp:127.0.0.1:8001-:8002 \
>       -drive file=host.img,format=raw,if=virtio \
>       >qemu.log </dev/null 2>&1 &
> 
> # store the pid
> QEMUPID=$!
> 
> # show the log and kill qemu in case the script exits first
> trap "cat --show-nonprinting qemu.log; kill $QEMUPID" EXIT
> 
> # the default ssh command does not store known hosts and even ignores host 
> keys
> # it identifies itself with the rsa key generated above
> # pseudo terminal allocation is disabled or otherwise, programs executed via
> # ssh might wait for input on stdin of the ssh process
> ssh="ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -i id_rsa 
> -T"
> 
> # we use sleepenh to make sure that we wait the right number of seconds
> # independent on how long the command took beforehand
> TIMESTAMP=$(sleepenh 0 || [ $? -eq 1 ])
> # the timeout in seconds
> TIMEOUT=5
> # the maximum number of tries
> NUM_TRIES=20
> i=0
> while true; do
>       rv=0
>       $ssh -p 10022 -o ConnectTimeout=$TIMEOUT root@localhost echo success || 
> rv=1
>       # with an exit code of zero, the ssh connection was successful
>       # and we break out of the loop
>       [ $rv -eq 0 ] && break
>       # if the command before took less than $TIMEOUT seconds, wait the 
> remaining time
>       TIMESTAMP=$(sleepenh $TIMESTAMP $TIMEOUT || [ $? -eq 1 ]);
>       # increment the counter and break out of the loop if we tried
>       # too often
>       i=$((i+1))
>       if [ $i -ge $NUM_TRIES ]; then
>               break
>       fi
> done
> 
> # if all tries were exhausted, the process failed
> if [ $i -eq $NUM_TRIES ]; then
>       echo "timeout reached: unable to connect to qemu via ssh"
>       exit 1
> fi
> 
> # ip of host system outside qemu: 10.0.2.2
> # ip of host system inside qemu: 10.0.2.15
> # ip of container: 10.0.0.2
> 
> # execute a shell script on the host system
> cat << END > expected
> NAME      STATE   AUTOSTART IPV4     UNPRIVILEGED 
> container RUNNING 1         10.0.0.2 true         
> END
> 
> $ssh -p 10022 root@localhost lxc-ls --fancy 
> --fancy-format=NAME,STATE,AUTOSTART,IPV4,UNPRIVILEGED | diff -u expected -
> 
> $ssh -p 10022 root@localhost lxc-attach --name=container -- apt-get update
> $ssh -p 10022 root@localhost lxc-attach --name=container -- apt-get install 
> --no-install-recommends --yes netcat-traditional procps
> 
> $ssh -p 10022 root@localhost lxc-attach --name=container -- nc -lp 8003 | 
> grep foobar &
> NCPID=$!
> 
> sleep 5
> 
> echo foobar | nc -q0 127.0.0.1 8001
> 
> wait $NCPID
> 
> LXCPID=$($ssh -p 10022 root@localhost lxc-info --name container | awk 
> '/^PID:/ { print $2; }')
> $ssh -p 10022 root@localhost ps -jf -u 1000000 | awk "\$2 == $LXCPID" | grep 
> /sbin/init
> $ssh -p 10022 root@localhost lxc-attach --name=container -- ps -jf -u 0 | 
> grep /sbin/init
> 
> trap - EXIT
> 
> $ssh -p 10022 root@localhost systemctl poweroff || true
> 
> wait $QEMUPID
> 
> # debian-unstable-container.tar debian-unstable-host.tar id_rsa id_rsa.pub 
> qemu.log
> for f in extlinux.conf interfaces host.img; do
>       rm "$f"
> done

Dear Josch,

I'm sorry but lxc unprivileged containers can't run with any apparmor
profile. You have to set this parameter to unconfined for your
unprivileged containers. Setting a default profile for unconfined
containers is a hard thing as only etc/default/lxc.conf is an option,
but it'd also apply to privileged containers.

With best regards,

-- 
Pierre-Elliott Bécue
GPG: 9AE0 4D98 6400 E3B6 7528  F493 0D44 2664 1949 74E2
It's far easier to fight for one's principles than to live up to them.

Attachment: signature.asc
Description: PGP signature

Reply via email to