Вложенные непривилегированные контейнеры lxc от выскочки, которые может остановить их владелец
На хосте, на котором работает Ubuntu 14.04.5 LTS, у меня есть пользователь с именем ci, который может создавать непривилегированные контейнеры lxc, на которых также работает Ubuntu 14.04.5 LTS. У пользователя есть диапазон subid 200000-231071. Файл конфигурации такого контейнера:
# Distribution configuration
lxc.include = /usr/share/lxc/config/ubuntu.common.conf
lxc.include = /usr/share/lxc/config/ubuntu.userns.conf
lxc.arch = x86_64
# Nested
lxc.mount.auto = cgroup
lxc.aa_profile = lxc-container-default-with-nesting
# Container specific configuration
lxc.id_map = u 0 200000 65536
lxc.id_map = u 100000 265536 65536
lxc.id_map = g 0 200000 65536
lxc.id_map = g 100000 265536 65536
lxc.rootfs = /home/ci/.local/share/lxc/ci/rootfs
lxc.utsname = ci
# Network configuration
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxcbr0
lxc.network.hwaddr = 00:16:3e:dd:f1:99
Пользователь может создать и запустить непривилегированный контейнер без проблем:
ci@host:~$ lxc-create -t download -n ci -- -d ubuntu -r trusty -a amd64
ci@host:~$ lxc-start -n ci -d
ci@host:~$ lxc-ls --fancy
NAME STATE IPV4 IPV6 AUTOSTART
---------------------------------------------------
ci RUNNING 10.0.3.75, 10.0.4.1 - NO
На хосте cgmanager работает:
root@host ~ # ps ax | grep cgmanager
382 ? Ss 0:01 /sbin/cgmanager --sigstop -m name=systemd
В непривилегированном контейнере ci работает cgproxy:
root@ci:~# ps ax | grep cgproxy
288 ? Ss 0:00 /sbin/cgproxy --sigstop
В непривилегированном контейнере ci пользователь с именем jenkins с диапазонами subid 100000-65535 может создавать и запускать непривилегированные контейнеры внутри него, то есть непривилегированные вложенные контейнеры, но не без некоторых хитростей, которые:
После входа в систему с помощью ssh от имени пользователя jenkins в непривилегированном контейнере ci, результат
cat /proc/self/cgroup
является:jenkins@ci:~$ cat /proc/self/cgroup 12:hugetlb:/user/1012.user/11.session/lxc/ci 11:net_prio:/user/1012.user/11.session/lxc/ci 10:perf_event:/user/1012.user/11.session/lxc/ci 9:net_cls:/user/1012.user/11.session/lxc/ci 8:freezer:/user/1012.user/11.session/lxc/ci 7:devices:/user/1012.user/11.session/lxc/ci 6:memory:/user/1012.user/11.session/lxc/ci 5:blkio:/user/1012.user/11.session/lxc/ci 4:name=systemd:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 3:cpuacct:/user/1012.user/11.session/lxc/ci 2:cpu:/user/1012.user/11.session/lxc/ci 1:cpuset:/user/1012.user/11.session/lxc/ci
На этом этапе jenkins может создать контейнер, но не может его запустить:
jenkins@ci:~$ lxc-create -t download -n test -- -d ubuntu -r trusty -a amd64 jenkins@ci:~$ lxc-start -n test lxc_container: cgmanager.c: lxc_cgmanager_create: 301 call to cgmanager_create_sync failed: invalid request lxc_container: cgmanager.c: lxc_cgmanager_create: 303 Failed to create hugetlb:lxc/test lxc_container: cgmanager.c: cgm_create: 650 Error creating cgroup hugetlb:lxc/test lxc_container: start.c: lxc_spawn: 891 failed creating cgroups lxc_container: start.c: __lxc_start: 1121 failed to spawn 'test' lxc_container: lxc_start.c: main: 341 The container failed to start. lxc_container: lxc_start.c: main: 345 Additional information can be obtained by setting the --logfile and --logpriority options.
Я выдаю в контейнере от имени root:
restart systemd-logind
Теперь, как пользователь jenkins в контейнере, я выхожу из системы и снова захожу с ssh. Cgroup изменилась, и теперь я могу создать и запустить контейнер:
jenkins@ci:~$ cat /proc/self/cgroup 12:hugetlb:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 11:net_prio:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 10:perf_event:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 9:net_cls:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 8:freezer:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 7:devices:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 6:memory:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 5:blkio:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 4:name=systemd:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 3:cpuacct:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 2:cpu:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session 1:cpuset:/user/1012.user/11.session/lxc/ci/user/1012.user/11.session/lxc/ci/user/107.user/c1.session jenkins@ci:~$ lxc-create -t download -n test -- -d ubuntu -r trusty -a amd64 jenkins@ci:~$ lxc-start -n test -d jenkins@ci:~$ lxc-ls --fancy NAME STATE IPV4 IPV6 AUTOSTART -------------------------------------------- test RUNNING 10.0.4.64 - NO
Первый вопрос: зачем мне restart systemd-logind
и как я могу избежать ввода его как root, прежде чем я смогу создавать вложенные непривилегированные контейнеры?
В контейнере CI я создал файл конфигурации init (файл конфигурации upstart по адресу /etc/init/jenkins.conf) для запуска программного обеспечения Jenkins от имени пользователя jenkins:
description "jenkins"
start on filesystem and static-network-up
stop on runlevel [016]
env USER="jenkins"
env GROUP="jenkins"
env HOME="/var/lib/jenkins"
env JENKINS_LOG="/var/log/jenkins"
env JENKINS_ROOT="/usr/share/jenkins"
env JENKINS_RUN="/var/run/jenkins"
env JENKINS_PIDFILE="jenkins.pid"
pre-start script
test -f $JENKINS_ROOT/jenkins.war || { stop ; exit 0; }
mkdir $JENKINS_RUN > /dev/null 2>&1 || true
chown -R $USER:$GROUP $JENKINS_RUN || true
mkdir $JENKINS_LOG > /dev/null 2>&1 || true
chown -R $USER:$GROUP $JENKINS_LOG || true
end script
script
. /etc/default/jenkins
# export XDG_SESSION_ID="/run/user/`id -u $USER`"
export HOME
export USER
export GROUP
exec daemon --name=jenkins --foreground --inherit --user=$USER:$GROUP --pidfile=$JENKINS_RUN/$JENKINS_PIDFILE --output=$JENKINS_LOG -- $JAVA $JAVA_ARGS -jar $JENKINS_WAR $JENKINS_ARGS
end script
post-start script
while [ ! -f $JENKINS_RUN/$JENKINS_PIDFILE ]; do sleep 1; done
PID=$(cat $JENKINS_RUN/$JENKINS_PIDFILE)
cgm create all $USER
cgm chown all $USER $(id -u $USER) $(id -g $USER)
# this need to be run in the jenkins job script:
# cgm movepid all $USER $$
end script
# vim: ft=upstart
В сценарии, который выдает процесс Jenkins для запуска так называемой сборки Jenkins, если я добавлю строку:
cgm movepid all $USER $$
скрипт может создавать и запускать непривилегированные вложенные контейнеры, являясь его группой:
+ cat /proc/self/cgroup
12:hugetlb:/user/1012.user/11.session/lxc/ci/jenkins
11:net_prio:/user/1012.user/11.session/lxc/ci/jenkins
10:perf_event:/user/1012.user/11.session/lxc/ci/jenkins
9:net_cls:/user/1012.user/11.session/lxc/ci/jenkins
8:freezer:/user/1012.user/11.session/lxc/ci/jenkins
7:devices:/user/1012.user/11.session/lxc/ci/jenkins
6:memory:/user/1012.user/11.session/lxc/ci/jenkins
5:blkio:/user/1012.user/11.session/lxc/ci/jenkins
4:name=systemd:/user/1012.user/11.session/lxc/ci/jenkins
3:cpuacct:/user/1012.user/11.session/lxc/ci/jenkins
2:cpu:/user/1012.user/11.session/lxc/ci/jenkins
1:cpuset:/user/1012.user/11.session/lxc/ci/jenkins
но пользователь jenkins, вошедший в систему с помощью ssh, не может остановить контейнер, созданный сценарием. Следующее никогда не заканчивается:
jenkins@ci:~$ lxc-stop -n test
Второй вопрос: как мне добиться, чтобы пользователь jenkins мог остановить любой контейнер, созданный пользователем jenkins, из сценария инициализации, подобного приведенному выше?