mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
add systemd run0 as a become method (#8306)
* add systemd run0 as a become method Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * add fragment Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * remove space after hyphen Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * replace ansible with collection version Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * update version_added and remove changelog fragment Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * update formating Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * add types Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * slim super() Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * imports must appear below docs Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * add initial unit test Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * update unit tests Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> --------- Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>
This commit is contained in:
parent
3b7f13c58e
commit
d347bf5fa0
3 changed files with 194 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -33,6 +33,8 @@ files:
|
||||||
maintainers: $team_ansible_core
|
maintainers: $team_ansible_core
|
||||||
$becomes/pmrun.py:
|
$becomes/pmrun.py:
|
||||||
maintainers: $team_ansible_core
|
maintainers: $team_ansible_core
|
||||||
|
$becomes/run0.py:
|
||||||
|
maintainers: konstruktoid
|
||||||
$becomes/sesu.py:
|
$becomes/sesu.py:
|
||||||
maintainers: nekonyuu
|
maintainers: nekonyuu
|
||||||
$becomes/sudosu.py:
|
$becomes/sudosu.py:
|
||||||
|
|
128
plugins/become/run0.py
Normal file
128
plugins/become/run0.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2024, Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
DOCUMENTATION = """
|
||||||
|
name: run0
|
||||||
|
short_description: Systemd's run0
|
||||||
|
description:
|
||||||
|
- This become plugins allows your remote/login user to execute commands as another user via the C(run0) utility.
|
||||||
|
author:
|
||||||
|
- Thomas Sjögren (@konstruktoid)
|
||||||
|
version_added: '9.0.0'
|
||||||
|
options:
|
||||||
|
become_user:
|
||||||
|
description: User you 'become' to execute the task.
|
||||||
|
default: root
|
||||||
|
ini:
|
||||||
|
- section: privilege_escalation
|
||||||
|
key: become_user
|
||||||
|
- section: run0_become_plugin
|
||||||
|
key: user
|
||||||
|
vars:
|
||||||
|
- name: ansible_become_user
|
||||||
|
- name: ansible_run0_user
|
||||||
|
env:
|
||||||
|
- name: ANSIBLE_BECOME_USER
|
||||||
|
- name: ANSIBLE_RUN0_USER
|
||||||
|
type: string
|
||||||
|
become_exe:
|
||||||
|
description: The C(run0) executable.
|
||||||
|
default: run0
|
||||||
|
ini:
|
||||||
|
- section: privilege_escalation
|
||||||
|
key: become_exe
|
||||||
|
- section: run0_become_plugin
|
||||||
|
key: executable
|
||||||
|
vars:
|
||||||
|
- name: ansible_become_exe
|
||||||
|
- name: ansible_run0_exe
|
||||||
|
env:
|
||||||
|
- name: ANSIBLE_BECOME_EXE
|
||||||
|
- name: ANSIBLE_RUN0_EXE
|
||||||
|
type: string
|
||||||
|
become_flags:
|
||||||
|
description: Options to pass to run0.
|
||||||
|
default: ''
|
||||||
|
ini:
|
||||||
|
- section: privilege_escalation
|
||||||
|
key: become_flags
|
||||||
|
- section: run0_become_plugin
|
||||||
|
key: flags
|
||||||
|
vars:
|
||||||
|
- name: ansible_become_flags
|
||||||
|
- name: ansible_run0_flags
|
||||||
|
env:
|
||||||
|
- name: ANSIBLE_BECOME_FLAGS
|
||||||
|
- name: ANSIBLE_RUN0_FLAGS
|
||||||
|
type: string
|
||||||
|
notes:
|
||||||
|
- This plugin will only work when a polkit rule is in place.
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
# An example polkit rule that allows the user 'ansible' in the 'wheel' group
|
||||||
|
# to execute commands using run0 without authentication.
|
||||||
|
/etc/polkit-1/rules.d/60-run0-fast-user-auth.rules: |
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if(action.id == "org.freedesktop.systemd1.manage-units" &&
|
||||||
|
subject.isInGroup("wheel")
|
||||||
|
subject.user == "ansible") {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
|
||||||
|
from re import compile as re_compile
|
||||||
|
|
||||||
|
from ansible.plugins.become import BecomeBase
|
||||||
|
from ansible.module_utils._text import to_bytes
|
||||||
|
|
||||||
|
ansi_color_codes = re_compile(to_bytes(r"\x1B\[[0-9;]+m"))
|
||||||
|
|
||||||
|
|
||||||
|
class BecomeModule(BecomeBase):
|
||||||
|
|
||||||
|
name = "community.general.run0"
|
||||||
|
|
||||||
|
prompt = "Password: "
|
||||||
|
fail = ("==== AUTHENTICATION FAILED ====",)
|
||||||
|
success = ("==== AUTHENTICATION COMPLETE ====",)
|
||||||
|
require_tty = (
|
||||||
|
True # see https://github.com/ansible-collections/community.general/issues/6932
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remove_ansi_codes(line):
|
||||||
|
return ansi_color_codes.sub(b"", line)
|
||||||
|
|
||||||
|
def build_become_command(self, cmd, shell):
|
||||||
|
super().build_become_command(cmd, shell)
|
||||||
|
|
||||||
|
if not cmd:
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
become = self.get_option("become_exe")
|
||||||
|
flags = self.get_option("become_flags")
|
||||||
|
user = self.get_option("become_user")
|
||||||
|
|
||||||
|
return (
|
||||||
|
f"{become} --user={user} {flags} {self._build_success_command(cmd, shell)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_success(self, b_output):
|
||||||
|
b_output = self.remove_ansi_codes(b_output)
|
||||||
|
return super().check_success(b_output)
|
||||||
|
|
||||||
|
def check_incorrect_password(self, b_output):
|
||||||
|
b_output = self.remove_ansi_codes(b_output)
|
||||||
|
return super().check_incorrect_password(b_output)
|
||||||
|
|
||||||
|
def check_missing_password(self, b_output):
|
||||||
|
b_output = self.remove_ansi_codes(b_output)
|
||||||
|
return super().check_missing_password(b_output)
|
64
tests/unit/plugins/become/test_run0.py
Normal file
64
tests/unit/plugins/become/test_run0.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# Copyright (c) 2024 Ansible Project
|
||||||
|
#
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from ansible import context
|
||||||
|
|
||||||
|
from .helper import call_become_plugin
|
||||||
|
|
||||||
|
|
||||||
|
def test_run0_basic(mocker, parser, reset_cli_args):
|
||||||
|
options = parser.parse_args([])
|
||||||
|
context._init_global_context(options)
|
||||||
|
|
||||||
|
default_cmd = "/bin/foo"
|
||||||
|
default_exe = "/bin/sh"
|
||||||
|
run0_exe = "run0"
|
||||||
|
|
||||||
|
success = "BECOME-SUCCESS-.+?"
|
||||||
|
|
||||||
|
task = {
|
||||||
|
"become_method": "community.general.run0",
|
||||||
|
}
|
||||||
|
var_options = {}
|
||||||
|
cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe)
|
||||||
|
assert (
|
||||||
|
re.match(
|
||||||
|
f"{run0_exe} --user=root {default_exe} -c 'echo {success}; {default_cmd}'",
|
||||||
|
cmd,
|
||||||
|
)
|
||||||
|
is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run0_flags(mocker, parser, reset_cli_args):
|
||||||
|
options = parser.parse_args([])
|
||||||
|
context._init_global_context(options)
|
||||||
|
|
||||||
|
default_cmd = "/bin/foo"
|
||||||
|
default_exe = "/bin/sh"
|
||||||
|
run0_exe = "run0"
|
||||||
|
run0_flags = "--nice=15"
|
||||||
|
|
||||||
|
success = "BECOME-SUCCESS-.+?"
|
||||||
|
|
||||||
|
task = {
|
||||||
|
"become_method": "community.general.run0",
|
||||||
|
"become_flags": run0_flags,
|
||||||
|
}
|
||||||
|
var_options = {}
|
||||||
|
cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe)
|
||||||
|
assert (
|
||||||
|
re.match(
|
||||||
|
f"{run0_exe} --user=root --nice=15 {default_exe} -c 'echo {success}; {default_cmd}'",
|
||||||
|
cmd,
|
||||||
|
)
|
||||||
|
is not None
|
||||||
|
)
|
Loading…
Reference in a new issue