lxd_connection: Allow non-root users to connect to an instance (#9659)
* fix: add support for non-root user
* fix: show correct info for connection
* fix: use build_exec_command to execute as nonroot
* unset default user
* feat: add options for setting remote user and become method
* fix: add root as default remote_user
* fix: remove ansible_ssh_user from remote_user vars
* fix: use single quotes inside f-string
* fix: ensure lxc exec comes first
* fix: line length
* fix: use -c flag with su
* Update plugins/connection/lxd.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* Update plugins/connection/lxd.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* Update plugins/connection/lxd.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* doc: add changelog fragment
* fix: use underscore for module name in fragment
* Update 9659-lxd_connection-nonroot-user.yml
Co-authored-by: Felix Fontein <felix@fontein.de>
* fix: add put command
* feat: add get_remote_uid_gid placeholder function
* feat: complete placeholder _get_remote_uid_gid function
* fix: better logging
* fix: ensure default values are of type str
* fix: use ints for uid and gid
* fix: print put command
* fix: format
* fix: display msg for PUT
* fix: add comment about defaults
* fix: format
* fix: use os module to get uid and gid
* Revert "fix: use os module to get uid and gid"
This reverts commit bb2ba14b8f
.
* Update plugins/connection/lxd.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* fix: omit uid, gid args in lxd file push if root
---------
Co-authored-by: Felix Fontein <felix@fontein.de>
pull/9750/head
parent
9d0bd1d4d9
commit
06df717bc6
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- lxd connection plugin - adds ``remote_user`` and ``lxd_become_method`` parameters for allowing a non-root user to connect to an LXD instance (https://github.com/ansible-collections/community.general/pull/9659).
|
|
@ -32,6 +32,15 @@ options:
|
|||
vars:
|
||||
- name: ansible_executable
|
||||
- name: ansible_lxd_executable
|
||||
lxd_become_method:
|
||||
description:
|
||||
- Become command used to switch to a non-root user.
|
||||
- Is only used when O(remote_user) is not V(root).
|
||||
type: str
|
||||
default: /bin/su
|
||||
vars:
|
||||
- name: lxd_become_method
|
||||
version_added: 10.4.0
|
||||
remote:
|
||||
description:
|
||||
- Name of the LXD remote to use.
|
||||
|
@ -40,6 +49,22 @@ options:
|
|||
vars:
|
||||
- name: ansible_lxd_remote
|
||||
version_added: 2.0.0
|
||||
remote_user:
|
||||
description:
|
||||
- User to login/authenticate as.
|
||||
- Can be set from the CLI via the C(--user) or C(-u) options.
|
||||
type: string
|
||||
default: root
|
||||
vars:
|
||||
- name: ansible_user
|
||||
env:
|
||||
- name: ANSIBLE_REMOTE_USER
|
||||
ini:
|
||||
- section: defaults
|
||||
key: remote_user
|
||||
keyword:
|
||||
- name: remote_user
|
||||
version_added: 10.4.0
|
||||
project:
|
||||
description:
|
||||
- Name of the LXD project to use.
|
||||
|
@ -63,7 +88,6 @@ class Connection(ConnectionBase):
|
|||
|
||||
transport = 'community.general.lxd'
|
||||
has_pipelining = True
|
||||
default_user = 'root'
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
@ -73,9 +97,6 @@ class Connection(ConnectionBase):
|
|||
except ValueError:
|
||||
raise AnsibleError("lxc command not found in PATH")
|
||||
|
||||
if self._play_context.remote_user is not None and self._play_context.remote_user != 'root':
|
||||
self._display.warning('lxd does not support remote_user, using default: root')
|
||||
|
||||
def _host(self):
|
||||
""" translate remote_addr to lxd (short) hostname """
|
||||
return self.get_option("remote_addr").split(".", 1)[0]
|
||||
|
@ -85,25 +106,40 @@ class Connection(ConnectionBase):
|
|||
super(Connection, self)._connect()
|
||||
|
||||
if not self._connected:
|
||||
self._display.vvv("ESTABLISH LXD CONNECTION FOR USER: root", host=self._host())
|
||||
self._display.vvv(f"ESTABLISH LXD CONNECTION FOR USER: {self.get_option('remote_user')}", host=self._host())
|
||||
self._connected = True
|
||||
|
||||
def _build_command(self, cmd) -> str:
|
||||
"""build the command to execute on the lxd host"""
|
||||
|
||||
exec_cmd = [self._lxc_cmd]
|
||||
|
||||
if self.get_option("project"):
|
||||
exec_cmd.extend(["--project", self.get_option("project")])
|
||||
|
||||
exec_cmd.extend(["exec", f"{self.get_option('remote')}:{self._host()}", "--"])
|
||||
|
||||
if self.get_option("remote_user") != "root":
|
||||
self._display.vvv(
|
||||
f"INFO: Running as non-root user: {self.get_option('remote_user')}, \
|
||||
trying to run 'lxc exec' with become method: {self.get_option('lxd_become_method')}",
|
||||
host=self._host(),
|
||||
)
|
||||
exec_cmd.extend(
|
||||
[self.get_option("lxd_become_method"), self.get_option("remote_user"), "-c"]
|
||||
)
|
||||
|
||||
exec_cmd.extend([self.get_option("executable"), "-c", cmd])
|
||||
|
||||
return exec_cmd
|
||||
|
||||
def exec_command(self, cmd, in_data=None, sudoable=True):
|
||||
""" execute a command on the lxd host """
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
|
||||
self._display.vvv(f"EXEC {cmd}", host=self._host())
|
||||
|
||||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
local_cmd.extend(["--project", self.get_option("project")])
|
||||
local_cmd.extend([
|
||||
"exec",
|
||||
f"{self.get_option('remote')}:{self._host()}",
|
||||
"--",
|
||||
self.get_option("executable"), "-c", cmd
|
||||
])
|
||||
|
||||
local_cmd = self._build_command(cmd)
|
||||
self._display.vvvvv(f"EXEC {local_cmd}", host=self._host())
|
||||
|
||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
||||
|
@ -125,6 +161,25 @@ class Connection(ConnectionBase):
|
|||
|
||||
return process.returncode, stdout, stderr
|
||||
|
||||
def _get_remote_uid_gid(self) -> tuple[int, int]:
|
||||
"""Get the user and group ID of 'remote_user' from the instance."""
|
||||
|
||||
rc, uid_out, err = self.exec_command("/bin/id -u")
|
||||
if rc != 0:
|
||||
raise AnsibleError(
|
||||
f"Failed to get remote uid for user {self.get_option('remote_user')}: {err}"
|
||||
)
|
||||
uid = uid_out.strip()
|
||||
|
||||
rc, gid_out, err = self.exec_command("/bin/id -g")
|
||||
if rc != 0:
|
||||
raise AnsibleError(
|
||||
f"Failed to get remote gid for user {self.get_option('remote_user')}: {err}"
|
||||
)
|
||||
gid = gid_out.strip()
|
||||
|
||||
return int(uid), int(gid)
|
||||
|
||||
def put_file(self, in_path, out_path):
|
||||
""" put a file from local to lxd """
|
||||
super(Connection, self).put_file(in_path, out_path)
|
||||
|
@ -137,11 +192,32 @@ class Connection(ConnectionBase):
|
|||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
local_cmd.extend(["--project", self.get_option("project")])
|
||||
local_cmd.extend([
|
||||
"file", "push",
|
||||
in_path,
|
||||
f"{self.get_option('remote')}:{self._host()}/{out_path}"
|
||||
])
|
||||
|
||||
if self.get_option("remote_user") != "root":
|
||||
uid, gid = self._get_remote_uid_gid()
|
||||
local_cmd.extend(
|
||||
[
|
||||
"file",
|
||||
"push",
|
||||
"--uid",
|
||||
str(uid),
|
||||
"--gid",
|
||||
str(gid),
|
||||
in_path,
|
||||
f"{self.get_option('remote')}:{self._host()}/{out_path}",
|
||||
]
|
||||
)
|
||||
else:
|
||||
local_cmd.extend(
|
||||
[
|
||||
"file",
|
||||
"push",
|
||||
in_path,
|
||||
f"{self.get_option('remote')}:{self._host()}/{out_path}",
|
||||
]
|
||||
)
|
||||
|
||||
self._display.vvvvv(f"PUT {local_cmd}", host=self._host())
|
||||
|
||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
||||
|
||||
|
|
Loading…
Reference in New Issue