From 6e555b9a0e2d68542f589a81fb8b33153d10fa81 Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sun, 6 Mar 2022 20:22:30 +0000 Subject: [PATCH] Usa sshlxd --- .gitignore | 1 + 03_container_haproxy.yaml | 2 +- 04_container_gitea.yaml | 2 +- ansible.cfg | 8 + connection_plugins/sshlxd.py | 364 +++++++++++++++++++++++++++++++++++ hosts | 6 +- 6 files changed, 378 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100644 ansible.cfg create mode 100644 connection_plugins/sshlxd.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/03_container_haproxy.yaml b/03_container_haproxy.yaml index f67b456..2bc1aaa 100644 --- a/03_container_haproxy.yaml +++ b/03_container_haproxy.yaml @@ -1,6 +1,6 @@ --- - name: HAPROXY -- install - hosts: haproxy + hosts: haproxy@git.ansol.org become: true tasks: - name: HAPROXY -- install needed packages diff --git a/04_container_gitea.yaml b/04_container_gitea.yaml index cc753e3..7deebd6 100644 --- a/04_container_gitea.yaml +++ b/04_container_gitea.yaml @@ -1,6 +1,6 @@ --- - name: GITEA -- install - hosts: gitea + hosts: gitea@git.ansol.org roles: - { role: do1jlr.gitea, tags: gitea } vars: diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..5d5f478 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,8 @@ +[defaults] +inventory = hosts + +[connection] +pipelining = True + +[ssh_connection] +ssh_args = -o ControlMaster=auto -o ControlPersist=3600s -o PreferredAuthentications=publickey diff --git a/connection_plugins/sshlxd.py b/connection_plugins/sshlxd.py new file mode 100644 index 0000000..3b601b6 --- /dev/null +++ b/connection_plugins/sshlxd.py @@ -0,0 +1,364 @@ +from __future__ import (absolute_import, division, print_function) +import os +import os.path +import pipes + +from ansible.errors import AnsibleError +from ansible.plugins.connection.ssh import Connection as SSHConnection +from contextlib import contextmanager + +__metaclass__ = type + +DOCUMENTATION = ''' + connection: ssh + short_description: connect to LXD containers using lxc comand via ssh client binary + description: + - Use the ssh client library to access the LXD host and then use lxc commands to interact with the container. + - Options are the same as ssh, except got host variable wich should be @ + author: Andreas Fuchs (@antifuchs), Joao Silva(@wnke) + version_added: historical + options: + host: + description: Hostname/ip to connect to. + default: inventory_hostname + vars: + - name: ansible_host + - name: ansible_ssh_host + host_key_checking: + description: Determines if ssh should check host keys + type: boolean + ini: + - section: defaults + key: 'host_key_checking' + - section: ssh_connection + key: 'host_key_checking' + version_added: '2.5' + env: + - name: ANSIBLE_HOST_KEY_CHECKING + - name: ANSIBLE_SSH_HOST_KEY_CHECKING + version_added: '2.5' + vars: + - name: ansible_host_key_checking + 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 + sshpass_prompt: + description: Password prompt that sshpass should search for. Supported by sshpass 1.06 and up + 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 + default: '-C -o ControlMaster=auto -o ControlPersist=60s' + ini: + - section: 'ssh_connection' + key: 'ssh_args' + env: + - name: ANSIBLE_SSH_ARGS + vars: + - name: ansible_ssh_args + version_added: '2.7' + ssh_common_args: + description: Common extra args for all ssh CLI tools + ini: + - section: 'ssh_connection' + key: 'ssh_common_args' + version_added: '2.7' + env: + - name: ANSIBLE_SSH_COMMON_ARGS + version_added: '2.7' + vars: + - name: ansible_ssh_common_args + 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. + env: [{name: ANSIBLE_SSH_EXECUTABLE}] + ini: + - {key: ssh_executable, section: ssh_connection} + #const: ANSIBLE_SSH_EXECUTABLE + version_added: "2.2" + vars: + - name: ansible_ssh_executable + version_added: '2.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. + env: [{name: ANSIBLE_SFTP_EXECUTABLE}] + ini: + - {key: sftp_executable, section: ssh_connection} + version_added: "2.6" + 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. + env: [{name: ANSIBLE_SCP_EXECUTABLE}] + ini: + - {key: scp_executable, section: ssh_connection} + version_added: "2.6" + scp_extra_args: + description: Extra exclusive to the 'scp' CLI + vars: + - name: ansible_scp_extra_args + sftp_extra_args: + description: Extra exclusive to the 'sftp' CLI + vars: + - name: ansible_sftp_extra_args + ssh_extra_args: + description: Extra exclusive to the 'ssh' CLI + vars: + - name: ansible_ssh_extra_args + env: + - name: ANSIBLE_SSH_EXTRA_ARGS + version_added: '2.7' + ini: + - key: ssh_extra_args + section: ssh_connection + version_added: '2.7' + reconnection_retries: + description: Number of attempts to connect. + default: 0 + type: integer + env: + - name: ANSIBLE_SSH_RETRIES + ini: + - section: connection + key: retries + - section: ssh_connection + key: retries + vars: + - name: ansible_ssh_retries + version_added: '2.7' + port: + description: Remote port to connect to. + type: int + default: 22 + ini: + - section: defaults + key: remote_port + env: + - name: ANSIBLE_REMOTE_PORT + vars: + - name: ansible_port + - name: ansible_ssh_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 + ini: + - section: defaults + key: remote_user + env: + - name: ANSIBLE_REMOTE_USER + vars: + - name: ansible_user + - name: ansible_ssh_user + pipelining: + env: + - name: ANSIBLE_PIPELINING + - name: ANSIBLE_SSH_PIPELINING + ini: + - 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 + ini: + - section: defaults + key: private_key_file + env: + - name: ANSIBLE_PRIVATE_KEY_FILE + vars: + - name: ansible_private_key_file + - name: ansible_ssh_private_key_file + 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. + env: + - name: ANSIBLE_SSH_CONTROL_PATH + ini: + - key: control_path + section: ssh_connection + control_path_dir: + 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. + env: + - name: ANSIBLE_SSH_CONTROL_PATH_DIR + ini: + - section: ssh_connection + key: control_path_dir + sftp_batch_mode: + default: 'yes' + description: 'TODO: write it' + env: [{name: ANSIBLE_SFTP_BATCH_MODE}] + ini: + - {key: sftp_batch_mode, section: ssh_connection} + type: bool + ssh_transfer_method: + default: smart + 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 + choices: ['sftp', 'scp', 'piped', 'smart'] + env: [{name: ANSIBLE_SSH_TRANSFER_METHOD}] + ini: + - {key: transfer_method, section: ssh_connection} + vars: + - name: ansible_ssh_transfer_method + version_added: '2.12' + scp_if_ssh: + 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' + env: [{name: ANSIBLE_SCP_IF_SSH}] + ini: + - {key: scp_if_ssh, section: ssh_connection} + use_tty: + version_added: '2.5' + default: 'yes' + description: add -tt to ssh commands to force tty allocation + env: [{name: ANSIBLE_SSH_USETTY}] + ini: + - {key: usetty, section: ssh_connection} + type: bool + yaml: {key: connection.usetty} + timeout: + default: 10 + description: + - This is the default ammount 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 + version_added: '2.11' + ini: + - key: timeout + section: defaults + - key: timeout + section: ssh_connection + version_added: '2.11' + vars: + - name: ansible_ssh_timeout + version_added: '2.11' + 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 +''' + + +try: + from __main__ import display +except ImportError: + from ansible.utils.display import Display + display = Display() + + +class Connection(SSHConnection): + ''' ssh based connections ''' + + transport = 'sshlxd' + + def __init__(self, *args, **kwargs): + + super(Connection, self).__init__(*args, **kwargs) + + self.inventory_hostname = self.host + self.containerspec, self.host = self.host.split('@', 1) + + self.connector = None + + def get_container_id(self): + return self.containerspec + + def get_container_connector(self): + return 'lxc' + + def _strip_sudo(self, executable, cmd): + # Get the command without sudo + sudoless = cmd.rsplit(executable + ' -c ', 1)[1] + # Get the quotes + quotes = sudoless.partition('echo')[0] + # Get the string between the quotes + cmd = sudoless[len(quotes):-len(quotes + '?')] + return cmd + + def host_command(self, cmd, do_become=False): + return super(Connection, self).exec_command(cmd, in_data=None, sudoable=True) + + def exec_command(self, cmd, in_data=None, executable='/bin/sh', sudoable=True): + ''' run a command in the container ''' + + self.set_option('host', self.host) + cmd = '%s exec %s -- %s' % (self.get_container_connector(), self.get_container_id(), cmd) + + return super(Connection, self).exec_command(cmd, in_data, True) + + def container_path(self, path): + return self.get_container_id() + path + + @contextmanager + def tempfile(self): + code, stdout, stderr = self.host_command('mktemp') + if code != 0: + raise AnsibleError("failed to make temp file:\n%s\n%s" % (stdout, stderr)) + tmp = stdout.strip().decode('utf-8').split('\n')[-1] + + yield tmp + + code, stdout, stderr = self.host_command(' '.join(['rm', tmp])) + if code != 0: + raise AnsibleError("failed to remove temp file %s:\n%s\n%s" % (tmp, stdout, stderr)) + + def put_file(self, in_path, out_path): + ''' transfer a file from local to remote container ''' + with self.tempfile() as tmp: + super(Connection, self).put_file(in_path, tmp) + self.host_command(' '.join(['/snap/bin/lxc', 'exec', self.get_container_id(), '--', 'mkdir', '-p', os.path.dirname(out_path)]), do_become=True) + self.host_command(' '.join(['/snap/bin/lxc', 'file', 'push', '--debug', tmp, self.container_path(out_path)]), do_become=True) + + def fetch_file(self, in_path, out_path): + ''' fetch a file from remote to local ''' + with self.tempfile() as tmp: + self.host_command(' '.join(['/snap/bin/lxc', 'file', 'pull', self.container_path(in_path), tmp]), do_become=True) + super(Connection, self).fetch_file(tmp, out_path) + + def close(self): + ''' Close the connection, nothing to do for us ''' + super(Connection, self).close() diff --git a/hosts b/hosts index da9b2a0..3c0ef69 100644 --- a/hosts +++ b/hosts @@ -1,6 +1,6 @@ [lxd_hosts] -git.ansol.org ansible_ssh_user=root +git.ansol.org [containers] -haproxy ansible_lxd_remote=lxd-ansol ansible_lxd_host=haproxy ansible_connection=community.general.lxd ansible_python_interpreter=/usr/bin/python3 -gitea ansible_lxd_remote=lxd-ansol ansible_lxd_host=gitea ansible_connection=community.general.lxd ansible_python_interpreter=/usr/bin/python3 +haproxy@git.ansol.org ansible_connection=sshlxd +gitea@git.ansol.org ansible_connection=sshlxd