mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2025-10-25 05:23:58 -07: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 | ||||
|   $becomes/pmrun.py: | ||||
|     maintainers: $team_ansible_core | ||||
|   $becomes/run0.py: | ||||
|     maintainers: konstruktoid | ||||
|   $becomes/sesu.py: | ||||
|     maintainers: nekonyuu | ||||
|   $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…
	
	Add table
		Add a link
		
	
		Reference in a new issue