From e96552396e4244f4755a1e0072f582b101ea5f56 Mon Sep 17 00:00:00 2001 From: Gavin Will Date: Sat, 25 Mar 2023 22:16:46 +0000 Subject: [PATCH] ssh_config: add proxyjump option (#6205) * feat(ssh_config): proxyjump option * feat(ssh_config: add proxyjump test * CamelCase ProxyJump * add changelog fragment * Update plugins/modules/ssh_config.py add version_added Co-authored-by: Felix Fontein * update task name to include new proxyjump option * adding tests for proxyjump option * fixing assert variable name --------- Co-authored-by: Felix Fontein --- ...970-add-proxyjump-option-to-ssh-config.yml | 2 + plugins/modules/ssh_config.py | 10 + .../targets/ssh_config/tasks/main.yml | 21 +- .../targets/ssh_config/tasks/options.yml | 210 ++++++++++++++++++ 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/5970-add-proxyjump-option-to-ssh-config.yml diff --git a/changelogs/fragments/5970-add-proxyjump-option-to-ssh-config.yml b/changelogs/fragments/5970-add-proxyjump-option-to-ssh-config.yml new file mode 100644 index 0000000000..a8594ed69f --- /dev/null +++ b/changelogs/fragments/5970-add-proxyjump-option-to-ssh-config.yml @@ -0,0 +1,2 @@ +minor_changes: + - ssh_config - add ``proxyjump`` option (https://github.com/ansible-collections/community.general/pull/5970). diff --git a/plugins/modules/ssh_config.py b/plugins/modules/ssh_config.py index 676cc84a93..672ac8c470 100644 --- a/plugins/modules/ssh_config.py +++ b/plugins/modules/ssh_config.py @@ -84,7 +84,14 @@ options: proxycommand: description: - Sets the C(ProxyCommand) option. + - Mutually exclusive with I(proxyjump). type: str + proxyjump: + description: + - Sets the C(ProxyJump) option. + - Mutually exclusive with I(proxycommand). + type: str + version_added: 6.5.0 forward_agent: description: - Sets the C(ForwardAgent) option. @@ -210,6 +217,7 @@ class SSHConfig(object): strict_host_key_checking=self.params.get('strict_host_key_checking'), user_known_hosts_file=self.params.get('user_known_hosts_file'), proxycommand=self.params.get('proxycommand'), + proxyjump=self.params.get('proxyjump'), host_key_algorithms=self.params.get('host_key_algorithms'), ) @@ -306,6 +314,7 @@ def main(): identity_file=dict(type='path'), port=dict(type='str'), proxycommand=dict(type='str', default=None), + proxyjump=dict(type='str', default=None), forward_agent=dict(type='bool'), remote_user=dict(type='str'), ssh_config_file=dict(default=None, type='path'), @@ -320,6 +329,7 @@ def main(): supports_check_mode=True, mutually_exclusive=[ ['user', 'ssh_config_file'], + ['proxycommand', 'proxyjump'], ], ) diff --git a/tests/integration/targets/ssh_config/tasks/main.yml b/tests/integration/targets/ssh_config/tasks/main.yml index c1bcbc372e..290639e83e 100644 --- a/tests/integration/targets/ssh_config/tasks/main.yml +++ b/tests/integration/targets/ssh_config/tasks/main.yml @@ -186,6 +186,25 @@ - not mut_ex.changed - "'parameters are mutually exclusive' in mut_ex.msg" +- name: Check if proxycommand and proxyjump are mutually exclusive + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + proxycommand: "ssh jumphost.example.com -W %h:%p" + proxyjump: "jumphost.example.com" + identity_file: '{{ ssh_private_key }}' + port: '2224' + state: present + register: proxy_mut_ex + ignore_errors: true + +- name: Check mutual exclusive test - proxycommand and proxyjump + assert: + that: + - not proxy_mut_ex.changed + - "'parameters are mutually exclusive' in proxy_mut_ex.msg" + - name: Add a full name host community.general.ssh_config: ssh_config_file: "{{ ssh_config_test }}" @@ -222,5 +241,5 @@ - short_name.hosts_changed == [] - short_name.hosts_removed == [] -- name: Include integration tests for additional options (e.g. proxycommand) +- name: Include integration tests for additional options (e.g. proxycommand, proxyjump) include: 'options.yml' diff --git a/tests/integration/targets/ssh_config/tasks/options.yml b/tests/integration/targets/ssh_config/tasks/options.yml index 9b617ad356..406de6831d 100644 --- a/tests/integration/targets/ssh_config/tasks/options.yml +++ b/tests/integration/targets/ssh_config/tasks/options.yml @@ -210,3 +210,213 @@ - "'proxycommand ssh new-jumphost.example.com -W %h:%p' not in slurp_ssh_config['content'] | b64decode" - "'forwardagent no' not in slurp_ssh_config['content'] | b64decode" - "'hostkeyalgorithms +ssh-ed25519' not in slurp_ssh_config['content'] | b64decode" + +# Proxycommand and ProxyJump are mutually exclusive. +# Reset ssh_config before testing options with proxyjump + +- name: Copy sample config file + copy: + src: 'files/ssh_config_test' + dest: '{{ ssh_config_test }}' + +- name: Options - Add in check mode + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "jumphost.example.com" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add + check_mode: true + +- name: Options - Check if changes are made in check mode + assert: + that: + - options_add.changed + - "'options.example.com' in options_add.hosts_added" + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Options - Verify that nothign was added to {{ ssh_config_test }} during change mode" + assert: + that: + - "'options.example.com' not in slurp_ssh_config['content'] | b64decode" + +- name: Options - Add a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "jumphost.example.com" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add + +- name: Options - Check if changes are made + assert: + that: + - options_add.changed + - "'options.example.com' in options_add.hosts_added" + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + +- name: Options - Add same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "jumphost.example.com" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add_again + +- name: Options - Check for idempotency + assert: + that: + - not options_add_again.changed + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + - options_add.hosts_added is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} contains added options" + assert: + that: + - "'proxyjump jumphost.example.com' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent yes' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-rsa' in slurp_ssh_config['content'] | b64decode" + +- name: Options - Update host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "new-jumphost.example.com" + forward_agent: false + host_key_algorithms: "+ssh-ed25519" + state: present + register: options_update + +- name: Options - Check for update operation + assert: + that: + - options_update.changed + - options_update.hosts_changed is defined + - "'options.example.com' in options_update.hosts_changed" + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: Options - Update host again + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "new-jumphost.example.com" + forward_agent: false + host_key_algorithms: "+ssh-ed25519" + state: present + register: options_update + +- name: Options - Check update operation for idempotency + assert: + that: + - not options_update.changed + - options_update.hosts_changed is defined + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} contains changed options" + assert: + that: + - "'proxyjump new-jumphost.example.com' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode" + +- name: Options - Ensure no update in case option exist in ssh_config file but wasn't defined in playbook + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: present + register: options_no_update + +- name: Options - Check that no update took place + assert: + that: + - not options_update.changed + - options_update.hosts_changed is defined + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} wasn't changed" + assert: + that: + - "'proxyjump new-jumphost.example.com' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode" + +- name: Debug + debug: + msg: "{{ slurp_ssh_config['content'] | b64decode }}" + +- name: Options - Delete a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: absent + register: options_delete + +- name: Options - Check if host was removed + assert: + that: + - options_delete.changed + - "'options.example.com' in options_delete.hosts_removed" + - options_delete.hosts_changed is defined + - options_delete.hosts_added is defined + +- name: Options - Delete same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: absent + register: options_delete_again + +- name: Options - Check delete operation for idempotency + assert: + that: + - not options_delete_again.changed + - options_delete_again.hosts_changed is defined + - options_delete_again.hosts_removed is defined + - options_delete_again.hosts_added is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} does not contains deleted options" + assert: + that: + - "'proxyjump new-jumphost.example.com' not in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' not in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' not in slurp_ssh_config['content'] | b64decode"