diff --git a/changelogs/fragments/32829-ssh-connection-module-add-pkcs11-support.yml b/changelogs/fragments/32829-ssh-connection-module-add-pkcs11-support.yml new file mode 100644 index 00000000000..150e61f6f59 --- /dev/null +++ b/changelogs/fragments/32829-ssh-connection-module-add-pkcs11-support.yml @@ -0,0 +1,3 @@ +minor_changes: + - ssh - added pkcs11 support by adding the pkcs11_provider option in the + ssh connection module. (https://www.github.com/ansible/ansible/pull/32829) diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py index 7e92a778d28..fa6e0bf83cf 100644 --- a/lib/ansible/plugins/connection/ssh.py +++ b/lib/ansible/plugins/connection/ssh.py @@ -56,7 +56,9 @@ DOCUMENTATION = ''' - name: ansible_ssh_pass - name: ansible_ssh_password sshpass_prompt: - description: Password prompt that sshpass should search for. Supported by sshpass 1.06 and up. + description: + - Password prompt that sshpass should search for. Supported by sshpass 1.06 and up. + - Defaults to ``Enter PIN for`` when pkcs11_provider is set. default: '' ini: - section: 'ssh_connection' @@ -322,6 +324,17 @@ DOCUMENTATION = ''' cli: - name: timeout type: integer + pkcs11_provider: + version_added: '2.12' + default: "" + description: + - "PKCS11 SmartCard provider such as opensc, example: /usr/local/lib/opensc-pkcs11.so" + - Requires sshpass version 1.06+, sshpass must support the -P option + env: [{name: ANSIBLE_PKCS11_PROVIDER}] + ini: + - {key: pkcs11_provider, section: ssh_connection} + vars: + - name: ansible_ssh_pkcs11_provider ''' import errno @@ -622,15 +635,21 @@ class Connection(ConnectionBase): # If we want to use password authentication, we have to set up a pipe to # write the password to sshpass. - - if conn_password: + pkcs11_provider = self.get_option("pkcs11_provider") + if conn_password or pkcs11_provider: if not self._sshpass_available(): - raise AnsibleError("to use the 'ssh' connection type with passwords, you must install the sshpass program") + raise AnsibleError("to use the 'ssh' connection type with passwords or pkcs11_provider, you must install the sshpass program") + if not conn_password and pkcs11_provider: + raise AnsibleError("to use pkcs11_provider you must specify a password/pin") self.sshpass_pipe = os.pipe() b_command += [b'sshpass', b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict')] password_prompt = self.get_option('sshpass_prompt') + if not password_prompt and pkcs11_provider: + # Set default password prompt for pkcs11_provider to make it clear its a PIN + password_prompt = 'Enter PIN for ' + if password_prompt: b_command += [b'-P', to_bytes(password_prompt, errors='surrogate_or_strict')] @@ -640,6 +659,15 @@ class Connection(ConnectionBase): # Next, additional arguments based on the configuration. # + # pkcs11 mode allows the use of Smartcards or Yubikey devices + if conn_password and pkcs11_provider: + self._add_args(b_command, + (b"-o", b"KbdInteractiveAuthentication=no", + b"-o", b"PreferredAuthentications=publickey", + b"-o", b"PasswordAuthentication=no", + b'-o', to_bytes(u'PKCS11Provider=%s' % pkcs11_provider)), + u'Enable pkcs11') + # sftp batch mode allows us to correctly catch failed transfers, but can # be disabled if the client side doesn't support the option. However, # sftp batch mode does not prompt for passwords so it must be disabled diff --git a/test/units/plugins/connection/test_ssh.py b/test/units/plugins/connection/test_ssh.py index 74f5157d710..7c7afa049b6 100644 --- a/test/units/plugins/connection/test_ssh.py +++ b/test/units/plugins/connection/test_ssh.py @@ -80,6 +80,8 @@ class TestConnectionBaseClass(unittest.TestCase): pc = PlayContext() new_stdin = StringIO() conn = connection_loader.get('ssh', pc, new_stdin) + conn.get_option = MagicMock() + conn.get_option.return_value = "" conn._build_command('ssh', 'ssh') def test_plugins_connection_ssh_exec_command(self):