From 4f0c49a967ed5549d80acbc3990e23db5c750b51 Mon Sep 17 00:00:00 2001 From: Alex Willmer Date: Tue, 5 Nov 2024 11:05:25 +0000 Subject: [PATCH] ansible_mitogen: Templated SSH private key file --- .ci/ansible_tests.py | 25 +++++++++-------- .ci/ci_lib.py | 27 ++++++++----------- .ci/debops_common_tests.py | 6 +++-- .ci/localhost_ansible_tests.py | 12 +++------ ansible_mitogen/transport_config.py | 2 +- docs/changelog.rst | 2 ++ tests/ansible/hosts/default.hosts | 1 + .../integration/ssh/templated_by_inv.yml | 7 +++++ .../ssh/templated_by_play_taskvar.yml | 16 ++++++++++- tests/ansible/integration/ssh/variables.yml | 8 +++--- tests/ansible/templates/test-targets.j2 | 1 + 11 files changed, 64 insertions(+), 43 deletions(-) diff --git a/.ci/ansible_tests.py b/.ci/ansible_tests.py index 62dfa8f5b..4155f24ec 100755 --- a/.ci/ansible_tests.py +++ b/.ci/ansible_tests.py @@ -12,9 +12,8 @@ import ci_lib -TEMPLATES_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible/templates') -TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible') -HOSTS_DIR = os.path.join(ci_lib.TMP, 'hosts') +TMP = ci_lib.TempDir(prefix='mitogen_ci_ansible') +TMP_HOSTS_DIR = os.path.join(TMP.path, 'hosts') def pause_if_interactive(): @@ -40,13 +39,14 @@ def pause_if_interactive(): with ci_lib.Fold('job_setup'): - os.chdir(TESTS_DIR) - os.chmod('../data/docker/mitogen__has_sudo_pubkey.key', int('0600', 7)) + os.chdir(ci_lib.ANSIBLE_TESTS_DIR) + os.chmod(ci_lib.TESTS_SSH_PRIVATE_KEY_FILE, int('0600', 7)) - ci_lib.run("mkdir %s", HOSTS_DIR) - for path in glob.glob(TESTS_DIR + '/hosts/*'): + os.mkdir(TMP_HOSTS_DIR) + for path in glob.glob(os.path.join(ci_lib.ANSIBLE_TESTS_HOSTS_DIR, '*')): if not path.endswith('default.hosts'): - ci_lib.run("ln -s %s %s", path, HOSTS_DIR) + link_name = os.path.join(TMP_HOSTS_DIR, path) + os.symlink(path, link_name) distros = collections.defaultdict(list) families = collections.defaultdict(list) @@ -55,12 +55,14 @@ def pause_if_interactive(): families[container['family']].append(container['name']) jinja_env = jinja2.Environment( - loader=jinja2.FileSystemLoader(searchpath=TEMPLATES_DIR), + loader=jinja2.FileSystemLoader( + searchpath=ci_lib.ANSIBLE_TESTS_TEMPLATES_DIR, + ), lstrip_blocks=True, # Remove spaces and tabs from before a block trim_blocks=True, # Remove first newline after a block ) inventory_template = jinja_env.get_template('test-targets.j2') - inventory_path = os.path.join(HOSTS_DIR, 'target') + inventory_path = os.path.join(TMP_HOSTS_DIR, 'test-targets.ini') with open(inventory_path, 'w') as fp: fp.write(inventory_template.render( @@ -80,7 +82,8 @@ def pause_if_interactive(): playbook = os.environ.get('PLAYBOOK', 'all.yml') try: ci_lib.run('./run_ansible_playbook.py %s -i "%s" %s', - playbook, HOSTS_DIR, ' '.join(sys.argv[1:])) + playbook, TMP_HOSTS_DIR, ' '.join(sys.argv[1:]), + ) except: pause_if_interactive() raise diff --git a/.ci/ci_lib.py b/.ci/ci_lib.py index afb62e023..1cb7df095 100644 --- a/.ci/ci_lib.py +++ b/.ci/ci_lib.py @@ -27,16 +27,25 @@ ) ) +GIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + +ANSIBLE_TESTS_DIR = os.path.join(GIT_ROOT, 'tests/ansible') +ANSIBLE_TESTS_HOSTS_DIR = os.path.join(GIT_ROOT, 'tests/ansible/hosts') +ANSIBLE_TESTS_TEMPLATES_DIR = os.path.join(GIT_ROOT, 'tests/ansible/templates') DISTRO_SPECS = os.environ.get( 'MITOGEN_TEST_DISTRO_SPECS', 'centos6 centos8 debian9 debian11 ubuntu1604 ubuntu2004', ) + +IMAGE_PREP_DIR = os.path.join(GIT_ROOT, 'tests/image_prep') IMAGE_TEMPLATE = os.environ.get( 'MITOGEN_TEST_IMAGE_TEMPLATE', 'public.ecr.aws/n5z0e8q9/%(distro)s-test', ) +TESTS_SSH_PRIVATE_KEY_FILE = os.path.join(GIT_ROOT, 'tests/data/docker/mitogen__has_sudo_pubkey.key') + _print = print def print(*args, **kwargs): @@ -185,8 +194,8 @@ def exists_in_path(progname): class TempDir(object): - def __init__(self): - self.path = tempfile.mkdtemp(prefix='mitogen_ci_lib') + def __init__(self, prefix='mitogen_ci_lib'): + self.path = tempfile.mkdtemp(prefix=prefix) atexit.register(self.destroy) def destroy(self, rmtree=shutil.rmtree): @@ -199,20 +208,6 @@ def __enter__(self): pass def __exit__(self, _1, _2, _3): pass -GIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -TMP = TempDir().path - - -# We copy this out of the way to avoid random stuff modifying perms in the Git -# tree (like git pull). -src_key_file = os.path.join(GIT_ROOT, - 'tests/data/docker/mitogen__has_sudo_pubkey.key') -key_file = os.path.join(TMP, - 'mitogen__has_sudo_pubkey.key') -shutil.copyfile(src_key_file, key_file) -os.chmod(key_file, int('0600', 8)) - - os.environ['PYTHONDONTWRITEBYTECODE'] = 'x' os.environ['PYTHONPATH'] = '%s:%s' % ( os.environ.get('PYTHONPATH', ''), diff --git a/.ci/debops_common_tests.py b/.ci/debops_common_tests.py index b065486f8..2ae605251 100755 --- a/.ci/debops_common_tests.py +++ b/.ci/debops_common_tests.py @@ -6,7 +6,8 @@ import ci_lib -project_dir = os.path.join(ci_lib.TMP, 'project') +TMP = ci_lib.TempDir(prefix='mitogen_ci_debops') +project_dir = os.path.join(TMP.path, 'project') vars_path = 'ansible/inventory/group_vars/debops_all_hosts.yml' inventory_path = 'ansible/inventory/hosts' docker_hostname = ci_lib.get_docker_hostname() @@ -24,6 +25,7 @@ with ci_lib.Fold('job_setup'): ci_lib.run('debops-init %s', project_dir) os.chdir(project_dir) + os.chmod(ci_lib.TESTS_SSH_PRIVATE_KEY_FILE, int('0600', 7)) ansible_strategy_plugin = "{}/ansible_mitogen/plugins/strategy".format(ci_lib.GIT_ROOT) @@ -45,7 +47,7 @@ "\n" # Speed up slow DH generation. "dhparam__bits: ['128', '64']\n" - % (ci_lib.key_file,) + % (ci_lib.TESTS_SSH_PRIVATE_KEY_FILE,) ) with open(inventory_path, 'a') as fp: diff --git a/.ci/localhost_ansible_tests.py b/.ci/localhost_ansible_tests.py index 502a9abca..031b17629 100755 --- a/.ci/localhost_ansible_tests.py +++ b/.ci/localhost_ansible_tests.py @@ -10,19 +10,13 @@ import ci_lib -TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible') -IMAGE_PREP_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/image_prep') -HOSTS_DIR = os.path.join(TESTS_DIR, 'hosts') -KEY_PATH = os.path.join(TESTS_DIR, '../data/docker/mitogen__has_sudo_pubkey.key') - - with ci_lib.Fold('unit_tests'): os.environ['SKIP_MITOGEN'] = '1' ci_lib.run('./run_tests -v') with ci_lib.Fold('job_setup'): - os.chmod(KEY_PATH, int('0600', 8)) + os.chmod(ci_lib.TESTS_SSH_PRIVATE_KEY_FILE, int('0600', 8)) # NOTE: sshpass v1.06 causes errors so pegging to 1.05 -> "msg": "Error when changing password","out": "passwd: DS error: eDSAuthFailed\n", # there's a checksum error with "brew install http://git.io/sshpass.rb" though, so installing manually if not ci_lib.exists_in_path('sshpass'): @@ -52,7 +46,7 @@ subprocess.check_call('sudo chmod 600 ~root/.ssh/authorized_keys', shell=True) if os.path.expanduser('~mitogen__user1') == '~mitogen__user1': - os.chdir(IMAGE_PREP_DIR) + os.chdir(ci_lib.IMAGE_PREP_DIR) ci_lib.run("ansible-playbook -c local -i localhost, _user_accounts.yml") cmd = ';'.join([ @@ -77,7 +71,7 @@ with ci_lib.Fold('ansible'): - os.chdir(TESTS_DIR) + os.chdir(ci_lib.ANSIBLE_TESTS_DIR) playbook = os.environ.get('PLAYBOOK', 'all.yml') ci_lib.run('./run_ansible_playbook.py %s %s', playbook, ' '.join(sys.argv[1:])) diff --git a/ansible_mitogen/transport_config.py b/ansible_mitogen/transport_config.py index 97f1b2f09..294e89140 100644 --- a/ansible_mitogen/transport_config.py +++ b/ansible_mitogen/transport_config.py @@ -508,7 +508,7 @@ def candidates(): return boolean(val) def private_key_file(self): - return self._play_context.private_key_file + return self._connection_option('private_key_file') def ssh_executable(self): return self._connection_option('ssh_executable') diff --git a/docs/changelog.rst b/docs/changelog.rst index d579544f3..a791d02c1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -21,6 +21,8 @@ To avail of fixes in an unreleased version, please download a ZIP file In progress (unreleased) ------------------------ +* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated SSH private key file + (e.g. ``ansible_private_key_file``). v0.3.16 (2024-11-05) diff --git a/tests/ansible/hosts/default.hosts b/tests/ansible/hosts/default.hosts index eb04cf904..0421f9129 100644 --- a/tests/ansible/hosts/default.hosts +++ b/tests/ansible/hosts/default.hosts @@ -45,6 +45,7 @@ ansible_user="{{ lookup('pipe', 'whoami') }}" [tt_targets_inventory] tt-password ansible_password="{{ 'has_sudo_nopw_password' | trim }}" ansible_user=mitogen__has_sudo_nopw tt-port ansible_password=has_sudo_nopw_password ansible_port="{{ 22 | int }}" ansible_user=mitogen__has_sudo_nopw +tt-private-key-file ansible_private_key_file="{{ '../../../data/docker/mitogen__has_sudo_pubkey.key' | trim }}" ansible_user=mitogen__has_sudo_pubkey tt-remote-user ansible_password=has_sudo_nopw_password ansible_user="{{ 'mitogen__has_sudo_nopw' | trim }}" tt-ssh-executable ansible_password=has_sudo_nopw_password ansible_ssh_executable="{{ 'ssh' | trim }}" ansible_user=mitogen__has_sudo_nopw diff --git a/tests/ansible/integration/ssh/templated_by_inv.yml b/tests/ansible/integration/ssh/templated_by_inv.yml index 686518fd4..f3d3f8ea3 100644 --- a/tests/ansible/integration/ssh/templated_by_inv.yml +++ b/tests/ansible/integration/ssh/templated_by_inv.yml @@ -2,6 +2,13 @@ hosts: tt_targets_inventory gather_facts: false tasks: + - name: Setup ansible_ssh_private_key_file + delegate_to: localhost + run_once: true + file: + path: ../../../data/docker/mitogen__has_sudo_pubkey.key + mode: u=rw,go= + - meta: reset_connection - name: Templated variables in inventory ping: diff --git a/tests/ansible/integration/ssh/templated_by_play_taskvar.yml b/tests/ansible/integration/ssh/templated_by_play_taskvar.yml index 0662adcd5..dcf0a2273 100644 --- a/tests/ansible/integration/ssh/templated_by_play_taskvar.yml +++ b/tests/ansible/integration/ssh/templated_by_play_taskvar.yml @@ -9,5 +9,19 @@ tasks: - meta: reset_connection - - name: Templated variables in play + - name: Templated variables in play, password authentication + ping: + +- name: integration/ssh/templated_by_play_taskvar.yml + hosts: tt_targets_bare + gather_facts: false + vars: + ansible_private_key_file: "{{ '../../../data/docker/mitogen__has_sudo_pubkey.key' | trim }}" + ansible_port: "{{ hostvars[groups['test-targets'][0]].ansible_port | default(22) }}" + ansible_ssh_executable: "{{ 'ssh' | trim }}" + ansible_user: "{{ 'mitogen__has_sudo_pubkey' | trim }}" + + tasks: + - meta: reset_connection + - name: Templated variables in play, key authentication ping: diff --git a/tests/ansible/integration/ssh/variables.yml b/tests/ansible/integration/ssh/variables.yml index 541b29f9c..a557d5d94 100644 --- a/tests/ansible/integration/ssh/variables.yml +++ b/tests/ansible/integration/ssh/variables.yml @@ -14,9 +14,9 @@ tasks: - name: setup ansible_ssh_private_key_file - shell: chmod 0600 ../data/docker/mitogen__has_sudo_pubkey.key - args: - chdir: ../.. + file: + path: ../../../data/docker/mitogen__has_sudo_pubkey.key + mode: u=rw,go= - name: ansible_user, ansible_ssh_private_key_file shell: > @@ -34,6 +34,7 @@ args: chdir: ../.. register: out + changed_when: false - name: ansible_user, wrong ansible_ssh_private_key_file shell: > @@ -52,6 +53,7 @@ args: chdir: ../.. register: out + changed_when: false ignore_errors: true - assert: diff --git a/tests/ansible/templates/test-targets.j2 b/tests/ansible/templates/test-targets.j2 index 279497586..81d568f23 100644 --- a/tests/ansible/templates/test-targets.j2 +++ b/tests/ansible/templates/test-targets.j2 @@ -73,6 +73,7 @@ ansible_user=mitogen__has_sudo_nopw [tt_targets_inventory] tt-password ansible_password="{{ '{{' }} 'has_sudo_nopw_password' | trim {{ '}}' }}" ansible_port={{ tt.port }} ansible_user=mitogen__has_sudo_nopw tt-port ansible_password=has_sudo_nopw_password ansible_port="{{ '{{' }} {{ tt.port }} | int {{ '}}' }}" ansible_user=mitogen__has_sudo_nopw +tt-private-key-file ansible_port={{ tt.port }} ansible_private_key_file="{{ '{{' }} '../../../data/docker/mitogen__has_sudo_pubkey.key' | trim {{ '}}' }}" ansible_user=mitogen__has_sudo_pubkey tt-remote-user ansible_password=has_sudo_nopw_password ansible_port={{ tt.port }} ansible_user="{{ '{{' }} 'mitogen__has_sudo_nopw' | trim {{ '}}' }}" tt-ssh-executable ansible_password=has_sudo_nopw_password ansible_port={{ tt.port }} ansible_ssh_executable="{{ '{{' }} 'ssh' | trim {{ '}}' }}" ansible_user=mitogen__has_sudo_nopw