From 00f080b7bcc894406d2680fc4a0b9caeeebdcea3 Mon Sep 17 00:00:00 2001 From: mpieters3 Date: Mon, 21 Mar 2022 23:56:46 -0700 Subject: [PATCH 1/2] Updates to support Ansible 2.12 based on https://github.com/charltonstanley suggestion --- .devcontainer/Dockerfile | 60 -------------- .devcontainer/devcontainer.json | 36 -------- README.md | 31 ++++--- plugins/connection/eci.py | 140 ++++++++++++++++++++++---------- requirements.txt | 23 +----- test/demo.yml | 41 ++++++---- 6 files changed, 142 insertions(+), 189 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 2051bdb..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,60 +0,0 @@ -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- - -FROM python:3 - -# Avoid warnings by switching to noninteractive -ENV DEBIAN_FRONTEND=noninteractive - -# This Dockerfile adds a non-root 'vscode' user with sudo access. However, for Linux, -# this user's GID/UID must match your local user UID/GID to avoid permission issues -# with bind mounts. Update USER_UID / USER_GID if yours is not 1000. See -# https://aka.ms/vscode-remote/containers/non-root-user for details. -ARG USERNAME=vscode -ARG USER_UID=1000 -ARG USER_GID=$USER_UID - -# Uncomment the following COPY line and the corresponding lines in the `RUN` command if you wish to -# include your requirements in the image itself. It is suggested that you only do this if your -# requirements rarely (if ever) change. -# COPY requirements.txt /tmp/pip-tmp/ -# Configure apt and install packages -RUN apt-get update \ - && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \ - # - # Verify git, process tools, lsb-release (common in install instructions for CLIs) installed - && apt-get -y install git iproute2 procps lsb-release \ - # Install Ansible requirements for ansible development - && apt-get -y install software-properties-common \ - && apt-get -y install ansible \ - # - # Install pylint - && pip --disable-pip-version-check --no-cache-dir install pylint \ - && pip --disable-pip-version-check --no-cache-dir install boto \ - && pip --disable-pip-version-check --no-cache-dir install boto3 \ - && pip --disable-pip-version-check --no-cache-dir install botocore \ - # - # Update Python environment based on requirements.txt - # && pip --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ - # && rm -rf /tmp/pip-tmp \ - # - # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user. - && groupadd --gid $USER_GID $USERNAME \ - && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ - # [Optional] Add sudo support for the non-root user - && apt-get install -y sudo \ - && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\ - && chmod 0440 /etc/sudoers.d/$USERNAME \ - # - # Clean up - && apt-get autoremove -y \ - && apt-get clean -y \ - && rm -rf /var/lib/apt/lists/* - - -# Switch back to dialog for any ad-hoc use of apt-get -ENV DEBIAN_FRONTEND= - - diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 78d0cba..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,36 +0,0 @@ -// For format details, see https://aka.ms/vscode-remote/devcontainer.json or the definition README at -// https://github.com/microsoft/vscode-dev-containers/tree/master/containers/python-3 -{ - "name": "Python3 Ansible", - "context": "..", - "dockerFile": "Dockerfile", - - // Use 'settings' to set *default* container specific settings.json values on container create. - // You can edit these settings after create using File > Preferences > Settings > Remote. - "settings": { - "terminal.integrated.shell.linux": "/bin/bash", - "python.pythonPath": "/usr/local/bin/python", - "python.linting.enabled": true, - "python.linting.pylintEnabled": true, - "python.linting.pylintPath": "/usr/local/bin/pylint" - }, - - // Uncomment the next line if you want to publish any ports. - // "appPort": [], - - // Uncomment the next line to run commands after the container is created. - "postCreateCommand": "pip install -r requirements.txt", - "runArgs": ["-e","ANSIBLE_CONNECTION_PLUGINS=/workspaces/ansible-eci-connector/plugins/connection ANSIBLE_HOST_KEY_CHECKING=False"], - - // Uncomment the next line to use a non-root user. On Linux, this will prevent - // new files getting created as root, but you may need to update the USER_UID - // and USER_GID in .devcontainer/Dockerfile to match your user if not 1000. - // "runArgs": [ "-u", "vscode" ], - // - //"runArgs": ["-v", "${env:HOME}${env:USERPROFILE}:/root/.ansible/plugins/"], - - // Add the IDs of extensions you want installed when the container is created in the array below. - "extensions": [ - "ms-python.python" - ] -} diff --git a/README.md b/README.md index b6e229d..887a8f1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # EC2 Instance Connect Connection Plugin for Ansible - The EC2 Instance Connect (ECI) connection plugin was created to take advantage of AWS's ECI capability Rather than rely on public keys statically stored on resources, this allows us to take advantage of using AWS native roles and permissions to access and manage linux servers instead. This is helpful in situations where you need to use continue to use ansible over AWS native instance management solutions, but want to take advantage of AWS's native IAM model for authorization as well as to avoid sharing of long living private keys. @@ -17,27 +16,33 @@ In general, aligned to the same requirements as most other mssh m ## TODO - IP Address to instance id (or vice versa?) lookup - Handle boto3 / other requirements missing cleanly -- Align requirements to better follow ansible plugin standards - remove temp keys when run finishes - persist temp key across tasks? -- Add a docker compose file to run without Visual Studio Code \ No newline at end of file +- Look at incorporating into or deprecating in favor of [ansible-collections/community.aws](https://github.com/ansible-collections/community.aws) +- - The S3 bucket does add additional complexity that this avoids... \ No newline at end of file diff --git a/plugins/connection/eci.py b/plugins/connection/eci.py index 00c1864..f241a59 100644 --- a/plugins/connection/eci.py +++ b/plugins/connection/eci.py @@ -3,7 +3,7 @@ short_description: use ec2-instance-connect to support the ansible ssh module description: - This connection plugin extends the ssh module and uses ec2-instance-connect to handle access permissions - author: mpieters + author: mpieters3 options: aws_access_key: description: AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used. @@ -43,13 +43,17 @@ vars: - name: ansible_instance_id host: - description: Hostname/ip to connect to. + description: Hostname/IP to connect to. default: inventory_hostname vars: + - name: inventory_hostname - name: ansible_host - name: ansible_ssh_host + - name: delegated_vars['ansible_host'] + - name: delegated_vars['ansible_ssh_host'] host_key_checking: - description: Determines if ssh should check host keys + description: Determines if SSH should check host keys. + default: True type: boolean ini: - section: defaults @@ -66,8 +70,27 @@ version_added: '2.5' - name: ansible_ssh_host_key_checking version_added: '2.5' + password: + description: Authentication password for the C(remote_user). Can be supplied as CLI option. + vars: + - name: ansible_password + - 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. + - Defaults to C(Enter PIN for) when pkcs11_provider is set. + default: '' + ini: + - section: 'ssh_connection' + key: 'sshpass_prompt' + env: + - name: ANSIBLE_SSHPASS_PROMPT + vars: + - name: ansible_sshpass_prompt + version_added: '2.10' ssh_args: - description: Arguments to pass to all ssh cli tools + description: Arguments to pass to all SSH CLI tools. default: '-C -o ControlMaster=auto -o ControlPersist=60s' ini: - section: 'ssh_connection' @@ -78,7 +101,7 @@ - name: ansible_ssh_args version_added: '2.7' ssh_common_args: - description: Common extra args for all ssh CLI tools + description: Common extra args for all SSH CLI tools. ini: - section: 'ssh_connection' key: 'ssh_common_args' @@ -88,12 +111,15 @@ version_added: '2.7' vars: - name: ansible_ssh_common_args + cli: + - name: ssh_common_args + default: '' ssh_executable: default: ssh description: - - This defines the location of the ssh binary. It defaults to ``ssh`` which will use the first ssh binary available in $PATH. - - This option is usually not required, it might be useful when access to system ssh is restricted, - or when using ssh wrappers to connect to remote hosts. + - This defines the location of the SSH binary. It defaults to C(ssh) which will use the first SSH binary available in $PATH. + - This option is usually not required, it might be useful when access to system SSH is restricted, + or when using SSH wrappers to connect to remote hosts. env: [{name: ANSIBLE_SSH_EXECUTABLE}] ini: - {key: ssh_executable, section: ssh_connection} @@ -105,7 +131,7 @@ sftp_executable: default: sftp description: - - This defines the location of the sftp binary. It defaults to ``sftp`` which will use the first binary available in $PATH. + - This defines the location of the sftp binary. It defaults to C(sftp) which will use the first binary available in $PATH. env: [{name: ANSIBLE_SFTP_EXECUTABLE}] ini: - {key: sftp_executable, section: ssh_connection} @@ -116,7 +142,7 @@ scp_executable: default: scp description: - - This defines the location of the scp binary. It defaults to `scp` which will use the first binary available in $PATH. + - This defines the location of the scp binary. It defaults to C(scp) which will use the first binary available in $PATH. env: [{name: ANSIBLE_SCP_EXECUTABLE}] ini: - {key: scp_executable, section: ssh_connection} @@ -125,7 +151,7 @@ - name: ansible_scp_executable version_added: '2.7' scp_extra_args: - description: Extra exclusive to the ``scp`` CLI + description: Extra exclusive to the C(scp) CLI vars: - name: ansible_scp_extra_args env: @@ -135,8 +161,11 @@ - key: scp_extra_args section: ssh_connection version_added: '2.7' + cli: + - name: scp_extra_args + default: '' sftp_extra_args: - description: Extra exclusive to the ``sftp`` CLI + description: Extra exclusive to the C(sftp) CLI vars: - name: ansible_sftp_extra_args env: @@ -146,8 +175,11 @@ - key: sftp_extra_args section: ssh_connection version_added: '2.7' + cli: + - name: sftp_extra_args + default: '' ssh_extra_args: - description: Extra exclusive to the 'ssh' CLI + description: Extra exclusive to the SSH CLI. vars: - name: ansible_ssh_extra_args env: @@ -157,10 +189,12 @@ - key: ssh_extra_args section: ssh_connection version_added: '2.7' - retries: - # constant: ANSIBLE_SSH_RETRIES + cli: + - name: ssh_extra_args + default: '' + reconnection_retries: description: Number of attempts to connect. - default: 3 + default: 0 type: integer env: - name: ANSIBLE_SSH_RETRIES @@ -175,7 +209,6 @@ port: description: Remote port to connect to. type: int - default: 22 ini: - section: defaults key: remote_port @@ -184,10 +217,12 @@ vars: - name: ansible_port - name: ansible_ssh_port + keyword: + - name: port remote_user: description: - User name with which to login to the remote server, normally set by the remote_user keyword. - - If no user is supplied, Ansible will let the ssh client binary choose the user as it normally + - If no user is supplied, Ansible will let the SSH client binary choose the user as it normally. ini: - section: defaults key: remote_user @@ -196,30 +231,29 @@ vars: - name: ansible_user - name: ansible_ssh_user + cli: + - name: user + keyword: + - name: remote_user pipelining: - default: ANSIBLE_PIPELINING description: - - Pipelining reduces the number of SSH operations required to execute a module on the remote server, - by executing many Ansible modules without actual file transfer. - - This can result in a very significant performance improvement when enabled. - - However this conflicts with privilege escalation (become). - For example, when using sudo operations you must first disable 'requiretty' in the sudoers file for the target hosts, - which is why this feature is disabled by default. + - Pipelining settings env: - name: ANSIBLE_PIPELINING - #- name: ANSIBLE_SSH_PIPELINING + - name: ANSIBLE_SSH_PIPELINING ini: - section: defaults key: pipelining - #- section: ssh_connection - # key: pipelining - type: boolean + - section: connection + key: pipelining + - section: ssh_connection + key: pipelining vars: - name: ansible_pipelining - name: ansible_ssh_pipelining private_key_file: description: - - Path to private key file to use for authentication + - Path to private key file to use for authentication. ini: - section: defaults key: private_key_file @@ -228,10 +262,15 @@ vars: - name: ansible_private_key_file - name: ansible_ssh_private_key_file + cli: + - name: private_key_file + option: '--private-key' control_path: description: - - This is the location to save ssh's ControlPath sockets, it uses ssh's variable substitution. - - Since 2.3, if null, ansible will generate a unique hash. Use `%(directory)s` to indicate where to use the control dir path setting. + - This is the location to save SSH's ControlPath sockets, it uses SSH's variable substitution. + - Since 2.3, if null (default), ansible will generate a unique hash. Use ``%(directory)s`` to indicate where to use the control dir path setting. + - Before 2.3 it defaulted to ``control_path=%(directory)s/ansible-ssh-%%h-%%p-%%r``. + - Be aware that this setting is ignored if C(-o ControlPath) is set in ssh args. env: - name: ANSIBLE_SSH_CONTROL_PATH ini: @@ -244,7 +283,7 @@ default: ~/.ansible/cp description: - This sets the directory to use for ssh control path if the control path setting is null. - - Also, provides the `%(directory)s` variable for the control path setting. + - Also, provides the ``%(directory)s`` variable for the control path setting. env: - name: ANSIBLE_SSH_CONTROL_PATH_DIR ini: @@ -267,7 +306,7 @@ description: - "Preferred method to use when transferring files over ssh" - Setting to 'smart' (default) will try them in order, until one succeeds or they all fail - - Using 'piped' creates an ssh pipe with ``dd`` on either side to copy the data + - Using 'piped' creates an ssh pipe with C(dd) on either side to copy the data choices: ['sftp', 'scp', 'piped', 'smart'] env: [{name: ANSIBLE_SSH_TRANSFER_METHOD}] ini: @@ -276,11 +315,16 @@ - name: ansible_ssh_transfer_method version_added: '2.12' scp_if_ssh: + deprecated: + why: In favor of the "ssh_transfer_method" option. + version: "2.17" + alternatives: ssh_transfer_method default: smart description: - - "Prefered method to use when transfering files over ssh" - - When set to smart, Ansible will try them until one succeeds or they all fail - - If set to True, it will force 'scp', if False it will use 'sftp' + - "Preferred method to use when transferring files over SSH." + - When set to I(smart), Ansible will try them until one succeeds or they all fail. + - If set to I(True), it will force 'scp', if I(False) it will use 'sftp'. + - This setting will overridden by ssh_transfer_method if set. env: [{name: ANSIBLE_SCP_IF_SSH}] ini: - {key: scp_if_ssh, section: ssh_connection} @@ -290,7 +334,7 @@ use_tty: version_added: '2.5' default: 'yes' - description: add -tt to ssh commands to force tty allocation + description: add -tt to ssh commands to force tty allocation. env: [{name: ANSIBLE_SSH_USETTY}] ini: - {key: usetty, section: ssh_connection} @@ -301,8 +345,8 @@ timeout: default: 10 description: - - This is the default amount of time we will wait while establishing an ssh connection - - It also controls how long we can wait to access reading the connection once established (select on the socket) + - This is the default amount of time we will wait while establishing an SSH connection. + - It also controls how long we can wait to access reading the connection once established (select on the socket). env: - name: ANSIBLE_TIMEOUT - name: ANSIBLE_SSH_TIMEOUT @@ -319,6 +363,17 @@ 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 importlib @@ -339,7 +394,6 @@ try: from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 from cryptography.hazmat.primitives.serialization.ssh import load_ssh_private_key from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization @@ -350,8 +404,10 @@ import tempfile from datetime import datetime -## we're going to base our connector off the basic SSH connector, as we want nearly all its behavior +import importlib ssh = importlib.import_module('ansible.plugins.connection.ssh') + +## we're going to base our connector off the basic SSH connector, as we want nearly all its behavior display = Display() ECI_PUSH_EXPIRY = 45 diff --git a/requirements.txt b/requirements.txt index aa1c968..f7bd683 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,20 +1,3 @@ -ansible==2.8.5 -asn1crypto==0.24.0 -bcrypt==3.1.7 -boto3==1.9.228 -botocore==1.12.228 -cffi==1.12.3 -cryptography==2.7 -docutils==0.15.2 -ec2instanceconnectcli==1.0.0 -Jinja2==2.10.1 -jmespath==0.9.4 -MarkupSafe==1.1.1 -paramiko==2.6.0 -pycparser==2.19 -PyNaCl==1.3.0 -python-dateutil==2.8.0 -PyYAML==5.1.2 -s3transfer==0.2.1 -six==1.12.0 -urllib3==1.25.3 \ No newline at end of file +ansible-core==2.12.0 +boto3==1.21.23 +ec2instanceconnectcli==1.0.2 \ No newline at end of file diff --git a/test/demo.yml b/test/demo.yml index ad74d85..4651a30 100644 --- a/test/demo.yml +++ b/test/demo.yml @@ -2,11 +2,11 @@ connection: local gather_facts: false tasks: - - name: create a security group in us-east-2 to allow SSH. Not safe for prod! - ec2_group: + - name: create a security group in us-east-1 to allow SSH. Not safe for prod! + amazon.aws.ec2_group: name: eci-example description: an example ec2 group - region: us-east-2 + region: us-east-1 rules: - proto: tcp from_port: 22 @@ -14,26 +14,30 @@ cidr_ip: 0.0.0.0/0 register: security_group - name: create ec2 instance - ec2: - image: ami-00c03f7f7f2ec15c3 + amazon.aws.ec2_instance: + image_id: ami-0c02fb55956c7d316 + state: running wait: yes + tags: + test: test instance_type: t2.micro - group_id: "{{ security_group.group_id }}" - region: us-east-2 - count: 1 + network: + assign_public_ip: true + security_group: "{{ security_group.group_id }}" + region: us-east-1 register: ec2 - name: Add new instance to host group add_host: - hostname: "{{ item.public_ip }}" + hostname: "{{ item.public_ip_address }}" groupname: launched_no_instance - ansible_region: "{{ item.region }}" + ansible_region: us-east-1 loop: "{{ ec2.instances }}" - name: Add new instance to host group (better with ansible_instance_id) add_host: - hostname: "{{ item.public_ip }}" + hostname: "{{ item.public_ip_address }}" groupname: launched - ansible_instance_id: "{{ item.id }}" - ansible_region: "{{ item.region }}" + ansible_instance_id: "{{ item.instance_id }}" + ansible_region: us-east-1 loop: "{{ ec2.instances }}" - name: Wait for SSH to come up wait_for: @@ -45,8 +49,9 @@ - hosts: launched connection: eci vars: - aws_region: us-east-2 + aws_region: us-east-1 ansible_ssh_user: "root" + ansible_host_key_checking: False gather_facts: false tasks: - name: Connect to ec2 using eci with root@ip_address with instance-id @@ -58,7 +63,7 @@ - hosts: launched_no_instance connection: eci vars: - aws_region: us-east-2 + aws_region: us-east-1 ansible_ssh_user: "ec2-user" gather_facts: false tasks: @@ -73,7 +78,7 @@ gather_facts: false tasks: - name: Terminate instances that were previously launched - ec2: - state: 'absent' - region: us-east-2 + amazon.aws.ec2_instance: + state: 'terminated' + region: us-east-1 instance_ids: '{{ ec2.instance_ids }}' \ No newline at end of file From 0ecb2e736e6e9e788996977b5da4de9b901270ef Mon Sep 17 00:00:00 2001 From: mpieters3 Date: Tue, 22 Mar 2022 00:04:54 -0700 Subject: [PATCH 2/2] Removing duplicate import --- plugins/connection/eci.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/connection/eci.py b/plugins/connection/eci.py index f241a59..fbeaf56 100644 --- a/plugins/connection/eci.py +++ b/plugins/connection/eci.py @@ -377,6 +377,8 @@ ''' import importlib +import tempfile +from datetime import datetime import os import time @@ -401,10 +403,6 @@ except ImportError: HAS_CRYPTOGRAPHY = False -import tempfile -from datetime import datetime - -import importlib ssh = importlib.import_module('ansible.plugins.connection.ssh') ## we're going to base our connector off the basic SSH connector, as we want nearly all its behavior