From f203a91e1a779e3ca05c414202482d9343a777a0 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Mon, 16 Apr 2018 14:24:39 +0100 Subject: [PATCH] tests: support CentOS Docker images. - namespace & document test accounts in README.md - standardize the password format everywhere, and ensure the passwords differ everywhere. - Add MITOGEN_TEST_DISTRO environment variable. --- tests/README.md | 30 +++++++ tests/build_docker_image.py | 64 -------------- tests/build_docker_images.py | 83 +++++++++++++++++++ ...ubkey.key => mitogen__has_sudo_pubkey.key} | 0 ...y.pub => mitogen__has_sudo_pubkey.key.pub} | 0 tests/fakessh_test.py | 12 +-- tests/ssh_test.py | 24 +++--- tests/testlib.py | 14 +++- 8 files changed, 142 insertions(+), 85 deletions(-) delete mode 100755 tests/build_docker_image.py create mode 100755 tests/build_docker_images.py rename tests/data/docker/{has-sudo-pubkey.key => mitogen__has_sudo_pubkey.key} (100%) rename tests/data/docker/{has-sudo-pubkey.key.pub => mitogen__has_sudo_pubkey.key.pub} (100%) diff --git a/tests/README.md b/tests/README.md index 41c024b5..4756541d 100644 --- a/tests/README.md +++ b/tests/README.md @@ -28,3 +28,33 @@ and run the tests there. 1. Enable the virtual environment we just built ``source ../venv/bin/activate`` 1. Install Mitogen in pip editable mode ``pip install -e .`` 1. Run ``test`` + + +# User Accounts + +A set of standard user account names are used by in the Docker container and +also by Ansible's `osx_setup.yml`. + +`root` + In the Docker image only, the password is "rootpassword". + +`mitogen__has_sudo` + The login password is "has_sudo_password" and the account is capable of + sudoing to root, by supplying the account password to sudo. + +`mitogen__has_sudo_pubkey` + The login password is "has_sudo_pubkey_password". Additionally + `tests/data/docker/mitogen__has_sudo_pubkey.key` SSH key may be used to log + in. It can sudo the same as `mitogen__has_sudo`. + +`mitogen__has_sudo_nopw` + The login password is "has_sudo_nopw_password". It can sudo to root without + supplying a password. + +`mitogen__user1` .. `mitogen__user21` + These accounts do not have passwords set. They exist to test the Ansible + interpreter recycling logic. + +`mitogen__webapp` + A plain old account with no sudo access, used as the target for fakessh + tddests. diff --git a/tests/build_docker_image.py b/tests/build_docker_image.py deleted file mode 100755 index 9a8a057c..00000000 --- a/tests/build_docker_image.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python - -import commands -import os -import shlex -import subprocess -import tempfile - - -DOCKERFILE = r""" -FROM debian:stable -RUN apt-get update -RUN \ - apt-get install -y python2.7 openssh-server sudo rsync git strace && \ - apt-get clean -RUN \ - mkdir /var/run/sshd && \ - echo '%sudo-nopw ALL=(ALL:ALL) NOPASSWD:ALL' > /etc/sudoers.d/001-sudo-nopw && \ - echo i-am-mitogen-test-docker-image > /etc/sentinel && \ - groupadd sudo-nopw && \ - useradd -m has-sudo -G sudo && \ - useradd -m has-sudo-pubkey -G sudo && \ - useradd -m has-sudo-nopw -G sudo-nopw && \ - useradd -m webapp && \ - ( echo 'root:x' | chpasswd; ) && \ - ( echo 'has-sudo:y' | chpasswd; ) && \ - ( echo 'has-sudo-pubkey:y' | chpasswd; ) && \ - ( echo 'has-sudo-nopw:y' | chpasswd; ) && \ - mkdir ~has-sudo-pubkey/.ssh && \ - { echo '#!/bin/bash\nexec strace -ff -o /tmp/pywrap$$.trace python2.7 "$@"' > /usr/local/bin/pywrap; chmod +x /usr/local/bin/pywrap; } && \ - { for i in `seq 1 21`; do useradd -s /bin/bash -m mitogen__user$i; done; } - -COPY data/docker/has-sudo-pubkey.key.pub /home/has-sudo-pubkey/.ssh/authorized_keys -RUN \ - chown -R has-sudo-pubkey ~has-sudo-pubkey && \ - chmod -R go= ~has-sudo-pubkey - -RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config -RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd - -ENV NOTVISIBLE "in users profile" -RUN echo "export VISIBLE=now" >> /etc/profile - -EXPOSE 22 -CMD ["/usr/sbin/sshd", "-D"] - -""" - - -def sh(s, *args): - if args: - s %= tuple(map(commands.mkarg, args)) - return shlex.split(s) - - -mydir = os.path.abspath(os.path.dirname(__file__)) -with tempfile.NamedTemporaryFile(dir=mydir) as dockerfile_fp: - dockerfile_fp.write(DOCKERFILE) - dockerfile_fp.flush() - - subprocess.check_call(sh('docker build %s -t d2mw/mitogen-test -f %s', - mydir, - dockerfile_fp.name - )) diff --git a/tests/build_docker_images.py b/tests/build_docker_images.py new file mode 100755 index 00000000..72306b03 --- /dev/null +++ b/tests/build_docker_images.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +""" +Build the Docker images used for testing. +""" + +import commands +import os +import shlex +import subprocess +import tempfile + + +DEBIAN_DOCKERFILE = r""" +FROM debian:stable +RUN apt-get update +RUN \ + apt-get install -y python2.7 openssh-server sudo rsync git strace && \ + apt-get clean +""" + +CENTOS_DOCKERFILE = r""" +FROM centos:7 +RUN yum clean all && \ + yum -y install -y python2.7 openssh-server sudo rsync git strace sudo && \ + yum clean all && \ + groupadd sudo + +""" + +DOCKERFILE = r""" +RUN \ + mkdir /var/run/sshd && \ + echo '%mitogen__sudo_nopw ALL=(ALL:ALL) NOPASSWD:ALL' > /etc/sudoers.d/001-mitogen__sudo_nopw && \ + echo i-am-mitogen-test-docker-image > /etc/sentinel && \ + groupadd mitogen__sudo_nopw && \ + useradd -m mitogen__has_sudo -G SUDO_GROUP && \ + useradd -m mitogen__has_sudo_pubkey -G SUDO_GROUP && \ + useradd -m mitogen__has_sudo_nopw -G mitogen__sudo_nopw && \ + useradd -m mitogen__webapp && \ + ( echo 'root:rootpassword' | chpasswd; ) && \ + ( echo 'mitogen__has_sudo:has_sudo_password' | chpasswd; ) && \ + ( echo 'mitogen__has_sudo_pubkey:has_sudo_pubkey_password' | chpasswd; ) && \ + ( echo 'mitogen__has_sudo_nopw:has_sudo_nopw_password' | chpasswd; ) && \ + mkdir ~mitogen__has_sudo_pubkey/.ssh && \ + { echo '#!/bin/bash\nexec strace -ff -o /tmp/pywrap$$.trace python2.7 "$@"' > /usr/local/bin/pywrap; chmod +x /usr/local/bin/pywrap; } && \ + { for i in `seq 1 21`; do useradd -s /bin/bash -m mitogen__user$i; done; } + +COPY data/docker/mitogen__has_sudo_pubkey.key.pub /home/mitogen__has_sudo_pubkey/.ssh/authorized_keys +RUN \ + chown -R mitogen__has_sudo_pubkey ~mitogen__has_sudo_pubkey && \ + chmod -R go= ~mitogen__has_sudo_pubkey + +RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd + +ENV NOTVISIBLE "in users profile" +RUN echo "export VISIBLE=now" >> /etc/profile + +EXPOSE 22 +CMD ["/usr/sbin/sshd", "-D"] +""" + + +def sh(s, *args): + if args: + s %= tuple(map(commands.mkarg, args)) + return shlex.split(s) + + +for (distro, wheel, prefix) in (('debian', 'sudo', DEBIAN_DOCKERFILE), + ('centos', 'wheel', CENTOS_DOCKERFILE)): + mydir = os.path.abspath(os.path.dirname(__file__)) + with tempfile.NamedTemporaryFile(dir=mydir) as dockerfile_fp: + dockerfile_fp.write(prefix) + dockerfile_fp.write(DOCKERFILE.replace('SUDO_GROUP', wheel)) + dockerfile_fp.flush() + + subprocess.check_call(sh('docker build %s -t %s -f %s', + mydir, + 'd2mw/mitogen-%s-test' % (distro,), + dockerfile_fp.name + )) diff --git a/tests/data/docker/has-sudo-pubkey.key b/tests/data/docker/mitogen__has_sudo_pubkey.key similarity index 100% rename from tests/data/docker/has-sudo-pubkey.key rename to tests/data/docker/mitogen__has_sudo_pubkey.key diff --git a/tests/data/docker/has-sudo-pubkey.key.pub b/tests/data/docker/mitogen__has_sudo_pubkey.key.pub similarity index 100% rename from tests/data/docker/has-sudo-pubkey.key.pub rename to tests/data/docker/mitogen__has_sudo_pubkey.key.pub diff --git a/tests/fakessh_test.py b/tests/fakessh_test.py index 8711b46b..8623d0ee 100644 --- a/tests/fakessh_test.py +++ b/tests/fakessh_test.py @@ -29,17 +29,17 @@ class RsyncTest(testlib.DockerMixin, unittest2.TestCase): @timeoutcontext.timeout(5) def test_rsync_between_direct_children(self): - # master -> SSH -> has-sudo-pubkey -> rsync(.ssh) -> master -> - # has-sudo -> rsync + # master -> SSH -> mitogen__has_sudo_pubkey -> rsync(.ssh) -> master -> + # mitogen__has_sudo -> rsync pubkey_acct = self.docker_ssh( - username='has-sudo-pubkey', - identity_file=testlib.data_path('docker/has-sudo-pubkey.key'), + username='mitogen__has_sudo_pubkey', + identity_file=testlib.data_path('docker/mitogen__has_sudo_pubkey.key'), ) nopw_acct = self.docker_ssh( - username='has-sudo-nopw', - password='y', + username='mitogen__has_sudo_nopw', + password='has_sudo_nopw_password', ) webapp_acct = self.router.sudo( diff --git a/tests/ssh_test.py b/tests/ssh_test.py index cf868ad3..114c72b9 100644 --- a/tests/ssh_test.py +++ b/tests/ssh_test.py @@ -12,7 +12,7 @@ class FakeSshTest(testlib.RouterMixin, unittest2.TestCase): def test_okay(self): context = self.router.ssh( hostname='hostname', - username='has-sudo', + username='mitogen__has_sudo', ssh_path=testlib.data_path('fakessh.py'), ) #context.call(mitogen.utils.log_to_file, '/tmp/log') @@ -25,8 +25,8 @@ class SshTest(testlib.DockerMixin, unittest2.TestCase): def test_stream_name(self): context = self.docker_ssh( - username='has-sudo', - password='y', + username='mitogen__has_sudo', + password='has_sudo_password', ) name = 'ssh.%s:%s' % ( self.dockerized_ssh.get_host(), @@ -36,8 +36,8 @@ class SshTest(testlib.DockerMixin, unittest2.TestCase): def test_via_stream_name(self): context = self.docker_ssh( - username='has-sudo-nopw', - password='y', + username='mitogen__has_sudo_nopw', + password='has_sudo_nopw_password', ) sudo = self.router.sudo(via=context) @@ -50,7 +50,7 @@ class SshTest(testlib.DockerMixin, unittest2.TestCase): def test_password_required(self): try: context = self.docker_ssh( - username='has-sudo', + username='mitogen__has_sudo', ) assert 0, 'exception not thrown' except mitogen.ssh.PasswordError, e: @@ -61,7 +61,7 @@ class SshTest(testlib.DockerMixin, unittest2.TestCase): def test_password_incorrect(self): try: context = self.docker_ssh( - username='has-sudo', + username='mitogen__has_sudo', password='badpw', ) assert 0, 'exception not thrown' @@ -72,8 +72,8 @@ class SshTest(testlib.DockerMixin, unittest2.TestCase): def test_password_specified(self): context = self.docker_ssh( - username='has-sudo', - password='y', + username='mitogen__has_sudo', + password='has_sudo_password', ) self.assertEqual( @@ -84,7 +84,7 @@ class SshTest(testlib.DockerMixin, unittest2.TestCase): def test_pubkey_required(self): try: context = self.docker_ssh( - username='has-sudo-pubkey', + username='mitogen__has_sudo_pubkey', ) assert 0, 'exception not thrown' except mitogen.ssh.PasswordError, e: @@ -94,8 +94,8 @@ class SshTest(testlib.DockerMixin, unittest2.TestCase): def test_pubkey_specified(self): context = self.docker_ssh( - username='has-sudo-pubkey', - identity_file=testlib.data_path('docker/has-sudo-pubkey.key'), + username='mitogen__has_sudo_pubkey', + identity_file=testlib.data_path('docker/mitogen__has_sudo_pubkey.key'), ) self.assertEqual( 'i-am-mitogen-test-docker-image\n', diff --git a/tests/testlib.py b/tests/testlib.py index 199e8a46..bc574b4e 100644 --- a/tests/testlib.py +++ b/tests/testlib.py @@ -170,11 +170,19 @@ def get_docker_host(docker): class DockerizedSshDaemon(object): + image = None + + def get_image(self): + if not self.image: + distro = os.environ.get('MITOGEN_TEST_DISTRO', 'debian') + self.image = 'd2mw/mitogen-%s-test' % (distro,) + return self.image + def __init__(self): self.docker = docker.from_env(version='auto') self.container_name = 'mitogen-test-%08x' % (random.getrandbits(64),) self.container = self.docker.containers.run( - image='d2mw/mitogen-test', + image=self.get_image(), detach=True, privileged=True, publish_all_ports=True, @@ -239,6 +247,6 @@ class DockerMixin(RouterMixin): def docker_ssh_any(self, **kwargs): return self.docker_ssh( - username='has-sudo-nopw', - password='y', + username='mitogen__has_sudo_nopw', + password='has_sudo_nopw_password', )