Read become password from pass
Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
		@@ -1,13 +1,15 @@
 | 
				
			|||||||
[defaults]
 | 
					[defaults]
 | 
				
			||||||
# Explicitely redefined some defaults to make play execution work
 | 
					# Explicitely redefined some defaults to make play execution work
 | 
				
			||||||
roles_path = ./roles
 | 
					roles_path = ./roles
 | 
				
			||||||
 | 
					vars_plugins = ./vars_plugins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inventory = ./hosts
 | 
					inventory = ./hosts
 | 
				
			||||||
timeout = 60
 | 
					timeout = 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[privilege_escalation]
 | 
					[privilege_escalation]
 | 
				
			||||||
become = True
 | 
					become = True
 | 
				
			||||||
become_ask_pass = True
 | 
					# Use a separate module to read passwords from pass
 | 
				
			||||||
 | 
					become_ask_pass = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[ssh_connection]
 | 
					[ssh_connection]
 | 
				
			||||||
pipelining = True
 | 
					pipelining = True
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								vars_plugins/__pycache__/pass.cpython-39.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vars_plugins/__pycache__/pass.cpython-39.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								vars_plugins/__pycache__/vault_cranspasswords.cpython-39.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vars_plugins/__pycache__/vault_cranspasswords.cpython-39.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6
									
								
								vars_plugins/pass.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vars_plugins/pass.ini
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					[pass]
 | 
				
			||||||
 | 
					# password_store_dir=/home/ynerant/.password-store
 | 
				
			||||||
 | 
					# crans_password_store_submodule=crans
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[pass_become]
 | 
				
			||||||
 | 
					all=templier
 | 
				
			||||||
							
								
								
									
										6
									
								
								vars_plugins/pass.ini.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vars_plugins/pass.ini.example
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					[pass]
 | 
				
			||||||
 | 
					# password_store_dir=/home/me/.password-store
 | 
				
			||||||
 | 
					# crans_password_store_submodule=crans
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[pass_become]
 | 
				
			||||||
 | 
					# all=mdp-root
 | 
				
			||||||
							
								
								
									
										102
									
								
								vars_plugins/pass.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vars_plugins/pass.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from functools import lru_cache
 | 
				
			||||||
 | 
					from getpass import getpass
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ansible.module_utils.six.moves import configparser
 | 
				
			||||||
 | 
					from ansible.plugins.vars import BaseVarsPlugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DOCUMENTATION = """
 | 
				
			||||||
 | 
					    module: pass
 | 
				
			||||||
 | 
					    vars: vault
 | 
				
			||||||
 | 
					    version_added: 2.9
 | 
				
			||||||
 | 
					    short_description: Load vault passwords from pass
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					        - Works exactly as a vault, loading variables from pass.
 | 
				
			||||||
 | 
					        - Decrypts the YAML file `ansible_vault` from cranspasswords.
 | 
				
			||||||
 | 
					        - Loads the secret variables.
 | 
				
			||||||
 | 
					        - Makes use of data caching in order to avoid calling cranspasswords multiple times.
 | 
				
			||||||
 | 
					        - Uses the local gpg key from the user running ansible on the Control node.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VarsModule(BaseVarsPlugin):
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    @lru_cache
 | 
				
			||||||
 | 
					    def decrypt_password(name, crans_submodule=False):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Passwords are decrypted from the local password store, then are cached.
 | 
				
			||||||
 | 
					        By that way, we don't decrypt these passwords everytime.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # Load config
 | 
				
			||||||
 | 
					        config = configparser.ConfigParser()
 | 
				
			||||||
 | 
					        config.read(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pass.ini'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        password_store = Path(config.get('pass', 'password_store_dir',
 | 
				
			||||||
 | 
					            fallback=os.getenv('PASSWORD_STORE_DIR', Path.home() / '.password-store')))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if crans_submodule:
 | 
				
			||||||
 | 
					            password_store /= config.get('pass', 'crans_password_store_submodule',
 | 
				
			||||||
 | 
					                    fallback=os.getenv('CRANS_PASSWORD_STORE_SUBMODULE', 'crans'))
 | 
				
			||||||
 | 
					        full_command = ['gpg', '-d', password_store / f'{name}.gpg']
 | 
				
			||||||
 | 
					        proc = subprocess.run(full_command, capture_output=True, close_fds=True)
 | 
				
			||||||
 | 
					        clear_text = proc.stdout.decode('UTF-8')
 | 
				
			||||||
 | 
					        sys.stderr.write(proc.stderr.decode('UTF-8'))
 | 
				
			||||||
 | 
					        return clear_text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    @lru_cache
 | 
				
			||||||
 | 
					    def become_password(entity):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Query the become password that should be used for the given entity.
 | 
				
			||||||
 | 
					        If entity is the whole group that has no default password,
 | 
				
			||||||
 | 
					        the become password will be prompted.
 | 
				
			||||||
 | 
					        The configuration should be given in pass.ini, in the `pass_become`
 | 
				
			||||||
 | 
					        group. You have only to write `group=pass-filename`.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # Load config
 | 
				
			||||||
 | 
					        config = configparser.ConfigParser()
 | 
				
			||||||
 | 
					        config.read(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pass.ini'))
 | 
				
			||||||
 | 
					        if config.has_option('pass_become', entity.get_name()):
 | 
				
			||||||
 | 
					            return VarsModule.decrypt_password(
 | 
				
			||||||
 | 
					                    config.get('pass_become', entity.get_name())).split('\n')[0]
 | 
				
			||||||
 | 
					        if entity.get_name() == "all":
 | 
				
			||||||
 | 
					            return getpass("BECOME password: ", stream=None)
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_vars(self, loader, path, entities):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Get all vars for entities, called by Ansible.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loader: Ansible's DataLoader.
 | 
				
			||||||
 | 
					        path: Current play's playbook directory.
 | 
				
			||||||
 | 
					        entities: Host or group names pertinent to the variables needed.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # VarsModule objects are called every time you need host vars, per host,
 | 
				
			||||||
 | 
					        # and per group the host is part of.
 | 
				
			||||||
 | 
					        # It is about 6 times per host per task in current state
 | 
				
			||||||
 | 
					        # of Ansible Crans configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # It is way to much.
 | 
				
			||||||
 | 
					        # So we cache the data into the DataLoader (see parsing/DataLoader).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        passwords = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for entity in entities:
 | 
				
			||||||
 | 
					            # Load vault passwords
 | 
				
			||||||
 | 
					            if entity.get_name() == 'all':
 | 
				
			||||||
 | 
					                passwords['vault'] = loader.load(
 | 
				
			||||||
 | 
					                        VarsModule.decrypt_password('ansible_vault', True))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Load become password
 | 
				
			||||||
 | 
					            become_password = VarsModule.become_password(entity)
 | 
				
			||||||
 | 
					            if become_password is not None:
 | 
				
			||||||
 | 
					                passwords['ansible_become_password'] = become_password
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return passwords
 | 
				
			||||||
		Reference in New Issue
	
	Block a user