-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add systemd run0 as a become method (#8306)
* add systemd run0 as a become method Signed-off-by: Thomas Sjögren <[email protected]> * add fragment Signed-off-by: Thomas Sjögren <[email protected]> * remove space after hyphen Signed-off-by: Thomas Sjögren <[email protected]> * replace ansible with collection version Signed-off-by: Thomas Sjögren <[email protected]> * update version_added and remove changelog fragment Signed-off-by: Thomas Sjögren <[email protected]> * update formating Signed-off-by: Thomas Sjögren <[email protected]> * add types Signed-off-by: Thomas Sjögren <[email protected]> * slim super() Signed-off-by: Thomas Sjögren <[email protected]> * imports must appear below docs Signed-off-by: Thomas Sjögren <[email protected]> * add initial unit test Signed-off-by: Thomas Sjögren <[email protected]> * update unit tests Signed-off-by: Thomas Sjögren <[email protected]> --------- Signed-off-by: Thomas Sjögren <[email protected]>
- Loading branch information
1 parent
3b7f13c
commit d347bf5
Showing
3 changed files
with
194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
) |