summaryrefslogtreecommitdiff
path: root/lib/ansible/plugins/connection/psrp.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/plugins/connection/psrp.py')
-rw-r--r--lib/ansible/plugins/connection/psrp.py105
1 files changed, 59 insertions, 46 deletions
diff --git a/lib/ansible/plugins/connection/psrp.py b/lib/ansible/plugins/connection/psrp.py
index dfcf0e54..37a4694a 100644
--- a/lib/ansible/plugins/connection/psrp.py
+++ b/lib/ansible/plugins/connection/psrp.py
@@ -1,7 +1,7 @@
# Copyright (c) 2018 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
+from __future__ import (annotations, absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = """
@@ -10,7 +10,7 @@ name: psrp
short_description: Run tasks over Microsoft PowerShell Remoting Protocol
description:
- Run commands or put/fetch on a target via PSRP (WinRM plugin)
-- This is similar to the I(winrm) connection plugin which uses the same
+- This is similar to the P(ansible.builtin.winrm#connection) connection plugin which uses the same
underlying transport but instead runs in a PowerShell interpreter.
version_added: "2.7"
requirements:
@@ -38,7 +38,7 @@ options:
keyword:
- name: remote_user
remote_password:
- description: Authentication password for the C(remote_user). Can be supplied as CLI option.
+ description: Authentication password for the O(remote_user). Can be supplied as CLI option.
type: str
vars:
- name: ansible_password
@@ -49,8 +49,8 @@ options:
port:
description:
- The port for PSRP to connect on the remote target.
- - Default is C(5986) if I(protocol) is not defined or is C(https),
- otherwise the port is C(5985).
+ - Default is V(5986) if O(protocol) is not defined or is V(https),
+ otherwise the port is V(5985).
type: int
vars:
- name: ansible_port
@@ -60,7 +60,7 @@ options:
protocol:
description:
- Set the protocol to use for the connection.
- - Default is C(https) if I(port) is not defined or I(port) is not C(5985).
+ - Default is V(https) if O(port) is not defined or O(port) is not V(5985).
choices:
- http
- https
@@ -77,8 +77,8 @@ options:
auth:
description:
- The authentication protocol to use when authenticating the remote user.
- - The default, C(negotiate), will attempt to use C(Kerberos) if it is
- available and fall back to C(NTLM) if it isn't.
+ - The default, V(negotiate), will attempt to use Kerberos (V(kerberos)) if it is
+ available and fall back to NTLM (V(ntlm)) if it isn't.
type: str
vars:
- name: ansible_psrp_auth
@@ -93,8 +93,8 @@ options:
cert_validation:
description:
- Whether to validate the remote server's certificate or not.
- - Set to C(ignore) to not validate any certificates.
- - I(ca_cert) can be set to the path of a PEM certificate chain to
+ - Set to V(ignore) to not validate any certificates.
+ - O(ca_cert) can be set to the path of a PEM certificate chain to
use in the validation.
choices:
- validate
@@ -107,7 +107,7 @@ options:
description:
- The path to a PEM certificate chain to use when validating the server's
certificate.
- - This value is ignored if I(cert_validation) is set to C(ignore).
+ - This value is ignored if O(cert_validation) is set to V(ignore).
type: path
vars:
- name: ansible_psrp_cert_trust_path
@@ -124,7 +124,7 @@ options:
read_timeout:
description:
- The read timeout for receiving data from the remote host.
- - This value must always be greater than I(operation_timeout).
+ - This value must always be greater than O(operation_timeout).
- This option requires pypsrp >= 0.3.
- This is measured in seconds.
type: int
@@ -156,15 +156,15 @@ options:
message_encryption:
description:
- Controls the message encryption settings, this is different from TLS
- encryption when I(ansible_psrp_protocol) is C(https).
- - Only the auth protocols C(negotiate), C(kerberos), C(ntlm), and
- C(credssp) can do message encryption. The other authentication protocols
- only support encryption when C(protocol) is set to C(https).
- - C(auto) means means message encryption is only used when not using
+ encryption when O(protocol) is V(https).
+ - Only the auth protocols V(negotiate), V(kerberos), V(ntlm), and
+ V(credssp) can do message encryption. The other authentication protocols
+ only support encryption when V(protocol) is set to V(https).
+ - V(auto) means means message encryption is only used when not using
TLS/HTTPS.
- - C(always) is the same as C(auto) but message encryption is always used
+ - V(always) is the same as V(auto) but message encryption is always used
even when running over TLS/HTTPS.
- - C(never) disables any encryption checks that are in place when running
+ - V(never) disables any encryption checks that are in place when running
over HTTP and disables any authentication encryption processes.
type: str
vars:
@@ -184,11 +184,11 @@ options:
description:
- Will disable any environment proxy settings and connect directly to the
remote host.
- - This option is ignored if C(proxy) is set.
+ - This option is ignored if O(proxy) is set.
vars:
- name: ansible_psrp_ignore_proxy
type: bool
- default: 'no'
+ default: false
# auth options
certificate_key_pem:
@@ -206,7 +206,7 @@ options:
credssp_auth_mechanism:
description:
- The sub authentication mechanism to use with CredSSP auth.
- - When C(auto), both Kerberos and NTLM is attempted with kerberos being
+ - When V(auto), both Kerberos and NTLM is attempted with kerberos being
preferred.
type: str
choices:
@@ -219,16 +219,16 @@ options:
credssp_disable_tlsv1_2:
description:
- Disables the use of TLSv1.2 on the CredSSP authentication channel.
- - This should not be set to C(yes) unless dealing with a host that does not
+ - This should not be set to V(yes) unless dealing with a host that does not
have TLSv1.2.
- default: no
+ default: false
type: bool
vars:
- name: ansible_psrp_credssp_disable_tlsv1_2
credssp_minimum_version:
description:
- The minimum CredSSP server authentication version that will be accepted.
- - Set to C(5) to ensure the server has been patched and is not vulnerable
+ - Set to V(5) to ensure the server has been patched and is not vulnerable
to CVE 2018-0886.
default: 2
type: int
@@ -262,7 +262,7 @@ options:
- CBT is used to provide extra protection against Man in the Middle C(MitM)
attacks by binding the outer transport channel to the auth channel.
- CBT is not used when using just C(HTTP), only C(HTTPS).
- default: yes
+ default: true
type: bool
vars:
- name: ansible_psrp_negotiate_send_cbt
@@ -282,7 +282,7 @@ options:
description:
- Sets the WSMan timeout for each operation.
- This is measured in seconds.
- - This should not exceed the value for C(connection_timeout).
+ - This should not exceed the value for O(connection_timeout).
type: int
vars:
- name: ansible_psrp_operation_timeout
@@ -309,13 +309,15 @@ import base64
import json
import logging
import os
+import typing as t
from ansible import constants as C
from ansible.errors import AnsibleConnectionFailure, AnsibleError
from ansible.errors import AnsibleFileNotFound
from ansible.module_utils.parsing.convert_bool import boolean
-from ansible.module_utils._text import to_bytes, to_native, to_text
+from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.plugins.connection import ConnectionBase
+from ansible.plugins.shell.powershell import ShellModule as PowerShellPlugin
from ansible.plugins.shell.powershell import _common_args
from ansible.utils.display import Display
from ansible.utils.hashing import sha1
@@ -345,13 +347,16 @@ class Connection(ConnectionBase):
has_pipelining = True
allow_extras = True
- def __init__(self, *args, **kwargs):
+ # Satifies mypy as this connection only ever runs with this plugin
+ _shell: PowerShellPlugin
+
+ def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
self.always_pipeline_modules = True
self.has_native_async = True
- self.runspace = None
- self.host = None
- self._last_pipeline = False
+ self.runspace: RunspacePool | None = None
+ self.host: PSHost | None = None
+ self._last_pipeline: PowerShell | None = None
self._shell_type = 'powershell'
super(Connection, self).__init__(*args, **kwargs)
@@ -361,7 +366,7 @@ class Connection(ConnectionBase):
logging.getLogger('requests_credssp').setLevel(logging.INFO)
logging.getLogger('urllib3').setLevel(logging.INFO)
- def _connect(self):
+ def _connect(self) -> Connection:
if not HAS_PYPSRP:
raise AnsibleError("pypsrp or dependencies are not installed: %s"
% to_native(PYPSRP_IMP_ERR))
@@ -408,7 +413,7 @@ class Connection(ConnectionBase):
self._last_pipeline = None
return self
- def reset(self):
+ def reset(self) -> None:
if not self._connected:
self.runspace = None
return
@@ -424,26 +429,27 @@ class Connection(ConnectionBase):
self.runspace = None
self._connect()
- def exec_command(self, cmd, in_data=None, sudoable=True):
+ def exec_command(self, cmd: str, in_data: bytes | None = None, sudoable: bool = True) -> tuple[int, bytes, bytes]:
super(Connection, self).exec_command(cmd, in_data=in_data,
sudoable=sudoable)
+ pwsh_in_data: bytes | str | None = None
+
if cmd.startswith(" ".join(_common_args) + " -EncodedCommand"):
# This is a PowerShell script encoded by the shell plugin, we will
# decode the script and execute it in the runspace instead of
# starting a new interpreter to save on time
b_command = base64.b64decode(cmd.split(" ")[-1])
script = to_text(b_command, 'utf-16-le')
- in_data = to_text(in_data, errors="surrogate_or_strict", nonstring="passthru")
+ pwsh_in_data = to_text(in_data, errors="surrogate_or_strict", nonstring="passthru")
- if in_data and in_data.startswith(u"#!"):
+ if pwsh_in_data and isinstance(pwsh_in_data, str) and pwsh_in_data.startswith("#!"):
# ANSIBALLZ wrapper, we need to get the interpreter and execute
# that as the script - note this won't work as basic.py relies
# on packages not available on Windows, once fixed we can enable
# this path
- interpreter = to_native(in_data.splitlines()[0][2:])
+ interpreter = to_native(pwsh_in_data.splitlines()[0][2:])
# script = "$input | &'%s' -" % interpreter
- # in_data = to_text(in_data)
raise AnsibleError("cannot run the interpreter '%s' on the psrp "
"connection plugin" % interpreter)
@@ -458,12 +464,13 @@ class Connection(ConnectionBase):
# In other cases we want to execute the cmd as the script. We add on the 'exit $LASTEXITCODE' to ensure the
# rc is propagated back to the connection plugin.
script = to_text(u"%s\nexit $LASTEXITCODE" % cmd)
+ pwsh_in_data = in_data
display.vvv(u"PSRP: EXEC %s" % script, host=self._psrp_host)
- rc, stdout, stderr = self._exec_psrp_script(script, in_data)
+ rc, stdout, stderr = self._exec_psrp_script(script, pwsh_in_data)
return rc, stdout, stderr
- def put_file(self, in_path, out_path):
+ def put_file(self, in_path: str, out_path: str) -> None:
super(Connection, self).put_file(in_path, out_path)
out_path = self._shell._unquote(out_path)
@@ -611,7 +618,7 @@ end {
raise AnsibleError("Remote sha1 hash %s does not match local hash %s"
% (to_native(remote_sha1), to_native(local_sha1)))
- def fetch_file(self, in_path, out_path):
+ def fetch_file(self, in_path: str, out_path: str) -> None:
super(Connection, self).fetch_file(in_path, out_path)
display.vvv("FETCH %s TO %s" % (in_path, out_path),
host=self._psrp_host)
@@ -689,7 +696,7 @@ if ($bytes_read -gt 0) {
display.warning("failed to close remote file stream of file "
"'%s': %s" % (in_path, to_native(stderr)))
- def close(self):
+ def close(self) -> None:
if self.runspace and self.runspace.state == RunspacePoolState.OPENED:
display.vvvvv("PSRP CLOSE RUNSPACE: %s" % (self.runspace.id),
host=self._psrp_host)
@@ -698,7 +705,7 @@ if ($bytes_read -gt 0) {
self._connected = False
self._last_pipeline = None
- def _build_kwargs(self):
+ def _build_kwargs(self) -> None:
self._psrp_host = self.get_option('remote_addr')
self._psrp_user = self.get_option('remote_user')
self._psrp_pass = self.get_option('remote_password')
@@ -802,7 +809,13 @@ if ($bytes_read -gt 0) {
option = self.get_option('_extras')['ansible_psrp_%s' % arg]
self._psrp_conn_kwargs[arg] = option
- def _exec_psrp_script(self, script, input_data=None, use_local_scope=True, arguments=None):
+ def _exec_psrp_script(
+ self,
+ script: str,
+ input_data: bytes | str | t.Iterable | None = None,
+ use_local_scope: bool = True,
+ arguments: t.Iterable[str] | None = None,
+ ) -> tuple[int, bytes, bytes]:
# Check if there's a command on the current pipeline that still needs to be closed.
if self._last_pipeline:
# Current pypsrp versions raise an exception if the current state was not RUNNING. We manually set it so we
@@ -828,7 +841,7 @@ if ($bytes_read -gt 0) {
return rc, stdout, stderr
- def _parse_pipeline_result(self, pipeline):
+ def _parse_pipeline_result(self, pipeline: PowerShell) -> tuple[int, bytes, bytes]:
"""
PSRP doesn't have the same concept as other protocols with its output.
We need some extra logic to convert the pipeline streams and host