diff options
Diffstat (limited to 'test/integration/targets/become_unprivileged')
10 files changed, 383 insertions, 0 deletions
diff --git a/test/integration/targets/become_unprivileged/action_plugins/tmpdir.py b/test/integration/targets/become_unprivileged/action_plugins/tmpdir.py new file mode 100644 index 0000000..b7cbb7a --- /dev/null +++ b/test/integration/targets/become_unprivileged/action_plugins/tmpdir.py @@ -0,0 +1,14 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + result = super(ActionModule, self).run(tmp, task_vars) + result.update(self._execute_module('ping', task_vars=task_vars)) + result['tmpdir'] = self._connection._shell.tmpdir + return result diff --git a/test/integration/targets/become_unprivileged/aliases b/test/integration/targets/become_unprivileged/aliases new file mode 100644 index 0000000..70cf577 --- /dev/null +++ b/test/integration/targets/become_unprivileged/aliases @@ -0,0 +1,5 @@ +destructive +shippable/posix/group3 +needs/ssh +needs/root +context/controller diff --git a/test/integration/targets/become_unprivileged/chmod_acl_macos/test.yml b/test/integration/targets/become_unprivileged/chmod_acl_macos/test.yml new file mode 100644 index 0000000..eaa5f5f --- /dev/null +++ b/test/integration/targets/become_unprivileged/chmod_acl_macos/test.yml @@ -0,0 +1,26 @@ +- name: Tests for chmod +a ACL functionality on macOS + hosts: ssh + gather_facts: yes + remote_user: unpriv1 + become: yes + become_user: unpriv2 + + tasks: + - name: Get AnsiballZ temp directory + action: tmpdir + register: tmpdir + become_user: unpriv2 + become: yes + + - name: run whoami + command: whoami + register: whoami + + - name: Ensure we used the right fallback + shell: ls -le /var/tmp/ansible*/*_command.py + register: ls + + - assert: + that: + - whoami.stdout == "unpriv2" + - "'user:unpriv2 allow read' in ls.stdout" diff --git a/test/integration/targets/become_unprivileged/cleanup_unpriv_users.yml b/test/integration/targets/become_unprivileged/cleanup_unpriv_users.yml new file mode 100644 index 0000000..8be2fe6 --- /dev/null +++ b/test/integration/targets/become_unprivileged/cleanup_unpriv_users.yml @@ -0,0 +1,53 @@ +- name: Clean up host and remove unprivileged users + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + # Do this first so we can use tilde notation while the user still exists + - name: Delete homedirs + file: + path: '~{{ item }}' + state: absent + with_items: + - unpriv1 + - unpriv2 + + - name: Delete users + user: + name: "{{ item }}" + state: absent + force: yes # I think this is needed in case pipelining is used and the session remains open + with_items: + - unpriv1 + - unpriv2 + + - name: Delete groups + group: + name: "{{ item }}" + state: absent + with_items: + - acommongroup + - unpriv1 + - unpriv2 + + - name: Fix sudoers.d path for FreeBSD + set_fact: + sudoers_etc: /usr/local/etc + when: ansible_distribution == 'FreeBSD' + + - name: Fix sudoers.d path for everything else + set_fact: + sudoers_etc: /etc + when: ansible_distribution != 'FreeBSD' + + - name: Undo OpenSUSE + lineinfile: + path: "{{ sudoers_etc }}/sudoers" + regexp: '^### Defaults targetpw' + line: 'Defaults targetpw' + backrefs: yes + + - name: Nuke custom sudoers file + file: + path: "{{ sudoers_etc }}/sudoers.d/unpriv1" + state: absent diff --git a/test/integration/targets/become_unprivileged/common_remote_group/cleanup.yml b/test/integration/targets/become_unprivileged/common_remote_group/cleanup.yml new file mode 100644 index 0000000..41784fc --- /dev/null +++ b/test/integration/targets/become_unprivileged/common_remote_group/cleanup.yml @@ -0,0 +1,35 @@ +- name: Cleanup (as root) + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + - name: Remove group for unprivileged users + group: + name: commongroup + state: absent + + - name: Check if /usr/bin/setfacl exists + stat: + path: /usr/bin/setfacl + register: usr_bin_setfacl + + - name: Check if /bin/setfacl exists + stat: + path: /bin/setfacl + register: bin_setfacl + + - name: Set path to setfacl + set_fact: + setfacl_path: /usr/bin/setfacl + when: usr_bin_setfacl.stat.exists + + - name: Set path to setfacl + set_fact: + setfacl_path: /bin/setfacl + when: bin_setfacl.stat.exists + + - name: chmod +x setfacl + file: + path: "{{ setfacl_path }}" + mode: a+x + when: setfacl_path is defined diff --git a/test/integration/targets/become_unprivileged/common_remote_group/setup.yml b/test/integration/targets/become_unprivileged/common_remote_group/setup.yml new file mode 100644 index 0000000..1e799c4 --- /dev/null +++ b/test/integration/targets/become_unprivileged/common_remote_group/setup.yml @@ -0,0 +1,43 @@ +- name: Prep (as root) + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + - name: Create group for unprivileged users + group: + name: commongroup + + - name: Add them to the group + user: + name: "{{ item }}" + groups: commongroup + append: yes + with_items: + - unpriv1 + - unpriv2 + + - name: Check if /usr/bin/setfacl exists + stat: + path: /usr/bin/setfacl + register: usr_bin_setfacl + + - name: Check if /bin/setfacl exists + stat: + path: /bin/setfacl + register: bin_setfacl + + - name: Set path to setfacl + set_fact: + setfacl_path: /usr/bin/setfacl + when: usr_bin_setfacl.stat.exists + + - name: Set path to setfacl + set_fact: + setfacl_path: /bin/setfacl + when: bin_setfacl.stat.exists + + - name: chmod -x setfacl to disable it + file: + path: "{{ setfacl_path }}" + mode: a-x + when: setfacl_path is defined diff --git a/test/integration/targets/become_unprivileged/common_remote_group/test.yml b/test/integration/targets/become_unprivileged/common_remote_group/test.yml new file mode 100644 index 0000000..4bc51f8 --- /dev/null +++ b/test/integration/targets/become_unprivileged/common_remote_group/test.yml @@ -0,0 +1,36 @@ +- name: Tests for ANSIBLE_COMMON_REMOTE_GROUP functionality + hosts: ssh + gather_facts: yes + remote_user: unpriv1 + + tasks: + - name: foo + action: tmpdir + register: tmpdir + become_user: unpriv2 + become: yes + + - name: run whoami with become + command: whoami + register: whoami + become_user: unpriv2 + become: yes + + - set_fact: + stat_cmd: stat -c '%U %G' {{ tmpdir.tmpdir }}/* + when: ansible_distribution not in ['MacOSX', 'FreeBSD'] + + - set_fact: + stat_cmd: stat -f '%Su %Sg' {{ tmpdir.tmpdir }}/* + when: ansible_distribution in ['MacOSX', 'FreeBSD'] + + - name: Ensure we tested the right fallback + shell: "{{ stat_cmd }}" + register: stat + become_user: unpriv2 + become: yes + + - assert: + that: + - whoami.stdout == "unpriv2" + - stat.stdout == 'unpriv1 commongroup' diff --git a/test/integration/targets/become_unprivileged/inventory b/test/integration/targets/become_unprivileged/inventory new file mode 100644 index 0000000..025d8cf --- /dev/null +++ b/test/integration/targets/become_unprivileged/inventory @@ -0,0 +1,10 @@ +[ssh] +#ssh-pipelining ansible_ssh_pipelining=true +ssh-no-pipelining ansible_ssh_pipelining=false +[ssh:vars] +ansible_host=localhost +ansible_connection=ssh +ansible_python_interpreter="{{ ansible_playbook_python }}" + +[all:vars] +ansible_python_interpreter="{{ ansible_playbook_python }}"
\ No newline at end of file diff --git a/test/integration/targets/become_unprivileged/runme.sh b/test/integration/targets/become_unprivileged/runme.sh new file mode 100755 index 0000000..7a3f7b8 --- /dev/null +++ b/test/integration/targets/become_unprivileged/runme.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +set -eux + +export ANSIBLE_KEEP_REMOTE_FILES=True +ANSIBLE_ACTION_PLUGINS="$(pwd)/action_plugins" +export ANSIBLE_ACTION_PLUGINS +export ANSIBLE_BECOME_PASS='iWishIWereCoolEnoughForRoot!' + +begin_sandwich() { + ansible-playbook setup_unpriv_users.yml -i inventory -v "$@" +} + +end_sandwich() { + unset ANSIBLE_KEEP_REMOTE_FILES + unset ANSIBLE_COMMON_REMOTE_GROUP + unset ANSIBLE_BECOME_PASS + + # Do a few cleanup tasks (nuke users, groups, and homedirs, undo config changes) + ansible-playbook cleanup_unpriv_users.yml -i inventory -v "$@" + + # We do these last since they do things like remove groups and will error + # if there are still users in them. + for pb in */cleanup.yml; do + ansible-playbook "$pb" -i inventory -v "$@" + done +} + +trap "end_sandwich \"\$@\"" EXIT + +# Common group tests +# Skip on macOS, chmod fallback will take over. +# 1) chmod is stupidly hard to disable, so hitting this test case on macOS would +# be a suuuuuuper edge case scenario +# 2) even if we can trick it so chmod doesn't exist, then other things break. +# Ansible wants a `chmod` around, even if it's not the final thing that gets +# us enough permission to run the task. +if [[ "$OSTYPE" != darwin* ]]; then + begin_sandwich "$@" + ansible-playbook common_remote_group/setup.yml -i inventory -v "$@" + export ANSIBLE_COMMON_REMOTE_GROUP=commongroup + ansible-playbook common_remote_group/test.yml -i inventory -v "$@" + end_sandwich "$@" +fi + +if [[ "$OSTYPE" == darwin* ]]; then + begin_sandwich "$@" + # In the default case this should happen on macOS, so no need for a setup + # It should just work. + ansible-playbook chmod_acl_macos/test.yml -i inventory -v "$@" + end_sandwich "$@" +fi diff --git a/test/integration/targets/become_unprivileged/setup_unpriv_users.yml b/test/integration/targets/become_unprivileged/setup_unpriv_users.yml new file mode 100644 index 0000000..4f67741 --- /dev/null +++ b/test/integration/targets/become_unprivileged/setup_unpriv_users.yml @@ -0,0 +1,109 @@ +#################################################################### +# NOTE! Any destructive changes you make here... Undo them in +# cleanup_become_unprivileged so that they don't affect other tests. +#################################################################### +- name: Set up host and create unprivileged users + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + - name: Create groups for unprivileged users + group: + name: "{{ item }}" + with_items: + - unpriv1 + - unpriv2 + + # MacOS requires unencrypted password + - name: Set password for unpriv1 (MacOSX) + set_fact: + password: 'iWishIWereCoolEnoughForRoot!' + when: ansible_distribution == 'MacOSX' + + - name: Set password for unpriv1 (everything else) + set_fact: + password: $6$CRuKRUfAoVwibjUI$1IEOISMFAE/a0VG73K9QsD0uruXNPLNkZ6xWg4Sk3kZIXwv6.YJLECzfNjn6pu8ay6XlVcj2dUvycLetL5Lgx1 + when: ansible_distribution != 'MacOSX' + + # This user is special. It gets a password so we can sudo as it + # (we set the sudo password in runme.sh) and it gets wheel so it can + # `become` unpriv2 without an overly complex sudoers file. + - name: Create first unprivileged user + user: + name: unpriv1 + group: unpriv1 + password: "{{ password }}" + + - name: Create second unprivileged user + user: + name: unpriv2 + group: unpriv2 + + - name: Special case group add for macOS + user: + name: unpriv1 + groups: com.apple.access_ssh + append: yes + when: ansible_distribution == 'MacOSX' + + - name: Create .ssh for unpriv1 + file: + path: ~unpriv1/.ssh + state: directory + owner: unpriv1 + group: unpriv1 + mode: 0700 + + - name: Set authorized key for unpriv1 + copy: + src: ~root/.ssh/authorized_keys + dest: ~unpriv1/.ssh/authorized_keys + remote_src: yes + owner: unpriv1 + group: unpriv1 + mode: 0600 + + # Without this we get: + # "Failed to connect to the host via ssh: "System is booting up. Unprivileged + # users are not permitted to log in yet. Please come back later." + - name: Nuke /run/nologin + file: + path: /run/nologin + state: absent + + - name: Fix sudoers.d path for FreeBSD + set_fact: + sudoers_etc: /usr/local/etc + when: ansible_distribution == 'FreeBSD' + + - name: Fix sudoers.d path for everything else + set_fact: + sudoers_etc: /etc + when: sudoers_etc is not defined + + - name: Set chown group for bsd and osx + set_fact: + chowngroup: wheel + when: ansible_distribution in ('FreeBSD', 'MacOSX') + + - name: Chown group for everything else + set_fact: + chowngroup: root + when: chowngroup is not defined + + - name: Make it so unpriv1 can sudo (Chapter 1) + copy: + dest: "{{ sudoers_etc }}/sudoers.d/unpriv1" + content: unpriv1 ALL=(ALL) ALL + owner: root + group: "{{ chowngroup }}" + mode: 0644 + + # OpenSUSE has a weird sudo default here and requires the root pw + # instead of the user pw. Undo that setting, we can clean it up later. + - name: Make it so unpriv1 can sudo (Chapter 2 - The Return Of the OpenSUSE) + lineinfile: + dest: "{{ sudoers_etc }}/sudoers" + regexp: '^Defaults targetpw' + line: '### Defaults targetpw' + backrefs: yes |