From 624f25b78d20b10b551e779f4921e08f36463be1 Mon Sep 17 00:00:00 2001 From: Frederic Boismenu Date: Tue, 17 Jan 2017 16:51:07 -0500 Subject: [PATCH 01/59] Fix for #38697 --- salt/modules/win_ip.py | 86 +++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index 4729099b5c1f..e06b3b3c747d 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -41,65 +41,57 @@ def _interface_configs(): ''' cmd = ['netsh', 'interface', 'ip', 'show', 'config'] lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - iface = '' - ip = 0 - dns_flag = None - wins_flag = None ret = {} + current_iface = None + current_ip_list = None + for line in lines: - if dns_flag: - try: - socket.inet_aton(line.strip()) - ret[iface][dns_flag].append(line.strip()) - dns_flag = None - continue - except socket.error as exc: - dns_flag = None - if wins_flag: - try: - socket.inet_aton(line.strip()) - ret[iface][wins_flag].append(line.strip()) - wins_flag = None - continue - except socket.error as exc: - wins_flag = None + + line = line.strip() if not line: - iface = '' + current_iface = None + current_ip_list = None continue + if 'Configuration for interface' in line: _, iface = line.rstrip('"').split('"', 1) # get iface name - ret[iface] = {} - ip = 0 - continue - try: - key, val = line.split(':', 1) - except ValueError as exc: - log.debug('Could not split line. Error was {0}.'.format(exc)) + current_iface = {} + ret[iface] = current_iface continue - if 'DNS Servers' in line: - dns_flag = key.strip() - ret[iface][key.strip()] = [val.strip()] - continue - if 'WINS Servers' in line: - wins_flag = key.strip() - ret[iface][key.strip()] = [val.strip()] - continue - if 'IP Address' in key: - if 'ip_addrs' not in ret[iface]: - ret[iface]['ip_addrs'] = [] - ret[iface]['ip_addrs'].append(dict([(key.strip(), val.strip())])) - continue - if 'Subnet Prefix' in key: - subnet, _, netmask = val.strip().split(' ', 2) - ret[iface]['ip_addrs'][ip]['Subnet'] = subnet.strip() - ret[iface]['ip_addrs'][ip]['Netmask'] = netmask.lstrip().rstrip(')') - ip = ip + 1 + + if ':' not in line: + if current_ip_list: + current_ip_list.append( line ) + else: + log.warning( 'Cannot parse "{0}"'.format(line) ) continue + + key, val = line.split(':', 1) + key = key.strip() + val = val.strip() + + lkey = key.lower() + if ('dns servers' in lkey) or ('wins servers' in lkey): + current_ip_list = [] + current_iface[key] = current_ip_list + current_ip_list.append( val ) + + elif 'ip address' in lkey: + current_iface.setdefault( 'ip_addrs', [] ).append( {key:val} ) + + elif 'subnet prefix' in lkey: + subnet, _, netmask = val.split(' ', 2) + last_ip = current_iface['ip_addrs'][-1] + last_ip['Subnet'] = subnet.strip() + last_ip['Netmask'] = netmask.lstrip().rstrip(')') + else: - ret[iface][key.strip()] = val.strip() + current_iface[key] = val + return ret + def raw_interface_configs(): ''' Return raw configs for all interfaces From 6b5a7f4b6481b4cfe0e98d1c2b529d510e8a3c56 Mon Sep 17 00:00:00 2001 From: Junya Hu Date: Sun, 22 Jan 2017 15:12:32 +0800 Subject: [PATCH 02/59] fix django auth not work --- salt/auth/django.py | 30 +++++++++++++++++++++++++----- salt/config/__init__.py | 6 ++++++ salt/master.py | 4 ++-- salt/netapi/rest_cherrypy/app.py | 19 +++++++++++-------- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/salt/auth/django.py b/salt/auth/django.py index 5962e3be1fab..feb3c40780f2 100644 --- a/salt/auth/django.py +++ b/salt/auth/django.py @@ -50,6 +50,10 @@ class SaltExternalAuthModel(models.Model): # Import python libs from __future__ import absolute_import import logging +import os +import sys + +from django.db import connection # Import 3rd-party libs import salt.ext.six as six @@ -77,6 +81,15 @@ def __virtual__(): return False +def is_connection_usable(): + try: + connection.connection.ping() + except: + return False + else: + return True + + def django_auth_setup(): ''' Prepare the connection to the Django authentication framework @@ -95,21 +108,28 @@ def django_auth_setup(): django_model_name = django_model_fullname.split('.')[-1] django_module_name = '.'.join(django_model_fullname.split('.')[0:-1]) - __import__(django_module_name, globals(), locals(), 'SaltExternalAuthModel') + django_auth_module = __import__(django_module_name, globals(), locals(), 'SaltExternalAuthModel') DJANGO_AUTH_CLASS_str = 'django_auth_module.{0}'.format(django_model_name) DJANGO_AUTH_CLASS = eval(DJANGO_AUTH_CLASS_str) # pylint: disable=W0123 - if django.VERSION >= (1, 7): - django.setup() - def auth(username, password): ''' Simple Django auth ''' - import django.contrib.auth # pylint: disable=import-error + sys.path.append(__opts__['django_auth_path']) + os.environ.setdefault('DJANGO_SETTINGS_MODULE', __opts__['django_auth_settings']) + + import django + if django.VERSION >= (1, 7): + django.setup() django_auth_setup() + + if not is_connection_usable(): + connection.close() + + import django.contrib.auth # pylint: disable=import-error user = django.contrib.auth.authenticate(username=username, password=password) if user is not None: if user.is_active: diff --git a/salt/config/__init__.py b/salt/config/__init__.py index 63c78f715659..ea0ef1022c28 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -928,6 +928,10 @@ def _gather_buffer_space(): # Note: to set enum arguments values like `cert_reqs` and `ssl_version` use constant names # without ssl module prefix: `CERT_REQUIRED` or `PROTOCOL_SSLv23`. 'ssl': (dict, type(None)), + + # django auth + 'django_auth_path': str, + 'django_auth_settings': str, } # default configurations @@ -1435,6 +1439,8 @@ def _gather_buffer_space(): 'cache': 'localfs', 'thin_extra_mods': '', 'ssl': None, + 'django_auth_path': '', + 'django_auth_settings': '', } diff --git a/salt/master.py b/salt/master.py index 9c8020e70f8b..671684e4c247 100644 --- a/salt/master.py +++ b/salt/master.py @@ -1917,7 +1917,7 @@ def mk_token(self, clear_load): name = self.loadauth.load_name(clear_load) groups = self.loadauth.get_groups(clear_load) eauth_config = self.opts['external_auth'][clear_load['eauth']] - if '*' not in eauth_config and name not in eauth_config: + if '^model' not in eauth_config and '*' not in eauth_config and name not in eauth_config: found = False for group in groups: if "{0}%".format(group) in eauth_config: @@ -2017,7 +2017,7 @@ def publish(self, clear_load): break except KeyError: pass - if '*' not in eauth_users and token['name'] not in eauth_users \ + if '^model' not in eauth_users and '*' not in eauth_users and token['name'] not in eauth_users \ and not group_auth_match: log.warning('Authentication failure of type "token" occurred.') return '' diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index 265f79cdd500..133aed8a3a80 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -1687,16 +1687,19 @@ def POST(self, **kwargs): try: eauth = self.opts.get('external_auth', {}).get(token['eauth'], {}) - # Get sum of '*' perms, user-specific perms, and group-specific perms - perms = eauth.get(token['name'], []) - perms.extend(eauth.get('*', [])) + if token['eauth'] == 'django' and '^model' in eauth: + perms = token['auth_list'] + else: + # Get sum of '*' perms, user-specific perms, and group-specific perms + perms = eauth.get(token['name'], []) + perms.extend(eauth.get('*', [])) - if 'groups' in token and token['groups']: - user_groups = set(token['groups']) - eauth_groups = set([i.rstrip('%') for i in eauth.keys() if i.endswith('%')]) + if 'groups' in token and token['groups']: + user_groups = set(token['groups']) + eauth_groups = set([i.rstrip('%') for i in eauth.keys() if i.endswith('%')]) - for group in user_groups & eauth_groups: - perms.extend(eauth['{0}%'.format(group)]) + for group in user_groups & eauth_groups: + perms.extend(eauth['{0}%'.format(group)]) if not perms: logger.debug("Eauth permission list not found.") From 933ebf15d770a5cfae972a9a4090b7a616327512 Mon Sep 17 00:00:00 2001 From: Junya Hu Date: Mon, 23 Jan 2017 15:22:22 +0800 Subject: [PATCH 03/59] fix pylint violations --- salt/auth/django.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/salt/auth/django.py b/salt/auth/django.py index feb3c40780f2..37327fb96ee3 100644 --- a/salt/auth/django.py +++ b/salt/auth/django.py @@ -53,13 +53,13 @@ class SaltExternalAuthModel(models.Model): import os import sys -from django.db import connection # Import 3rd-party libs import salt.ext.six as six # pylint: disable=import-error try: import django + from django.db import connection HAS_DJANGO = True except Exception as exc: # If Django is installed and is not detected, uncomment @@ -84,7 +84,7 @@ def __virtual__(): def is_connection_usable(): try: connection.connection.ping() - except: + except Exception: return False else: return True @@ -94,6 +94,9 @@ def django_auth_setup(): ''' Prepare the connection to the Django authentication framework ''' + if django.VERSION >= (1, 7): + django.setup() + global DJANGO_AUTH_CLASS if DJANGO_AUTH_CLASS is not None: @@ -120,10 +123,6 @@ def auth(username, password): sys.path.append(__opts__['django_auth_path']) os.environ.setdefault('DJANGO_SETTINGS_MODULE', __opts__['django_auth_settings']) - import django - if django.VERSION >= (1, 7): - django.setup() - django_auth_setup() if not is_connection_usable(): From 7118eff034aef26e36f79c78bdcfb0fd1466bc95 Mon Sep 17 00:00:00 2001 From: Mircea Ulinic Date: Tue, 31 Jan 2017 15:51:20 +0000 Subject: [PATCH 04/59] Avoid KeyError: 'multiprocessing' Fix for #39059 --- salt/utils/minion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/utils/minion.py b/salt/utils/minion.py index 2df7a30095c3..38267d7cb8e1 100644 --- a/salt/utils/minion.py +++ b/salt/utils/minion.py @@ -81,7 +81,7 @@ def _read_proc_file(path, opts): except IOError: pass return None - if opts['multiprocessing']: + if opts.get('multiprocessing'): if data.get('pid') == pid: return None else: From 5e94a30a34ea695104a39a90e2aa95766a455c0b Mon Sep 17 00:00:00 2001 From: Ethan Moore Date: Thu, 15 Sep 2016 15:02:49 +0000 Subject: [PATCH 05/59] add additional static policies to computer configuration policy class duplicate code cleanup/misc code efficiencies --- salt/modules/win_lgpo.py | 1623 ++++++++++++++++++++++++-------------- 1 file changed, 1051 insertions(+), 572 deletions(-) diff --git a/salt/modules/win_lgpo.py b/salt/modules/win_lgpo.py index a8cbfd25be5a..9c39b3df6ce7 100644 --- a/salt/modules/win_lgpo.py +++ b/salt/modules/win_lgpo.py @@ -216,6 +216,162 @@ class _policy_info(object): in the GUI ''' def __init__(self): + self.audit_lookup = { + 0: 'No auditing', + 1: 'Success', + 2: 'Failure', + 3: 'Success, Failure', + 'Not Defined': 'Not Defined', + None: 'Not Defined', + } + self.sc_removal_lookup = { + 0: 'No Action', + 1: 'Lock Workstation', + 2: 'Force Logoff', + 3: 'Disconnect if a Remote Desktop Services session', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.uac_admin_prompt_lookup = { + 0: 'Elevate without prompting', + 1: 'Prompt for credentials on the secure desktop', + 2: 'Prompt for consent on the secure desktop', + 3: 'Prompt for credentials', + 4: 'Prompt for consent', + 5: 'Prompt for consent for non-Windows binaries', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.uac_user_prompt_lookup = { + 0: 'Automatically deny elevation requests', + 1: 'Prompt for credentials on the secure desktop', + 3: 'Prompt for credentials', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.enabled_one_disabled_zero = { + 0: 'Disabled', + 1: 'Enabled', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.enabled_one_disabled_zero_transform = { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.enabled_one_disabled_zero, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.enabled_one_disabled_zero, + 'value_lookup': True, + }, + } + self.s4u2self_options = { + 0: 'Default', + 1: 'Enabled', + 2: 'Disabled', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.audit_transform = { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.audit_lookup, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.audit_lookup, + 'value_lookup': True, + }, + } + self.enabled_one_disabled_zero_strings = { + '0': 'Disabled', + '1': 'Enabled', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.enabled_one_disabled_zero_strings_transform = { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.enabled_one_disabled_zero_strings, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.enabled_one_disabled_zero_strings, + 'value_lookup': True, + }, + } + self.security_options_gpedit_path = [ + 'Computer Configuration', + 'Windows Settings', + 'Security Settings', + 'Local Policies', + 'Security Options' + ] + self.password_policy_gpedit_path = [ + 'Computer Configuration', + 'Windows Settings', + 'Security Settings', + 'Account Policies', + 'Password Policy' + ] + self.audit_policy_gpedit_path = [ + 'Computer Configuration', + 'Windows Settings', + 'Security Settings', + 'Local Policies', + 'Audit Policy' + ] + self.account_lockout_policy_gpedit_path = [ + 'Computer Configuration', + 'Windows Settings', + 'Security Settings', + 'Account Policies', + 'Account Lockout Policy' + ] + self.user_rights_assignment_gpedit_path = [ + 'Computer Configuration', + 'Windows Settings', + 'Security Settings', + 'Local Policies', + 'User Rights Assignment' + ] + self.block_ms_accounts = { + 0: 'This policy is disabled', + 1: 'Users can\'t add Microsoft accounts', + 3: 'Users can\'t add or log on with Microsoft accounts', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.ldap_server_signing_requirements = { + 1: 'None', + 2: 'Require signing', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.smb_server_name_hardening_levels = { + 0: 'Off', + 1: 'Accept if provided by client', + 2: 'Require from client', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.locked_session_user_info = { + 1: 'User display name, domain and user names', + 2: 'User display name only', + 3: 'Do not display user information', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } + self.force_guest = { + 0: 'Classic - local users authenticate as themselves', + 1: 'Guest only - local users authenticate as Guest', + None: 'Not Defined', + '(value not set)': 'Not Defined', + } self.policies = { 'Machine': { 'lgpo_section': 'Computer Configuration', @@ -328,32 +484,142 @@ def __init__(self): 'Put': '_powershell_script_order_reverse_conversion', }, }, + 'LSAAnonymousNameLookup': { + 'Policy': 'Network access: Allow anonymous SID/Name translation', + 'lgpo_section': self.password_policy_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), + 'Secedit': { + 'Option': 'LSAAnonymousNameLookup', + 'Section': 'System Access', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'RestrictAnonymousSam': { + 'Policy': 'Network access: Do not allow anonymous enumeration of SAM accounts', + 'lgpo_section': self.security_options_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', + 'Value': 'RestrictAnonymousSam', + 'Type': 'REG_DWORD' + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, 'RestrictAnonymous': { - 'Policy': 'Network Access: Do not allow anonymous enumeration of SAM accounts and shares', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], - 'Settings': [0, 1], + 'Policy': 'Network access: Do not allow anonymous enumeration of SAM accounts and shares', + 'lgpo_section': self.security_options_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', 'Value': 'RestrictAnonymous', 'Type': 'REG_DWORD' }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'DisableDomainCreds': { + 'Policy': 'Network access: Do not allow storage of passwords and credentials for network authentication', + 'lgpo_section': self.security_options_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', + 'Value': 'DisableDomainCreds', + 'Type': 'REG_DWORD' + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'EveryoneIncludesAnonymous': { + 'Policy': 'Network access: Let Everyone permissions apply to anonymous users', + 'lgpo_section': self.security_options_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', + 'Value': 'everyoneincludesanonymous', + 'Type': 'REG_DWORD' + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'NullSessionPipes': { + 'Policy': 'Network access: Named Pipes that can be accessed anonymously', + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'NullSessionPipes', + 'Type': 'REG_MULTI_SZ' + }, + }, + 'RemoteRegistryExactPaths': { + 'Policy': 'Network access: Remotely accessible registry paths', + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\SecurePipeServers\\winreg\\AllowedExactPaths', + 'Value': 'Machine', + 'Type': 'REG_MULTI_SZ' + }, + }, + 'RemoteRegistryPaths': { + 'Policy': 'Network access: Remotely accessible registry paths and sub-paths', + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\SecurePipeServers\\winreg\\AllowedPaths', + 'Value': 'Machine', + 'Type': 'REG_MULTI_SZ' + }, + }, + 'RestrictNullSessAccess': { + 'Policy': 'Network access: Restrict anonymous access to Named Pipes and Shares', + 'lgpo_section': self.security_options_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'RestrictNullSessAccess', + 'Type': 'REG_DWORD' + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'NullSessionShares': { + 'Policy': 'Network access: Shares that can be accessed anonymously', + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'NullSessionShares', + 'Type': 'REG_MULTI_SZ' + }, + }, + 'ForceGuest': { + 'Policy': 'Network access: Sharing and security model for local accounts', + 'lgpo_section': self.security_options_gpedit_path, + 'Settings': self.force_guest.keys(), + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', + 'Value': 'ForceGuest', + 'Type': 'REG_DWORD' + }, 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.force_guest, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.force_guest, + 'value_lookup': True, + }, }, }, 'PasswordHistory': { 'Policy': 'Enforce password history', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Password Policy'], + 'lgpo_section': self.password_policy_gpedit_path, 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 24} @@ -365,11 +631,7 @@ def __init__(self): }, 'MaxPasswordAge': { 'Policy': 'Maximum password age', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Password Policy'], + 'lgpo_section': self.password_policy_gpedit_path, 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 86313600} @@ -385,11 +647,7 @@ def __init__(self): }, 'MinPasswordAge': { 'Policy': 'Minimum password age', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Password Policy'], + 'lgpo_section': self.password_policy_gpedit_path, 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 86313600} @@ -405,11 +663,7 @@ def __init__(self): }, 'MinPasswordLen': { 'Policy': 'Minimum password length', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Password Policy'], + 'lgpo_section': self.password_policy_gpedit_path, 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 14} @@ -421,99 +675,83 @@ def __init__(self): }, 'PasswordComplexity': { 'Policy': 'Passwords must meet complexity requirements', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Password Policy'], - 'Settings': [0, 1], + 'lgpo_section': self.password_policy_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), 'Secedit': { 'Option': 'PasswordComplexity', 'Section': 'System Access', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - }, + 'Transform': self.enabled_one_disabled_zero_transform, }, 'ClearTextPasswords': { 'Policy': 'Store passwords using reversible encryption', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Password Policy'], - 'Settings': [0, 1], + 'lgpo_section': self.password_policy_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), 'Secedit': { 'Option': 'ClearTextPassword', 'Section': 'System Access', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - }, + 'Transform': self.enabled_one_disabled_zero_transform, }, 'AdminAccountStatus': { 'Policy': 'Accounts: Administrator account status', - 'Settings': [0, 1], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Secedit': { 'Option': 'EnableAdminAccount', 'Section': 'System Access', }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'NoConnectedUser': { + 'Policy': 'Accounts: Block Microsoft accounts', + 'Settings': self.block_ms_accounts.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Value': 'NoConnectedUser', + 'Type': 'REG_DWORD', + }, 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.block_ms_accounts, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.block_ms_accounts, + 'value_lookup': True, + }, }, }, 'GuestAccountStatus': { 'Policy': 'Accounts: Guest account status', - 'Settings': [0, 1], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Secedit': { 'Option': 'EnableGuestAccount', 'Section': 'System Access', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - }, + 'Transform': self.enabled_one_disabled_zero_transform, }, 'LimitBlankPasswordUse': { 'Policy': 'Accounts: Limit local account use of blank passwords to console logon only', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], - 'Settings': [0, 1], + 'lgpo_section': self.security_options_gpedit_path, + 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', 'Value': 'limitblankpassworduse', 'Type': 'REG_DWORD', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - }, + 'Transform': self.enabled_one_disabled_zero_transform, }, 'RenameAdministratorAccount': { 'Policy': 'Accounts: Rename administrator account', 'Settings': None, - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'lgpo_section': self.security_options_gpedit_path, 'Secedit': { 'Option': 'NewAdministratorName', 'Section': 'System Access', @@ -526,11 +764,7 @@ def __init__(self): 'RenameGuestAccount': { 'Policy': 'Accounts: Rename guest account', 'Settings': None, - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'lgpo_section': self.security_options_gpedit_path, 'Secedit': { 'Option': 'NewGuestName', 'Section': 'System Access', @@ -542,50 +776,476 @@ def __init__(self): }, 'AuditBaseObjects': { 'Policy': 'Audit: Audit the access of global system objects', - 'Settings': [0, 1], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', 'Value': 'AuditBaseObjects', 'Type': 'REG_DWORD', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', + 'Transform': self.enabled_one_disabled_zero_transform + }, + 'SceNoApplyLegacyAuditPolicy': { + 'Policy': 'Audit: Force audit policy subcategoy settings (Windows Vista or later) to override audit policy category settings', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', + 'Value': 'AuditBaseObjects', + 'Type': 'REG_DWORD', }, + 'Transform': self.enabled_one_disabled_zero_transform }, - 'DoNotDisplayLastUserName': { + 'DontDisplayLastUserName': { 'Policy': 'Interactive logon: Do not display last user name', - 'Settings': [0, 1], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'Value': 'DontDisplayLastUserName', 'Type': 'REG_DWORD', }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'CachedLogonsCount': { + 'Policy': 'Interactive logon: Number of previous logons to cache (in case domain controller is not available)', + 'Settings': { + 'Function': '_in_range_inclusive', + 'Args': {'min': 0, 'max': 50} + }, + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Value': 'CachedLogonsCount', + 'Type': 'REG_SZ', + }, + }, + 'ForceUnlockLogon': { + 'Policy': 'Interactive logon: Require Domain Controller authentication to unlock workstation', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Value': 'ForceUnlockLogon', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'ScRemoveOption': { + 'Policy': 'Interactive logon: Smart card removal behavior', + 'Settings': self.sc_removal_lookup.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Value': 'ScRemoveOption', + 'Type': 'REG_DWORD', + }, 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.sc_removal_lookup, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.sc_removal_lookup, + 'value_lookup': True, + }, }, }, + 'DisableCAD': { + 'Policy': 'Interactive logon: Do not require CTRL+ALT+DEL', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'DisableCAD', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'FilterAdministratorToken': { + 'Policy': 'User Account Control: Admin Approval Mode for the built-in Administrator account', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'FilterAdministratorToken', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'EnableUIADesktopToggle': { + 'Policy': 'User Account Control: Allow UIAccess applications to prompt for elevation without using the secure desktop', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'EnableUIADesktopToggle', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'ConsentPromptBehaviorAdmin': { + 'Policy': 'User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode', + 'Settings': self.uac_admin_prompt_lookup.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'ConsentPromptBehaviorAdmin', + 'Type': 'REG_DWORD', + }, + 'Transform': { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.uac_admin_prompt_lookup, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.uac_admin_prompt_lookup, + 'value_lookup': True, + }, + }, + }, + 'ConsentPromptBehaviorUser': { + 'Policy': 'User Account Control: Behavior of the elevation prompt for standard users', + 'Settings': self.uac_user_prompt_lookup.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'ConsentPromptBehaviorUser', + 'Type': 'REG_DWORD', + }, + 'Transform': { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.uac_user_prompt_lookup, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.uac_user_prompt_lookup, + 'value_lookup': True, + }, + }, + }, + 'EnableInstallerDetection': { + 'Policy': 'User Account Control: Detect application installations and prompt for elevation', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'EnableInstallerDetection', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'ValidateAdminCodeSignatures': { + 'Policy': 'User Account Control: Only elevate executables that are signed and validated', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'ValidateAdminCodeSignatures', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'EnableSecureUIAPaths': { + 'Policy': 'User Account Control: Only elevate UIAccess applicaitons that are installed in secure locations', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'EnableSecureUIAPaths', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'EnableLUA': { + 'Policy': 'User Account Control: Run all administrators in Admin Approval Mode', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'EnableLUA', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'PromptOnSecureDesktop': { + 'Policy': 'User Account Control: Switch to the secure desktop when prompting for elevation', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'PromptOnSecureDesktop', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'EnableVirtualization': { + 'Policy': 'User Account Control: Virtualize file and registry write failures to per-user locations', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'EnableVirtualization', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'PasswordExpiryWarning': { + 'Policy': 'Interactive logon: Prompt user to change password before expiration', + 'Settings': { + 'Function': '_in_range_inclusive', + 'Args': {'min': 0, 'max': 999} + }, + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Value': 'PasswordExpiryWarning', + 'Type': 'REG_DWORD', + }, + }, + 'MaxDevicePasswordFailedAttempts': { + 'Policy': 'Interactive logon: Machine account lockout threshold', + 'Settings': { + 'Function': '_in_range_inclusive', + 'Args': {'min': 0, 'max': 999} + }, + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Value': 'MaxDevicePasswordFailedAttempts', + 'Type': 'REG_DWORD', + }, + }, + 'InactivityTimeoutSecs': { + 'Policy': 'Interactive logon: Machine inactivity limit', + 'Settings': { + 'Function': '_in_range_inclusive', + 'Args': {'min': 0, 'max': 599940} + }, + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Value': 'InactivityTimeoutSecs', + 'Type': 'REG_DWORD', + }, + }, + 'legalnoticetext': { + 'Policy': 'Interactive logon: Message text for users attempting to log on', + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Value': 'legalnoticetext', + 'Type': 'REG_SZ', + }, + }, + 'legalnoticecaption': { + 'Policy': 'Interactive logon: Message title for users attempting to log on', + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Value': 'legalnoticecaption', + 'Type': 'REG_SZ', + }, + }, + 'DontDisplayLockedUserId': { + 'Policy': 'Interactive logon: Display user information when the session is locked', + 'Settings': self.locked_session_user_info.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Value': 'DontDisplayLockedUserId', + 'Type': 'REG_DWORD', + }, + 'Transform': { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.locked_session_user_info, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.locked_session_user_info, + 'value_lookup': True, + }, + } + }, + 'ScForceOption': { + 'Policy': 'Interactive logon: Require smart card', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Value': 'ScForceOption', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'Client_RequireSecuritySignature': { + 'Policy': 'Microsoft network client: Digitally sign communications (always)', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters', + 'Value': 'RequireSecuritySignature', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'Client_EnableSecuritySignature': { + 'Policy': 'Microsoft network client: Digitally sign communications (if server agrees)', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters', + 'Value': 'EnableSecuritySignature', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'EnablePlainTextPassword': { + 'Policy': 'Microsoft network client: Send unencrypted password to third-party SMB servers', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters', + 'Value': 'EnablePlainTextPassword', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'AutoDisconnect': { + 'Policy': 'Microsoft network server: Amount of idle time required before suspending session', + 'Settings': { + 'Function': '_in_range_inclusive', + 'Args': {'min': 0, 'max': 99999} + }, + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'AutoDisconnect', + 'Type': 'REG_DWORD', + }, + }, + 'EnableS4U2SelfForClaims': { + 'Policy': 'Microsoft network server: Attempt S4U2Self to obtain claim information', + 'Settings': self.s4u2self_options.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'EnableS4U2SelfForClaims', + 'Type': 'REG_DWORD', + }, + 'Transform': { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.s4u2self_options, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.s4u2self_options, + 'value_lookup': True, + }, + }, + }, + 'Server_RequireSecuritySignature': { + 'Policy': 'Microsoft network server: Digitally sign communications (always)', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'RequireSecuritySignature', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'Server_EnableSecuritySignature': { + 'Policy': 'Microsoft network server: Digitally sign communications (if client agrees)', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'EnableSecuritySignature', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'EnableForcedLogoff': { + 'Policy': 'Microsoft network server: Disconnect clients when logon hours expire', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'EnableForcedLogoff', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'SmbServerNameHardeningLevel': { + 'Policy': 'Microsoft network server: Server SPN target name validation level', + 'Settings': self.smb_server_name_hardening_levels.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Value': 'SmbServerNameHardeningLevel', + 'Type': 'REG_DWORD', + }, + 'Transform': { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.smb_server_name_hardening_levels, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.smb_server_name_hardening_levels, + 'value_lookup': True, + }, + } + }, 'FullPrivilegeAuditing': { 'Policy': 'Audit: Audit the use of Backup and Restore privilege', 'Settings': [chr(0), chr(1)], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'System\\CurrentControlSet\\Control\\Lsa', @@ -599,50 +1259,32 @@ def __init__(self): }, 'CrashOnAuditFail': { 'Policy': 'Audit: Shut down system immediately if unable to log security audits', - 'Settings': [0, 1], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', 'Value': 'CrashOnAuditFail', 'Type': 'REG_DWORD', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - }, + 'Transform': self.enabled_one_disabled_zero_transform, }, 'UndockWithoutLogon': { 'Policy': 'Devices: Allow undock without having to log on', - 'Settings': [0, 1], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'Value': 'UndockWithoutLogon', 'Type': 'REG_DWORD', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - }, + 'Transform': self.enabled_one_disabled_zero_transform, }, 'AllocateDASD': { 'Policy': 'Devices: Allowed to format and eject removable media', 'Settings': ["", "0", "1", "2"], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', @@ -656,53 +1298,33 @@ def __init__(self): }, 'AllocateCDRoms': { 'Policy': 'Devices: Restrict CD-ROM access to locally logged-on user only', - 'Settings': ["0", "1"], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', 'Value': 'AllocateCDRoms', 'Type': 'REG_SZ', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - 'PutArgs': {'return_string': True} - }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'AllocateFloppies': { 'Policy': 'Devices: Restrict floppy access to locally logged-on user only', - 'Settings': ["0", "1"], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', 'Value': 'AllocateFloppies', 'Type': 'REG_SZ', }, - 'Transform': { - 'Get': '_enable_one_disable_zero_conversion', - 'Put': '_enable_one_disable_zero_reverse_conversion', - 'PutArgs': {'return_string': True} - }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, }, # see KB298503 why we aren't just doing this one via the registry 'DriverSigningPolicy': { 'Policy': 'Devices: Unsigned driver installation behavior', 'Settings': ['3,0', '3,' + chr(1), '3,' + chr(2)], - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options'], + 'lgpo_section': self.security_options_gpedit_path, 'Secedit': { 'Option': 'MACHINE\\Software\\Microsoft\\Driver Signing\\Policy', 'Section': 'Registry Values', @@ -712,13 +1334,130 @@ def __init__(self): 'Put': '_driver_signing_reg_reverse_conversion', }, }, + 'SubmitControl': { + 'Policy': 'Domain controller: Allow server operators to schedule tasks', + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Control\\Lsa', + 'Value': 'SubmitControl', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, + }, + 'LDAPServerIntegrity': { + 'Policy': 'Domain controller: LDAP server signing requirements', + 'Settings': self.ldap_server_signing_requirements.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\NTDS\\Parameters', + 'Value': 'LDAPServerIntegrity', + 'Type': 'REG_DWORD', + }, + 'Transform': { + 'Get': '_dict_lookup', + 'Put': '_dict_lookup', + 'GetArgs': { + 'lookup': self.ldap_server_signing_requirements, + 'value_lookup': False, + }, + 'PutArgs': { + 'lookup': self.ldap_server_signing_requirements, + 'value_lookup': True, + }, + } + }, + 'RefusePasswordChange': { + 'Policy': 'Domain controller: Refuse machine account password changes', + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Value': 'RefusePasswordChange', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, + }, + 'RequireSignOrSeal': { + 'Policy': 'Domain member: Digitally encrypt or sign secure channel data (always)', + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Value': 'RequireSignOrSeal', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, + }, + 'SealSecureChannel': { + 'Policy': 'Domain member: Digitally encrypt secure channel data (when possible)', + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Value': 'SealSecureChannel', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, + }, + 'SignSecureChannel': { + 'Policy': 'Domain member: Digitally sign secure channel data (when possible)', + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Value': 'SignSecureChannel', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, + }, + 'DisablePasswordChange': { + 'Policy': 'Domain member: Disable machine account password changes', + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Value': 'DisablePasswordChange', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, + }, + 'MaximumPasswordAge': { + 'Policy': 'Domain member: Maximum machine account password age', + 'Settings': { + 'Function': '_in_range_inclusive', + 'Args': {'min': 0, 'max': 999} + }, + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Value': 'MaximumPasswordAge', + 'Type': 'REG_DWORD', + }, + }, + 'RequireStrongKey': { + 'Policy': 'Domain member: Require strong (Windows 2000 or later) session key', + 'Settings': self.enabled_one_disabled_zero_strings.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Value': 'RequireStrongKey', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_strings_transform, + }, 'LockoutDuration': { 'Policy': 'Account lockout duration', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Account Lockout Policy'], + 'lgpo_section': self.account_lockout_policy_gpedit_path, 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 6000000} @@ -734,11 +1473,7 @@ def __init__(self): }, 'LockoutThreshold': { 'Policy': 'Account lockout threshold', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Account Lockout Policy'], + 'lgpo_section': self.account_lockout_policy_gpedit_path, 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 1000} @@ -750,11 +1485,7 @@ def __init__(self): }, 'LockoutWindow': { 'Policy': 'Reset account lockout counter after', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Account Lockout Policy'], + 'lgpo_section': self.account_lockout_policy_gpedit_path, 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 6000000} @@ -770,164 +1501,97 @@ def __init__(self): }, 'AuditAccountLogon': { 'Policy': 'Audit account logon events', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Account Lockout Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditAccountLogon', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditAccountManage': { 'Policy': 'Audit account management', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditAccountManage', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditDSAccess': { 'Policy': 'Audit directory service access', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditDSAccess', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditLogonEvents': { 'Policy': 'Audit logon events', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditLogonEvents', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditObjectAccess': { 'Policy': 'Audit object access', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditObjectAccess', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditPolicyChange': { 'Policy': 'Audit policy change', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditPolicyChange', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditPrivilegeUse': { 'Policy': 'Audit privilege use', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditPrivilegeUse', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditProcessTracking': { 'Policy': 'Audit process tracking', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditProcessTracking', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'AuditSystemEvents': { 'Policy': 'Audit system events', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy'], - 'Settings': [0, 1, 2, 3], + 'lgpo_section': self.audit_policy_gpedit_path, + 'Settings': self.audit_lookup.keys(), 'Secedit': { 'Option': 'AuditSystemEvents', 'Section': 'Event Audit', }, - 'Transform': { - 'Get': '_event_audit_conversion', - 'Put': '_event_audit_reverse_conversion', - }, + 'Transform': self.audit_transform, }, 'SeTrustedCredManAccessPrivilege': { 'Policy': 'Access Credential Manager as a trusted caller', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeTrustedCredManAccessPrivilege' @@ -939,11 +1603,7 @@ def __init__(self): }, 'SeNetworkLogonRight': { 'Policy': 'Access this computer from the network', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeNetworkLogonRight' @@ -955,11 +1615,7 @@ def __init__(self): }, 'SeTcbPrivilege': { 'Policy': 'Act as part of the operating system', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeTcbPrivilege' @@ -971,11 +1627,7 @@ def __init__(self): }, 'SeMachineAccountPrivilege': { 'Policy': 'Add workstations to domain', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeMachineAccountPrivilege' @@ -987,11 +1639,7 @@ def __init__(self): }, 'SeIncreaseQuotaPrivilege': { 'Policy': 'Adjust memory quotas for a process', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeIncreaseQuotaPrivilege' @@ -1002,12 +1650,8 @@ def __init__(self): }, }, 'SeInteractiveLogonRight': { - 'Policy': 'Allow logon locally', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'Policy': 'Allow log on locally', + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeInteractiveLogonRight' @@ -1018,12 +1662,8 @@ def __init__(self): }, }, 'SeRemoteInteractiveLogonRight': { - 'Policy': 'Allow logon through Remote Desktop Services', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'Policy': 'Allow log on through Remote Desktop Services', + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeRemoteInteractiveLogonRight' @@ -1035,11 +1675,7 @@ def __init__(self): }, 'SeBackupPrivilege': { 'Policy': 'Backup files and directories', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeBackupPrivilege' @@ -1051,11 +1687,7 @@ def __init__(self): }, 'SeChangeNotifyPrivilege': { 'Policy': 'Bypass traverse checking', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeChangeNotifyPrivilege' @@ -1067,11 +1699,7 @@ def __init__(self): }, 'SeSystemtimePrivilege': { 'Policy': 'Change the system time', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeSystemtimePrivilege' @@ -1083,11 +1711,7 @@ def __init__(self): }, 'SeTimeZonePrivilege': { 'Policy': 'Change the time zone', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeTimeZonePrivilege' @@ -1099,11 +1723,7 @@ def __init__(self): }, 'SeCreatePagefilePrivilege': { 'Policy': 'Create a pagefile', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeCreatePagefilePrivilege' @@ -1115,11 +1735,7 @@ def __init__(self): }, 'SeCreateTokenPrivilege': { 'Policy': 'Create a token object', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeCreateTokenPrivilege' @@ -1131,11 +1747,7 @@ def __init__(self): }, 'SeCreateGlobalPrivilege': { 'Policy': 'Create global objects', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeCreateGlobalPrivilege' @@ -1147,11 +1759,7 @@ def __init__(self): }, 'SeCreatePermanentPrivilege': { 'Policy': 'Create permanent shared objects', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeCreatePermanentPrivilege' @@ -1163,11 +1771,7 @@ def __init__(self): }, 'SeCreateSymbolicLinkPrivilege': { 'Policy': 'Create symbolic links', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeCreateSymbolicLinkPrivilege' @@ -1179,11 +1783,7 @@ def __init__(self): }, 'SeDebugPrivilege': { 'Policy': 'Debug programs', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeDebugPrivilege' @@ -1195,11 +1795,7 @@ def __init__(self): }, 'SeDenyNetworkLogonRight': { 'Policy': 'Deny access to this computer from the network', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyNetworkLogonRight' @@ -1211,11 +1807,7 @@ def __init__(self): }, 'SeDenyBatchLogonRight': { 'Policy': 'Deny log on as a batch job', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyBatchLogonRight' @@ -1227,11 +1819,7 @@ def __init__(self): }, 'SeDenyServiceLogonRight': { 'Policy': 'Deny log on as a service', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyServiceLogonRight' @@ -1243,11 +1831,7 @@ def __init__(self): }, 'SeDenyInteractiveLogonRight': { 'Policy': 'Deny log on locally', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyInteractiveLogonRight' @@ -1259,11 +1843,7 @@ def __init__(self): }, 'SeDenyRemoteInteractiveLogonRight': { 'Policy': 'Deny log on through Remote Desktop Services', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyRemoteInteractiveLogonRight' @@ -1275,11 +1855,7 @@ def __init__(self): }, 'SeEnableDelegationPrivilege': { 'Policy': 'Enable computer and user accounts to be trusted for delegation', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeEnableDelegationPrivilege' @@ -1291,11 +1867,7 @@ def __init__(self): }, 'SeRemoteShutdownPrivilege': { 'Policy': 'Force shutdown from a remote system', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeRemoteShutdownPrivilege' @@ -1307,11 +1879,7 @@ def __init__(self): }, 'SeAuditPrivilege': { 'Policy': 'Generate security audits', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeAuditPrivilege' @@ -1323,11 +1891,7 @@ def __init__(self): }, 'SeImpersonatePrivilege': { 'Policy': 'Impersonate a client after authentication', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeImpersonatePrivilege' @@ -1339,11 +1903,7 @@ def __init__(self): }, 'SeIncreaseWorkingSetPrivilege': { 'Policy': 'Increase a process working set', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeIncreaseWorkingSetPrivilege' @@ -1355,11 +1915,7 @@ def __init__(self): }, 'SeIncreaseBasePriorityPrivilege': { 'Policy': 'Increase scheduling priority', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeIncreaseBasePriorityPrivilege' @@ -1371,11 +1927,7 @@ def __init__(self): }, 'SeLoadDriverPrivilege': { 'Policy': 'Load and unload device drivers', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeLoadDriverPrivilege' @@ -1387,11 +1939,7 @@ def __init__(self): }, 'SeLockMemoryPrivilege': { 'Policy': 'Lock pages in memory', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeLockMemoryPrivilege' @@ -1403,11 +1951,7 @@ def __init__(self): }, 'SeBatchLogonRight': { 'Policy': 'Log on as a batch job', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeBatchLogonRight' @@ -1419,11 +1963,7 @@ def __init__(self): }, 'SeServiceLogonRight': { 'Policy': 'Log on as a service', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeServiceLogonRight' @@ -1435,11 +1975,7 @@ def __init__(self): }, 'SeSecurityPrivilege': { 'Policy': 'Manage auditing and security log', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeSecurityPrivilege' @@ -1451,11 +1987,7 @@ def __init__(self): }, 'SeRelabelPrivilege': { 'Policy': 'Modify an object label', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeRelabelPrivilege' @@ -1467,11 +1999,7 @@ def __init__(self): }, 'SeSystemEnvironmentPrivilege': { 'Policy': 'Modify firmware environment values', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeSystemEnvironmentPrivilege' @@ -1483,11 +2011,7 @@ def __init__(self): }, 'SeManageVolumePrivilege': { 'Policy': 'Perform volume maintenance tasks', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeManageVolumePrivilege' @@ -1499,11 +2023,7 @@ def __init__(self): }, 'SeProfileSingleProcessPrivilege': { 'Policy': 'Profile single process', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeProfileSingleProcessPrivilege' @@ -1515,11 +2035,7 @@ def __init__(self): }, 'SeSystemProfilePrivilege': { 'Policy': 'Profile system performance', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeSystemProfilePrivilege' @@ -1531,11 +2047,7 @@ def __init__(self): }, 'SeUndockPrivilege': { 'Policy': 'Remove computer from docking station', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeUndockPrivilege' @@ -1547,11 +2059,7 @@ def __init__(self): }, 'SeAssignPrimaryTokenPrivilege': { 'Policy': 'Replace a process level token', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeAssignPrimaryTokenPrivilege' @@ -1563,11 +2071,7 @@ def __init__(self): }, 'SeRestorePrivilege': { 'Policy': 'Restore files and directories', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeRestorePrivilege' @@ -1579,11 +2083,7 @@ def __init__(self): }, 'SeShutdownPrivilege': { 'Policy': 'Shut down the system', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeShutdownPrivilege' @@ -1595,11 +2095,7 @@ def __init__(self): }, 'SeSyncAgentPrivilege': { 'Policy': 'Synchronize directory service data', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeSyncAgentPrivilege' @@ -1611,11 +2107,7 @@ def __init__(self): }, 'SeTakeOwnershipPrivilege': { 'Policy': 'Take ownership of files and other objects', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment'], + 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { 'Option': 'SeTakeOwnershipPrivilege' @@ -1625,6 +2117,30 @@ def __init__(self): 'Put': '_usernamesToSidObjects', }, }, + 'RecoveryConsoleSecurityLevel': { + 'Policy': 'Recovery console: Allow automatic adminstrative logon', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole', + 'Value': 'SecurityLevel', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, + 'RecoveryConsoleSetCommand': { + 'Policy': 'Recovery console: Allow floppy copy and access to all drives and all folders', + 'Settings': self.enabled_one_disabled_zero.keys(), + 'lgpo_section': self.security_options_gpedit_path, + 'Registry': { + 'Hive': 'HKEY_LOCAL_MACHINE', + 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole', + 'Value': 'SetCommand', + 'Type': 'REG_DWORD', + }, + 'Transform': self.enabled_one_disabled_zero_transform, + }, } }, 'User': { @@ -1656,83 +2172,6 @@ def _notEmpty(cls, val, **kwargs): else: return False - @classmethod - def _enable_one_disable_zero_conversion(cls, val, **kwargs): - ''' - converts a reg dword 1/0 value to the strings enable/disable - ''' - if val is not None: - if val == 1 or val == "1": - return 'Enabled' - elif val == 0 or val == "0": - return 'Disabled' - elif val == '(value not set)': - return 'Not Defined' - else: - return 'Invalid Value' - else: - return 'Not Defined' - - @classmethod - def _enable_one_disable_zero_reverse_conversion(cls, val, **kwargs): - ''' - converts Enable/Disable to 1/0 - ''' - return_string = False - if 'return_string' in kwargs: - return_string = True - if val is not None: - if val.upper() == 'ENABLED': - if return_string: - return '1' - else: - return 1 - elif val.upper() == 'DISABLED': - if return_string: - return '0' - else: - return 0 - else: - return None - else: - return None - - @classmethod - def _event_audit_conversion(cls, val, **kwargs): - ''' - converts an audit setting # (0, 1, 2, 3) to the string text - ''' - if val is not None: - if val == 0 or val == "0": - return 'No auditing' - elif val == 1 or val == "1": - return 'Success' - elif val == 2 or val == "2": - return 'Failure' - elif val == 3 or val == "3": - return 'Succes, Failure' - else: - return 'Invalid Auditing Value' - else: - return 'Not Defined' - - @classmethod - def _event_audit_reverse_conversion(cls, val, **kwargs): - ''' - converts audit strings to numerical values - ''' - if val is not None: - if val.upper() == 'NO AUDITING': - return 0 - elif val.upper() == 'SUCCESS': - return 1 - elif val.upper() == 'FAILURE': - return 2 - elif val.upper() == 'SUCCESS, FAILURE': - return 3 - else: - return 'Not Defined' - @classmethod def _seconds_to_days(cls, val, **kwargs): ''' @@ -1862,6 +2301,11 @@ def _in_range_inclusive(cls, val, **kwargs): ''' minimum = 0 maximum = 1 + if isinstance(string_types, val): + if val.lower() == 'not defined': + return True + else: + return False if 'min' in kwargs: minimum = kwargs['min'] if 'max' in kwargs: @@ -1985,6 +2429,31 @@ def _powershell_script_order_reverse_conversion(cls, val, **kwargs): else: return 'Invalid Value' + @classmethod + def _dict_lookup(cls, item, **kwargs): + ''' + Retrieves the key or value from a dict based on the item + kwarg lookup dict to search for item + kwarg value_lookup bool to determine if item should be compared to keys or values + ''' + log.debug('item == {0}'.format(item)) + value_lookup = False + if 'value_lookup' in kwargs: + value_lookup = kwargs['value_lookup'] + else: + value_lookup = False + if 'lookup' in kwargs: + for k, v in kwargs['lookup'].iteritems(): + if value_lookup: + if str(v).lower() == str(item).lower(): + log.debug('returning key {0}'.format(k)) + return k + else: + if str(k).lower() == str(item).lower(): + log.debug('returning value {0}'.format(v)) + return v + return 'Invalid Value' + def __virtual__(): ''' @@ -2187,6 +2656,9 @@ def _transformValue(value, policy, transform_type): else: return value else: + if 'Registry' in policy: + if value == '(value not set)': + return 'Not Defined' return value @@ -4352,12 +4824,19 @@ def set_(computer_policy=None, user_policy=None, if _regedits: for regedit in _regedits.keys(): log.debug('{0} is a Registry policy'.format(regedit)) - _ret = __salt__['reg.set_value']( - _regedits[regedit]['policy']['Registry']['Hive'], - _regedits[regedit]['policy']['Registry']['Path'], - _regedits[regedit]['policy']['Registry']['Value'], - _regedits[regedit]['value'], - _regedits[regedit]['policy']['Registry']['Type']) + # if the value setting is None or "(value not set)", we will delete the value from the registry + if _regedits[regedit]['value'] is not None and _regedits[regedit]['value'] != '(value not set)': + _ret = __salt__['reg.set_value']( + _regedits[regedit]['policy']['Registry']['Hive'], + _regedits[regedit]['policy']['Registry']['Path'], + _regedits[regedit]['policy']['Registry']['Value'], + _regedits[regedit]['value'], + _regedits[regedit]['policy']['Registry']['Type']) + else: + _ret = __salt__['reg.delete_value']( + _regedits[regedit]['polic']['Registry']['Hive'], + _regedits[regedit]['policy']['Registry']['Path'], + _regedits[regedit]['policy']['Registry']['Value']) if not _ret: msg = ('Error while attempting to set policy {0} via the registry.' ' Some changes may not be applied as expected') From e2574da0b811a9e5527e8dd3eac31ad01658644e Mon Sep 17 00:00:00 2001 From: twangboy Date: Wed, 21 Sep 2016 12:35:57 -0600 Subject: [PATCH 06/59] Fix/Add documentation, 80 char line lengths --- salt/modules/win_lgpo.py | 1050 ++++++++++++++++++++++++-------------- 1 file changed, 664 insertions(+), 386 deletions(-) diff --git a/salt/modules/win_lgpo.py b/salt/modules/win_lgpo.py index 9c39b3df6ce7..aea23b27bb99 100644 --- a/salt/modules/win_lgpo.py +++ b/salt/modules/win_lgpo.py @@ -4,27 +4,31 @@ .. versionadded:: 2016.11.0 -This module allows configuring local group policy (i.e. gpedit.msc) on a Windows server. +This module allows configuring local group policy (i.e. ``gpedit.msc``) on a +Windows server. Administrative Templates ======================== - Administrative template policies are dynamically read from admx/adml files on the server. +Administrative template policies are dynamically read from ADMX/ADML files on +the server. Windows Settings ================ - Policies contained in the "Windows Settings" section of the gpedit.msc gui are statically - defined in this module. Each policy is configured for the section (Machine/User) in the - module's _policy_info class. The _policy_info class contains a policies dict on how the module will - configure the policy, where the policy resides in the gui (for display purposes), data - validation data, data transformation data, etc. + +Policies contained in the "Windows Settings" section of the ``gpedit.msc`` GUI +are statically defined in this module. Each policy is configured for the section +(Machine/User) in the module's _policy_info class. The ``_policy_info`` class +contains a "policies" dict on how the module will configure the policy, where +the policy resides in the GUI (for display purposes), data validation data, data +transformation data, etc. Current known limitations ========================= - At this time, start/shutdown scripts policies are displayed, but are not configurable. - - Not all "Security Settings" policies exist in the _policy_info class +- At this time, start/shutdown scripts policies are displayed, but are not + configurable. +- Not all "Security Settings" policies exist in the _policy_info class :depends: - pywin32 Python module @@ -33,7 +37,6 @@ - codecs - struct - salt.modules.reg - ''' # Import python libs @@ -41,6 +44,7 @@ import os import logging import re + # Import salt libs import salt.utils from salt.exceptions import CommandExecutionError @@ -54,7 +58,8 @@ __func_alias__ = {'set_': 'set'} adm_policy_name_map = {True: {}, False: {}} HAS_WINDOWS_MODULES = False -# define some globals XPATH variables that we'll set assuming all our imports are good +# define some global XPATH variables that we'll set assuming all our imports are +# good TRUE_VALUE_XPATH = None FALSE_VALUE_XPATH = None ELEMENTS_XPATH = None @@ -114,106 +119,140 @@ class _policy_info(object): ''' - policy helper stuff + Policy Helper Class + =================== The format of the policy dict is as follows: - The top most two key/value pairs in the dict divide the policies object into the two sections - of local group policy, using the keys "Machine" and "User". The value make-up of these dicts - are described below in "Policy Section Definition" - - Policy Section Definition - ------------------------- - A policy section dict has two required key/value pairs: - key name: lgpo_section - value: string matching how the policy section is displayed in the mmc snap-in - ("Computer Configuration" for "Machine" and "User Configuration" for "User") - - key name: policies - value: a dict containing the non-Administrative template policy definitions, the key for - each item is a short/unique identifier for the policy, the value is described below in - "Policies Definition" - - Policies Definition - ------------------- - A policies definition item describes the particular policy. - There are three child key/value pairs shared with all policy types: - key name: lgpo_section - value: a list containing the hierarchical path to the policy in the gpedit mmc snap-in. - - key name: Policy - value: a string containing the name of the policy in the gpedit mmc snap-in - - key name: Settings - value: An object which describes valid settings for the policy. This can be None for no validation, - a list of possible settings, or a dict with the following key/value pairs: - key name: Function - value: the class function to use to validate the setting - - key name: Args - value: a dict of kwargs to pass to the class function - - Additionally, each policies definition will contain a key/value pair that defines the mechanism that - will be used to configure the policy. The available mechanisms are: NetUserModal, Registry, Secedit, - and LsaRights - - Registry mechanism - ------------------ - Some policies set simply values in the windows registry, the value of this key is a dict with the - following make-up: - key name: Hive - value: a string containing the Registry hive, such as HKEY_LOCAL_MACHINE - - key name: Path - value: a string containing the registry key path, such as SYSTEM\\CurrentControlSet\\Control\\Lsa - - key name: Value - value: a string containing the name of the registry value, such as restrictanonymous - - key name: Type - value: a string contianing the registry type of the value, such as REG_DWORD - - Secedit mechanism - ----------------- - Some policies are configurable via the "secedit.exe" executable, the value of this key is a dict with the - following make-up: - key name: Option - value: a string containing the name of the policy as it appears in an export from secedit, such as - PasswordComplexity - - key name: Section - value: a string containing the name of the section that the "Option" value appears in in an export - from secedit, such as "System Access" - - LsaRights mechanism - ------------------- - LSA Rights policiesare configured via the LsaRights mechansim, the value of this key is a dict with the - following make-up: - key name: Option - value: a string containing the programmatic name of the Lsa Right, such as SeNetworkLogonRight - - NetUserModal mechanism - ---------------------- - Some policies are configurable by the NetUserModalGet and NetUserModalSet function from pywin32. The value - of this key is a dict with the following make-up: - key name: Modal - value: The modal "level" that the particular option is specified in (0-3), see - https://msdn.microsoft.com/en-us/library/windows/desktop/aa370656(v=vs.85).aspx - - key name: Option - value: The name of the structure member which contains the data for the policy, for example max_passwd_age - - Optionally, each policy definition can contain a 'Transform' key. The Transform key is used to handle data that is stored - and viewed differently. This key's value is a dict with the following key/value pairs: - key name: Get - value: the name of the class function to use to transform the data from the stored value to how the value - is displayed in the GUI - - key name: Put - value: the name of the class function to use to transfor the data supplied by the user to the correct value - that the policy is stored in - For example, "Minimum password age" is stored in seconds, but is displayed in days. Thus the "Get" and "Put" functions - for this policy do these conversions so the user is able to set and view the policy using the same data that is shown - in the GUI + + The top most two key/value pairs in the dict divide the policies object into + the two sections of local group policy, using the keys "Machine" and "User". + The value make-up of these dicts are described below in "Policy Section + Definition" + + Policy Section Definition + ------------------------- + + A policy section dict has two required key/value pairs: + + ============ ============================================================== + Key + ============ ============================================================== + lgpo_section String matching how the policy section is displayed in the mmc + snap-in ("Computer Configuration" for "Machine" and "User + Configuration" for "User") + policies a dict containing the non-Administrative template policy + definitions, the key for each item is a short/unique + identifier for the policy, the value is described below in + "Policies Definition" + ============ ============================================================== + + Policies Definition + ------------------- + + A policies definition item describes the particular policy. There are three + child key/value pairs shared with all policy types: + + ============ ============================================================== + Key Value + ============ ============================================================== + lgpo_section A list containing the hierarchical path to the policy in the + gpedit mmc snap-in. + Policy A string containing the name of the policy in the gpedit mmc + snap-in + Settings An object which describes valid settings for the policy. This + can be None for no validation, a list of possible settings, or + a dict with the following key/value pairs: + + - **Function:** The class function to use to validate the + setting + - **Args:** A dict of kwargs to pass to the class function + ============ ============================================================== + + Additionally, each policies definition will contain a key/value pair that + defines the mechanism that will be used to configure the policy. The + available mechanisms are: NetUserModal, Registry, Secedit, and LsaRights + + Registry Mechanism + ------------------ + + Some policies simply set values in the Windows registry. The value of this + key is a dict with the following make-up: + + ===== ===================================================================== + Key Value + ===== ===================================================================== + Hive A string containing the Registry hive, such as ``HKEY_LOCAL_MACHINE`` + Path A string containing the registry key path, such as + ``SYSTEM\\CurrentControlSet\\Control\\Lsa`` + Value A string containing the name of the registry value, such as + **restrictanonymous** + Type A string containing the registry type of the value, such as + ``REG_DWORD`` + ===== ===================================================================== + + Secedit Mechanism + ----------------- + + Some policies are configurable via the "secedit.exe" executable. The value + of this key is a dict with the following make-up: + + ======= =================================================================== + Key Value + ======= =================================================================== + Option A string containing the name of the policy as it appears in an + export from secedit, such as **PasswordComplexity** + Section A string containing the name of the section in which the "Option" + value appears in an export from ``secedit``, such as "System + Access" + ======= =================================================================== + + LsaRights mechanism + ------------------- + + LSA Rights policies are configured via the LsaRights mechanism. The value of + this key is a dict with the following make-up: + + ====== ==================================================================== + Key Value + ====== ==================================================================== + Option A string containing the programmatic name of the Lsa Right, such as + **SeNetworkLogonRight** + ====== ==================================================================== + + NetUserModal mechanism + ---------------------- + + Some policies are configurable by the **NetUserModalGet** and + **NetUserModalSet** function from pywin32. The value of this key is a dict + with the following make-up: + + ====== ==================================================================== + Key Value + ====== ==================================================================== + Modal The modal "level" that the particular option is specified in (0-3), + see `here `_ + Option The name of the structure member which contains the data for the + policy, for example **max_passwd_age** + ====== ==================================================================== + + Optionally, each policy definition can contain a "Transform" key. The + Transform key is used to handle data that is stored and viewed differently. + This key's value is a dict with the following key/value pairs: + + === ======================================================================= + Key Value + === ======================================================================= + Get The name of the class function to use to transform the data from the + stored value to how the value is displayed in the GUI + Put The name of the class function to use to transform the data supplied by + the user to the correct value that the policy is stored in + === ======================================================================= + + For example, "Minimum password age" is stored in seconds, but is displayed + in days. Thus the "Get" and "Put" functions for this policy do these + conversions so the user is able to set and view the policy using the same + data that is shown in the GUI. ''' def __init__(self): self.audit_lookup = { @@ -409,7 +448,8 @@ def __init__(self): }, }, 'StartupPowershellScriptOrder': { - 'Policy': 'Startup - For this GPO, run scripts in the following order', + 'Policy': 'Startup - For this GPO, run scripts in the ' + 'following order', 'lgpo_section': ['Computer Configuration', 'Windows Settings', 'Scripts (Startup/Shutdown)', @@ -463,7 +503,8 @@ def __init__(self): }, }, 'ShutdownPowershellScriptOrder': { - 'Policy': 'Shutdown - For this GPO, run scripts in the following order', + 'Policy': 'Shutdown - For this GPO, run scripts in the ' + 'following order', 'lgpo_section': ['Computer Configuration', 'Windows Settings', 'Scripts (Startup/Shutdown)', @@ -485,7 +526,8 @@ def __init__(self): }, }, 'LSAAnonymousNameLookup': { - 'Policy': 'Network access: Allow anonymous SID/Name translation', + 'Policy': 'Network access: Allow anonymous SID/Name ' + 'translation', 'lgpo_section': self.password_policy_gpedit_path, 'Settings': self.enabled_one_disabled_zero.keys(), 'Secedit': { @@ -495,7 +537,8 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform, }, 'RestrictAnonymousSam': { - 'Policy': 'Network access: Do not allow anonymous enumeration of SAM accounts', + 'Policy': 'Network access: Do not allow anonymous ' + 'enumeration of SAM accounts', 'lgpo_section': self.security_options_gpedit_path, 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { @@ -507,7 +550,8 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform, }, 'RestrictAnonymous': { - 'Policy': 'Network access: Do not allow anonymous enumeration of SAM accounts and shares', + 'Policy': 'Network access: Do not allow anonymous ' + 'enumeration of SAM accounts and shares', 'lgpo_section': self.security_options_gpedit_path, 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { @@ -519,7 +563,9 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform, }, 'DisableDomainCreds': { - 'Policy': 'Network access: Do not allow storage of passwords and credentials for network authentication', + 'Policy': 'Network access: Do not allow storage of ' + 'passwords and credentials for network ' + 'authentication', 'lgpo_section': self.security_options_gpedit_path, 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { @@ -531,7 +577,8 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform, }, 'EveryoneIncludesAnonymous': { - 'Policy': 'Network access: Let Everyone permissions apply to anonymous users', + 'Policy': 'Network access: Let Everyone permissions ' + 'apply to anonymous users', 'lgpo_section': self.security_options_gpedit_path, 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { @@ -543,59 +590,71 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform, }, 'NullSessionPipes': { - 'Policy': 'Network access: Named Pipes that can be accessed anonymously', + 'Policy': 'Network access: Named Pipes that can be ' + 'accessed anonymously', 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'NullSessionPipes', 'Type': 'REG_MULTI_SZ' }, }, 'RemoteRegistryExactPaths': { - 'Policy': 'Network access: Remotely accessible registry paths', + 'Policy': 'Network access: Remotely accessible registry ' + 'paths', 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\SecurePipeServers\\winreg\\AllowedExactPaths', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\' + 'SecurePipeServers\\winreg\\' + 'AllowedExactPaths', 'Value': 'Machine', 'Type': 'REG_MULTI_SZ' }, }, 'RemoteRegistryPaths': { - 'Policy': 'Network access: Remotely accessible registry paths and sub-paths', + 'Policy': 'Network access: Remotely accessible registry ' + 'paths and sub-paths', 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\SecurePipeServers\\winreg\\AllowedPaths', + 'Path': 'SYSTEM\\CurrentControlSet\\Control\\' + 'SecurePipeServers\\winreg\\AllowedPaths', 'Value': 'Machine', 'Type': 'REG_MULTI_SZ' }, }, 'RestrictNullSessAccess': { - 'Policy': 'Network access: Restrict anonymous access to Named Pipes and Shares', + 'Policy': 'Network access: Restrict anonymous access to ' + 'Named Pipes and Shares', 'lgpo_section': self.security_options_gpedit_path, 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'RestrictNullSessAccess', 'Type': 'REG_DWORD' }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'NullSessionShares': { - 'Policy': 'Network access: Shares that can be accessed anonymously', + 'Policy': 'Network access: Shares that can be accessed ' + 'anonymously', 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'NullSessionShares', 'Type': 'REG_MULTI_SZ' }, }, 'ForceGuest': { - 'Policy': 'Network access: Sharing and security model for local accounts', + 'Policy': 'Network access: Sharing and security model ' + 'for local accounts', 'lgpo_section': self.security_options_gpedit_path, 'Settings': self.force_guest.keys(), 'Registry': { @@ -709,7 +768,8 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\' + 'CurrentVersion\\policies\\system', 'Value': 'NoConnectedUser', 'Type': 'REG_DWORD', }, @@ -737,7 +797,8 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform, }, 'LimitBlankPasswordUse': { - 'Policy': 'Accounts: Limit local account use of blank passwords to console logon only', + 'Policy': 'Accounts: Limit local account use of blank ' + 'passwords to console logon only', 'lgpo_section': self.security_options_gpedit_path, 'Settings': self.enabled_one_disabled_zero.keys(), 'Registry': { @@ -775,7 +836,8 @@ def __init__(self): }, }, 'AuditBaseObjects': { - 'Policy': 'Audit: Audit the access of global system objects', + 'Policy': 'Audit: Audit the access of global system ' + 'objects', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { @@ -787,7 +849,9 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform }, 'SceNoApplyLegacyAuditPolicy': { - 'Policy': 'Audit: Force audit policy subcategoy settings (Windows Vista or later) to override audit policy category settings', + 'Policy': 'Audit: Force audit policy subcategory ' + 'settings (Windows Vista or later) to ' + 'override audit policy category settings', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { @@ -799,19 +863,23 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_transform }, 'DontDisplayLastUserName': { - 'Policy': 'Interactive logon: Do not display last user name', + 'Policy': 'Interactive logon: Do not display last user ' + 'name', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'DontDisplayLastUserName', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'CachedLogonsCount': { - 'Policy': 'Interactive logon: Number of previous logons to cache (in case domain controller is not available)', + 'Policy': 'Interactive logon: Number of previous logons ' + 'to cache (in case domain controller is not ' + 'available)', 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 50} @@ -819,30 +887,35 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Winlogon', 'Value': 'CachedLogonsCount', 'Type': 'REG_SZ', }, }, 'ForceUnlockLogon': { - 'Policy': 'Interactive logon: Require Domain Controller authentication to unlock workstation', + 'Policy': 'Interactive logon: Require Domain Controller ' + 'authentication to unlock workstation', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Winlogon', 'Value': 'ForceUnlockLogon', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'ScRemoveOption': { - 'Policy': 'Interactive logon: Smart card removal behavior', + 'Policy': 'Interactive logon: Smart card removal ' + 'behavior', 'Settings': self.sc_removal_lookup.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Winlogon', 'Value': 'ScRemoveOption', 'Type': 'REG_DWORD', }, @@ -860,48 +933,58 @@ def __init__(self): }, }, 'DisableCAD': { - 'Policy': 'Interactive logon: Do not require CTRL+ALT+DEL', + 'Policy': 'Interactive logon: Do not require ' + 'CTRL+ALT+DEL', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'DisableCAD', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'FilterAdministratorToken': { - 'Policy': 'User Account Control: Admin Approval Mode for the built-in Administrator account', + 'Policy': 'User Account Control: Admin Approval Mode ' + 'for the built-in Administrator account', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'FilterAdministratorToken', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'EnableUIADesktopToggle': { - 'Policy': 'User Account Control: Allow UIAccess applications to prompt for elevation without using the secure desktop', + 'Policy': 'User Account Control: Allow UIAccess ' + 'applications to prompt for elevation without ' + 'using the secure desktop', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'EnableUIADesktopToggle', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'ConsentPromptBehaviorAdmin': { - 'Policy': 'User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode', + 'Policy': 'User Account Control: Behavior of the ' + 'elevation prompt for administrators in Admin ' + 'Approval Mode', 'Settings': self.uac_admin_prompt_lookup.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'ConsentPromptBehaviorAdmin', 'Type': 'REG_DWORD', }, @@ -919,12 +1002,14 @@ def __init__(self): }, }, 'ConsentPromptBehaviorUser': { - 'Policy': 'User Account Control: Behavior of the elevation prompt for standard users', + 'Policy': 'User Account Control: Behavior of the ' + 'elevation prompt for standard users', 'Settings': self.uac_user_prompt_lookup.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'ConsentPromptBehaviorUser', 'Type': 'REG_DWORD', }, @@ -942,79 +1027,94 @@ def __init__(self): }, }, 'EnableInstallerDetection': { - 'Policy': 'User Account Control: Detect application installations and prompt for elevation', + 'Policy': 'User Account Control: Detect application ' + 'installations and prompt for elevation', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'EnableInstallerDetection', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'ValidateAdminCodeSignatures': { - 'Policy': 'User Account Control: Only elevate executables that are signed and validated', + 'Policy': 'User Account Control: Only elevate ' + 'executables that are signed and validated', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'ValidateAdminCodeSignatures', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'EnableSecureUIAPaths': { - 'Policy': 'User Account Control: Only elevate UIAccess applicaitons that are installed in secure locations', + 'Policy': 'User Account Control: Only elevate UIAccess ' + 'applicaitons that are installed in secure ' + 'locations', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'EnableSecureUIAPaths', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'EnableLUA': { - 'Policy': 'User Account Control: Run all administrators in Admin Approval Mode', + 'Policy': 'User Account Control: Run all administrators ' + 'in Admin Approval Mode', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'EnableLUA', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'PromptOnSecureDesktop': { - 'Policy': 'User Account Control: Switch to the secure desktop when prompting for elevation', + 'Policy': 'User Account Control: Switch to the secure ' + 'desktop when prompting for elevation', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'PromptOnSecureDesktop', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'EnableVirtualization': { - 'Policy': 'User Account Control: Virtualize file and registry write failures to per-user locations', + 'Policy': 'User Account Control: Virtualize file and ' + 'registry write failures to per-user ' + 'locations', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'EnableVirtualization', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'PasswordExpiryWarning': { - 'Policy': 'Interactive logon: Prompt user to change password before expiration', + 'Policy': 'Interactive logon: Prompt user to change ' + 'password before expiration', 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 999} @@ -1022,13 +1122,15 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Winlogon', 'Value': 'PasswordExpiryWarning', 'Type': 'REG_DWORD', }, }, 'MaxDevicePasswordFailedAttempts': { - 'Policy': 'Interactive logon: Machine account lockout threshold', + 'Policy': 'Interactive logon: Machine account lockout ' + 'threshold', 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 999} @@ -1036,7 +1138,8 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\' + 'CurrentVersion\\policies\\system', 'Value': 'MaxDevicePasswordFailedAttempts', 'Type': 'REG_DWORD', }, @@ -1050,38 +1153,45 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\' + 'CurrentVersion\\policies\\system', 'Value': 'InactivityTimeoutSecs', 'Type': 'REG_DWORD', }, }, 'legalnoticetext': { - 'Policy': 'Interactive logon: Message text for users attempting to log on', + 'Policy': 'Interactive logon: Message text for users ' + 'attempting to log on', 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\' + 'CurrentVersion\\policies\\system', 'Value': 'legalnoticetext', 'Type': 'REG_SZ', }, }, 'legalnoticecaption': { - 'Policy': 'Interactive logon: Message title for users attempting to log on', + 'Policy': 'Interactive logon: Message title for users ' + 'attempting to log on', 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\' + 'CurrentVersion\\policies\\system', 'Value': 'legalnoticecaption', 'Type': 'REG_SZ', }, }, 'DontDisplayLockedUserId': { - 'Policy': 'Interactive logon: Display user information when the session is locked', + 'Policy': 'Interactive logon: Display user information ' + 'when the session is locked', 'Settings': self.locked_session_user_info.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', + 'Path': 'SOFTWARE\\Microsoft\\Windows\\' + 'CurrentVersion\\policies\\system', 'Value': 'DontDisplayLockedUserId', 'Type': 'REG_DWORD', }, @@ -1104,50 +1214,58 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'ScForceOption', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'Client_RequireSecuritySignature': { - 'Policy': 'Microsoft network client: Digitally sign communications (always)', + 'Policy': 'Microsoft network client: Digitally sign ' + 'communications (always)', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanWorkstation\\Parameters', 'Value': 'RequireSecuritySignature', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'Client_EnableSecuritySignature': { - 'Policy': 'Microsoft network client: Digitally sign communications (if server agrees)', + 'Policy': 'Microsoft network client: Digitally sign ' + 'communications (if server agrees)', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanWorkstation\\Parameters', 'Value': 'EnableSecuritySignature', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'EnablePlainTextPassword': { - 'Policy': 'Microsoft network client: Send unencrypted password to third-party SMB servers', + 'Policy': 'Microsoft network client: Send unencrypted ' + 'password to third-party SMB servers', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanWorkstation\\Parameters', 'Value': 'EnablePlainTextPassword', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'AutoDisconnect': { - 'Policy': 'Microsoft network server: Amount of idle time required before suspending session', + 'Policy': 'Microsoft network server: Amount of idle ' + 'time required before suspending session', 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 99999} @@ -1155,18 +1273,21 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'AutoDisconnect', 'Type': 'REG_DWORD', }, }, 'EnableS4U2SelfForClaims': { - 'Policy': 'Microsoft network server: Attempt S4U2Self to obtain claim information', + 'Policy': 'Microsoft network server: Attempt S4U2Self ' + 'to obtain claim information', 'Settings': self.s4u2self_options.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'EnableS4U2SelfForClaims', 'Type': 'REG_DWORD', }, @@ -1184,48 +1305,56 @@ def __init__(self): }, }, 'Server_RequireSecuritySignature': { - 'Policy': 'Microsoft network server: Digitally sign communications (always)', + 'Policy': 'Microsoft network server: Digitally sign ' + 'communications (always)', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'RequireSecuritySignature', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'Server_EnableSecuritySignature': { - 'Policy': 'Microsoft network server: Digitally sign communications (if client agrees)', + 'Policy': 'Microsoft network server: Digitally sign ' + 'communications (if client agrees)', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'EnableSecuritySignature', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'EnableForcedLogoff': { - 'Policy': 'Microsoft network server: Disconnect clients when logon hours expire', + 'Policy': 'Microsoft network server: Disconnect clients ' + 'when logon hours expire', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'EnableForcedLogoff', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'SmbServerNameHardeningLevel': { - 'Policy': 'Microsoft network server: Server SPN target name validation level', + 'Policy': 'Microsoft network server: Server SPN target ' + 'name validation level', 'Settings': self.smb_server_name_hardening_levels.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\LanmanServer\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'LanmanServer\\Parameters', 'Value': 'SmbServerNameHardeningLevel', 'Type': 'REG_DWORD', }, @@ -1243,7 +1372,8 @@ def __init__(self): } }, 'FullPrivilegeAuditing': { - 'Policy': 'Audit: Audit the use of Backup and Restore privilege', + 'Policy': 'Audit: Audit the use of Backup and Restore ' + 'privilege', 'Settings': [chr(0), chr(1)], 'lgpo_section': self.security_options_gpedit_path, 'Registry': { @@ -1258,7 +1388,8 @@ def __init__(self): }, }, 'CrashOnAuditFail': { - 'Policy': 'Audit: Shut down system immediately if unable to log security audits', + 'Policy': 'Audit: Shut down system immediately if ' + 'unable to log security audits', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { @@ -1275,19 +1406,22 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', + 'Path': 'Software\\Microsoft\\Windows\\' + 'CurrentVersion\\Policies\\System', 'Value': 'UndockWithoutLogon', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'AllocateDASD': { - 'Policy': 'Devices: Allowed to format and eject removable media', + 'Policy': 'Devices: Allowed to format and eject ' + 'removable media', 'Settings': ["", "0", "1", "2"], 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Winlogon', 'Value': 'AllocateDASD', 'Type': 'REG_SZ', }, @@ -1297,36 +1431,43 @@ def __init__(self): }, }, 'AllocateCDRoms': { - 'Policy': 'Devices: Restrict CD-ROM access to locally logged-on user only', + 'Policy': 'Devices: Restrict CD-ROM access to locally ' + 'logged-on user only', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Winlogon', 'Value': 'AllocateCDRoms', 'Type': 'REG_SZ', }, 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'AllocateFloppies': { - 'Policy': 'Devices: Restrict floppy access to locally logged-on user only', + 'Policy': 'Devices: Restrict floppy access to locally ' + 'logged-on user only', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Winlogon', 'Value': 'AllocateFloppies', 'Type': 'REG_SZ', }, 'Transform': self.enabled_one_disabled_zero_strings_transform, }, - # see KB298503 why we aren't just doing this one via the registry + # see KB298503 why we aren't just doing this one via the + # registry 'DriverSigningPolicy': { - 'Policy': 'Devices: Unsigned driver installation behavior', + 'Policy': 'Devices: Unsigned driver installation ' + 'behavior', 'Settings': ['3,0', '3,' + chr(1), '3,' + chr(2)], 'lgpo_section': self.security_options_gpedit_path, 'Secedit': { - 'Option': 'MACHINE\\Software\\Microsoft\\Driver Signing\\Policy', + 'Option': 'MACHINE\\Software\\Microsoft\\Driver ' + 'Signing\\Policy', 'Section': 'Registry Values', }, 'Transform': { @@ -1335,7 +1476,8 @@ def __init__(self): }, }, 'SubmitControl': { - 'Policy': 'Domain controller: Allow server operators to schedule tasks', + 'Policy': 'Domain controller: Allow server operators to ' + 'schedule tasks', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { @@ -1347,12 +1489,14 @@ def __init__(self): 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'LDAPServerIntegrity': { - 'Policy': 'Domain controller: LDAP server signing requirements', + 'Policy': 'Domain controller: LDAP server signing ' + 'requirements', 'Settings': self.ldap_server_signing_requirements.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\NTDS\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\NTDS' + '\\Parameters', 'Value': 'LDAPServerIntegrity', 'Type': 'REG_DWORD', }, @@ -1370,67 +1514,78 @@ def __init__(self): } }, 'RefusePasswordChange': { - 'Policy': 'Domain controller: Refuse machine account password changes', + 'Policy': 'Domain controller: Refuse machine account ' + 'password changes', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' + 'Netlogon\\Parameters', 'Value': 'RefusePasswordChange', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'RequireSignOrSeal': { - 'Policy': 'Domain member: Digitally encrypt or sign secure channel data (always)', + 'Policy': 'Domain member: Digitally encrypt or sign ' + 'secure channel data (always)', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'Netlogon\\Parameters', 'Value': 'RequireSignOrSeal', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'SealSecureChannel': { - 'Policy': 'Domain member: Digitally encrypt secure channel data (when possible)', + 'Policy': 'Domain member: Digitally encrypt secure ' + 'channel data (when possible)', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'Netlogon\\Parameters', 'Value': 'SealSecureChannel', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'SignSecureChannel': { - 'Policy': 'Domain member: Digitally sign secure channel data (when possible)', + 'Policy': 'Domain member: Digitally sign secure channel ' + 'data (when possible)', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'Netlogon\\Parameters', 'Value': 'SignSecureChannel', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'DisablePasswordChange': { - 'Policy': 'Domain member: Disable machine account password changes', + 'Policy': 'Domain member: Disable machine account ' + 'password changes', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'Netlogon\\Parameters', 'Value': 'DisablePasswordChange', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_strings_transform, }, 'MaximumPasswordAge': { - 'Policy': 'Domain member: Maximum machine account password age', + 'Policy': 'Domain member: Maximum machine account ' + 'password age', 'Settings': { 'Function': '_in_range_inclusive', 'Args': {'min': 0, 'max': 999} @@ -1438,18 +1593,21 @@ def __init__(self): 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'Netlogon\\Parameters', 'Value': 'MaximumPasswordAge', 'Type': 'REG_DWORD', }, }, 'RequireStrongKey': { - 'Policy': 'Domain member: Require strong (Windows 2000 or later) session key', + 'Policy': 'Domain member: Require strong (Windows 2000 ' + 'or later) session key', 'Settings': self.enabled_one_disabled_zero_strings.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', + 'Path': 'System\\CurrentControlSet\\Services\\' + 'Netlogon\\Parameters', 'Value': 'RequireStrongKey', 'Type': 'REG_DWORD', }, @@ -1590,7 +1748,8 @@ def __init__(self): 'Transform': self.audit_transform, }, 'SeTrustedCredManAccessPrivilege': { - 'Policy': 'Access Credential Manager as a trusted caller', + 'Policy': 'Access Credential Manager as a trusted ' + 'caller', 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { @@ -1794,7 +1953,8 @@ def __init__(self): }, }, 'SeDenyNetworkLogonRight': { - 'Policy': 'Deny access to this computer from the network', + 'Policy': 'Deny access to this computer from the ' + 'network', 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { @@ -1854,7 +2014,8 @@ def __init__(self): }, }, 'SeEnableDelegationPrivilege': { - 'Policy': 'Enable computer and user accounts to be trusted for delegation', + 'Policy': 'Enable computer and user accounts to be ' + 'trusted for delegation', 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { @@ -2118,24 +2279,28 @@ def __init__(self): }, }, 'RecoveryConsoleSecurityLevel': { - 'Policy': 'Recovery console: Allow automatic adminstrative logon', + 'Policy': 'Recovery console: Allow automatic ' + 'adminstrative logon', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Setup\\RecoveryConsole', 'Value': 'SecurityLevel', 'Type': 'REG_DWORD', }, 'Transform': self.enabled_one_disabled_zero_transform, }, 'RecoveryConsoleSetCommand': { - 'Policy': 'Recovery console: Allow floppy copy and access to all drives and all folders', + 'Policy': 'Recovery console: Allow floppy copy and ' + 'access to all drives and all folders', 'Settings': self.enabled_one_disabled_zero.keys(), 'lgpo_section': self.security_options_gpedit_path, 'Registry': { 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole', + 'Path': 'Software\\Microsoft\\Windows NT\\' + 'CurrentVersion\\Setup\\RecoveryConsole', 'Value': 'SetCommand', 'Type': 'REG_DWORD', }, @@ -2150,12 +2315,16 @@ def __init__(self): } self.admx_registry_classes = { 'User': { - 'policy_path': os.path.join(os.getenv('WINDIR'), 'System32', 'GroupPolicy', 'User', 'Registry.pol'), + 'policy_path': os.path.join(os.getenv('WINDIR'), 'System32', + 'GroupPolicy', 'User', + 'Registry.pol'), 'hive': 'HKEY_USERS', 'lgpo_section': 'User Configuration' }, 'Machine': { - 'policy_path': os.path.join(os.getenv('WINDIR'), 'System32', 'GroupPolicy', 'Machine', 'Registry.pol'), + 'policy_path': os.path.join(os.getenv('WINDIR'), 'System32', + 'GroupPolicy', 'Machine', + 'Registry.pol'), 'hive': 'HKEY_LOCAL_MACHINE', 'lgpo_section': 'Computer Configuration', }, @@ -2263,7 +2432,7 @@ def _dasd_conversion(cls, val, **kwargs): ''' if val is not None: if val == '0' or val == 0 or val == '': - return 'Administrtors' + return 'Administrators' elif val == '1' or val == 1: return 'Administrators and Power Users' elif val == '2' or val == 2: @@ -2280,14 +2449,15 @@ def _dasd_reverse_conversion(cls, val, **kwargs): ''' if val is not None: if val.upper() == 'ADMINISTRATORS': - # "" also shows 'administrators' in the gui + # "" also shows 'administrators' in the GUI return '0' elif val.upper() == 'ADMINISTRATORS AND POWER USERS': return '1' elif val.upper() == 'ADMINISTRATORS AND INTERACTIVE USERS': return '2' elif val.upper() == 'NOT DEFINED': - # a setting of anything other than nothing,0,1,2 or if they doesn't exist shows 'not defined' + # a setting of anything other than nothing, 0, 1, 2 or if it + # doesn't exist show 'not defined' return '9999' else: return 'Invalid Value' @@ -2321,7 +2491,8 @@ def _in_range_inclusive(cls, val, **kwargs): @classmethod def _driver_signing_reg_conversion(cls, val, **kwargs): ''' - converts the binary value in the registry for driver signing into the correct string representation + converts the binary value in the registry for driver signing into the + correct string representation ''' log.debug('we have {0} for the driver signing value'.format(val)) if val is not None: @@ -2346,7 +2517,8 @@ def _driver_signing_reg_conversion(cls, val, **kwargs): @classmethod def _driver_signing_reg_reverse_conversion(cls, val, **kwargs): ''' - converts the string value seen in the gui to the correct registry value for seceit + converts the string value seen in the GUI to the correct registry value + for secedit ''' if val is not None: if val.upper() == 'SILENTLY SUCCEED': @@ -2396,14 +2568,16 @@ def _usernamesToSidObjects(cls, val, **kwargs): sids.append(sid) except Exception as e: raise CommandExecutionError(( - 'There was an error obtaining the SID of user "{0}". Error returned: {1}' + 'There was an error obtaining the SID of user "{0}". Error ' + 'returned: {1}' ).format(_user, e)) return sids @classmethod def _powershell_script_order_conversion(cls, val, **kwargs): ''' - converts true/false/None to the GUI representation of the powershell startup/shutdown script order + converts true/false/None to the GUI representation of the powershell + startup/shutdown script order ''' log.debug('script order value = {0}'.format(val)) if val is None or val == 'None': @@ -2418,7 +2592,8 @@ def _powershell_script_order_conversion(cls, val, **kwargs): @classmethod def _powershell_script_order_reverse_conversion(cls, val, **kwargs): ''' - converts powershell script GUI strings representations to True/False/None + converts powershell script GUI strings representations to + True/False/None ''' if val.upper() == 'Run Windows PowerShell scripts first'.upper(): return 'true' @@ -2434,7 +2609,8 @@ def _dict_lookup(cls, item, **kwargs): ''' Retrieves the key or value from a dict based on the item kwarg lookup dict to search for item - kwarg value_lookup bool to determine if item should be compared to keys or values + kwarg value_lookup bool to determine if item should be compared to keys + or values ''' log.debug('item == {0}'.format(item)) value_lookup = False @@ -2460,7 +2636,6 @@ def __virtual__(): Only works on Windows systems ''' if salt.utils.is_windows() and HAS_WINDOWS_MODULES: - return __virtualname__ return False @@ -2490,9 +2665,9 @@ def _updateNamespace(item, new_namespace): def _updatePolicyElements(policy_item, regkey): ''' - helper function to add the reg key to each policies element definitions if the key - attribute is not defined to make xpath searching easier - for each child in the policy item + helper function to add the reg key to each policies element definitions if + the key attribute is not defined to make xpath searching easier for each + child in the policy item ''' for child in policy_item.getiterator(): if 'valueName' in child.attrib: @@ -2501,21 +2676,25 @@ def _updatePolicyElements(policy_item, regkey): return policy_item -def _processPolicyDefinitions(policy_def_path='c:\\Windows\\PolicyDefinitions', display_language='en-US'): +def _processPolicyDefinitions(policy_def_path='c:\\Windows\\PolicyDefinitions', + display_language='en-US'): ''' helper function to process all ADMX files in the specified policy_def_path - and build a single XML doc that we can search/use for admx polic processing + and build a single XML doc that we can search/use for ADMX policy processing ''' display_language_fallback = 'en-US' t_policy_definitions = lxml.etree.Element('policyDefinitions') t_policy_definitions.append(lxml.etree.Element('categories')) t_policy_definitions.append(lxml.etree.Element('policies')) t_policy_definitions.append(lxml.etree.Element('policyNamespaces')) - t_policy_definition_resources = lxml.etree.Element('policyDefinitionResources') + t_policy_definition_resources = lxml.etree.Element( + 'policyDefinitionResources') policydefs_policies_xpath = etree.XPath('/policyDefinitions/policies') policydefs_categories_xpath = etree.XPath('/policyDefinitions/categories') - policydefs_policyns_xpath = etree.XPath('/policyDefinitions/policyNamespaces') - policydefs_resources_localname_xpath = etree.XPath('//*[local-name() = "policyDefinitionResources"]/*') + policydefs_policyns_xpath = etree.XPath( + '/policyDefinitions/policyNamespaces') + policydefs_resources_localname_xpath = etree.XPath( + '//*[local-name() = "policyDefinitionResources"]/*') policydef_resources_xpath = etree.XPath('/policyDefinitionResources') for root, dirs, files in os.walk(policy_def_path): if root == policy_def_path: @@ -2559,15 +2738,17 @@ def _processPolicyDefinitions(policy_def_path='c:\\Windows\\PolicyDefinitions', policydefs_policyns_xpath(t_policy_definitions)[0].append(temp_ns) adml_file = os.path.join(root, display_language, os.path.splitext(t_admfile)[0] + '.adml') if not __salt__['file.file_exists'](adml_file): - msg = ('An adml file in the specified adml language "{0}" does not ' - 'exist for the admx "{1}", the fallback languange will be tried.') + msg = ('An ADML file in the specified ADML language "{0}" ' + 'does not exist for the ADMX "{1}", the fallback ' + 'language will be tried.') log.info(msg.format(display_language, t_admfile)) adml_file = os.path.join(root, display_language_fallback, os.path.splitext(t_admfile)[0] + '.adml') if not __salt__['file.file_exists'](adml_file): - msg = ('An adml file in the specified adml language "{0}" and the fallback ' - 'language "{1}" do not exist for the admx "{2}".') + msg = ('An ADML file in the specified ADML language ' + '"{0}" and the fallback language "{1}" do not ' + 'exist for the ADMX "{2}".') raise SaltInvocationError(msg.format(display_language, display_language_fallback, t_admfile)) @@ -2600,7 +2781,8 @@ def _findOptionValueInSeceditFile(option): ''' try: _d = uuid.uuid4().hex - _tfile = '{0}\\{1}'.format(__salt__['config.get']('cachedir'), 'salt-secedit-dump-{0}.txt'.format(_d)) + _tfile = '{0}\\{1}'.format(__salt__['config.get']('cachedir'), + 'salt-secedit-dump-{0}.txt'.format(_d)) _ret = __salt__['cmd.run']('secedit /export /cfg {0}'.format(_tfile)) if _ret: _reader = codecs.open(_tfile, 'r', encoding='utf-16') @@ -2622,8 +2804,10 @@ def _importSeceditConfig(infdata): ''' try: _d = uuid.uuid4().hex - _tSdbfile = '{0}\\{1}'.format(__salt__['config.get']('cachedir'), 'salt-secedit-import-{0}.sdb'.format(_d)) - _tInfFile = '{0}\\{1}'.format(__salt__['config.get']('cachedir'), 'salt-secedit-config-{0}.inf'.format(_d)) + _tSdbfile = '{0}\\{1}'.format(__salt__['config.get']('cachedir'), + 'salt-secedit-import-{0}.sdb'.format(_d)) + _tInfFile = '{0}\\{1}'.format(__salt__['config.get']('cachedir'), + 'salt-secedit-config-{0}.inf'.format(_d)) # make sure our temp files don't already exist _ret = __salt__['file.remove'](_tSdbfile) _ret = __salt__['file.remove'](_tInfFile) @@ -2643,8 +2827,8 @@ def _importSeceditConfig(infdata): def _transformValue(value, policy, transform_type): ''' - helper function to transform the policy value into something that more closely matches - how the policy is displayed in the gpedit gui + helper function to transform the policy value into something that more + closely matches how the policy is displayed in the gpedit GUI ''' t_kwargs = {} if 'Transform' in policy: @@ -2665,11 +2849,12 @@ def _transformValue(value, policy, transform_type): def _validateSetting(value, policy): ''' helper function to validate specified value is appropriate for the policy - if the 'Settings' key is a list, the value will checked that it is in the list - if the 'Settings' key is a dict - we will try to execute the function name from the 'Function' key, passing the value - and additional arguments from the 'Args' dict - if the 'Settings' key is None, we won't do any validation and just return True + if the 'Settings' key is a list, the value will check that it is in the list + if the 'Settings' key is a dict we will try to execute the function name + from the 'Function' key, passing the value and additional arguments from + the 'Args' dict + if the 'Settings' key is None, we won't do any validation and just return + True if the Policy has 'Children', we'll validate their settings too ''' log.debug('validating {0} for policy {1}'.format(value, policy)) @@ -2729,10 +2914,12 @@ def _getRightsAssignments(user_right): def _getAdmlDisplayName(adml_xml_data, display_name): ''' - helper function to take the 'displayName' attribute of an element and find the value from the ADML data + helper function to take the 'displayName' attribute of an element and find + the value from the ADML data - adml_xml_data :: XML data of all adml files to search - display_name :: the value of the displayName attribute from the admx entry to search the adml data for + adml_xml_data :: XML data of all ADML files to search + display_name :: the value of the displayName attribute from the ADMX entry + to search the ADML data for ''' if display_name.startswith('$(') and display_name.endswith(')'): display_name = re.sub(r'(^\$\(|\)$)', '', display_name) @@ -2880,13 +3067,17 @@ def _getDataFromRegPolData(search_string, policy_data, return_value_name=False): def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy_file_data, test_items=True): ''' - helper function to process an enabled/Disabled/true/falseList set - if test_items is True, it will determine if the policy is enabled or disabled - returning True if all items are configured in the registry.pol file and false if they are not + helper function to process an enabled/disabled/true/falseList set - if test_items is False, the expected strings for the items will be returned as a list + if test_items is True, it will determine if the policy is enabled or + disabled returning True if all items are configured in the registry.pol file + and false if they are not - returns True if the enabled/disabledList is 100% configured in the registry.pol file, otherwise returns False + if test_items is False, the expected strings for the items will be returned + as a list + + returns True if the enabled/disabledList is 100% configured in the + registry.pol file, otherwise returns False ''' xpath_string = ('.//*[local-name() = "decimal" or local-name() = "delete"' ' or local-name() = "longDecimal" or local-name() = "string"]') @@ -2904,7 +3095,8 @@ def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy if 'valueName' in item.attrib: item_valuename = item.attrib['valueName'] else: - msg = '{2} item with attributes {0} in policy {1} does not have the required "valueName" attribute' + msg = ('{2} item with attributes {0} in policy {1} does not ' + 'have the required "valueName" attribute') log.error(msg.format(item.attrib, policy_element.attrib, etree.QName(list_element).localname)) break for value_item in value_item_child_xpath(item): @@ -2963,9 +3155,11 @@ def _checkValueItemParent(policy_element, policy_name, policy_key, return False -def _buildKnownDataSearchString(reg_key, reg_valueName, reg_vtype, reg_data, check_deleted=False): +def _buildKnownDataSearchString(reg_key, reg_valueName, reg_vtype, reg_data, + check_deleted=False): ''' - helper function similar to _processValueItem to build a search string for a known key/value/type/data + helper function similar to _processValueItem to build a search string for a + known key/value/type/data ''' registry = Registry() this_element_value = None @@ -3004,16 +3198,22 @@ def _buildKnownDataSearchString(reg_key, reg_valueName, reg_vtype, reg_data, che def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, check_deleted=False, this_element_value=None): ''' - helper function to process an value type item and generate the expected string in the Registry.pol file + helper function to process a value type item and generate the expected + string in the Registry.pol file element - the element to process - reg_key - the registry key associated with the element (some inherit from their parent policy) - reg_valuename - the registry valueName associated with the element (some inherit from their parent policy) + reg_key - the registry key associated with the element (some inherit from + their parent policy) + reg_valuename - the registry valueName associated with the element (some + inherit from their parent policy) policy - the parent policy element - parent_element - the parent element (primarily passed in to differentiate children of "elements" objects - check_deleted - if the returned expected string should be for a deleted value - this_element_value - a specific value to place into the expected string returned - for "elements" children whose values are specified by the user + parent_element - the parent element (primarily passed in to differentiate + children of "elements" objects + check_deleted - if the returned expected string should be for a deleted + value + this_element_value - a specific value to place into the expected string + returned for "elements" children whose values are + specified by the user ''' registry = Registry() expected_string = None @@ -3027,23 +3227,26 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, for val in struct.unpack('2H', struct.pack('I', int(element.attrib['value']))): this_element_value = this_element_value + unichr(val) else: - msg = 'The {2} child {1} element for the policy with attributes: {0} does not have the required' - msg = ' "value" attribute. The elment attributes are: {3}' + msg = ('The {2} child {1} element for the policy with attributes: ' + '{0} does not have the required "value" attribute. The ' + 'element attributes are: {3}') log.error(msg.format(policy.attrib, etree.QName(element).localname, etree.QName(parent_element).localname, element.attrib)) return None elif etree.QName(element).localname == 'longDecimal' and etree.QName(parent_element).localname != 'elements': - # WARNING: no longDecimals in current ADMX files included with 2012 server, so untested/assumed + # WARNING: no longDecimals in current ADMX files included with 2012 + # server, so untested/assumed this_vtype = 'REG_QWORD' if 'value' in element.attrib: this_element_value = '' for val in struct.unpack('4H', struct.pack('I', int(element.attrib['value']))): this_element_value = this_element_value + unichr(val) else: - msg = ('The {2} child {1} element for the policy with attributes: {0} does not have the required' - ' "value" attribute. The elment attributes are: {3}') + msg = ('The {2} child {1} element for the policy with attributes: ' + '{0} does not have the required "value" attribute. The ' + 'element attributes are: {3}') log.error(msg.format(policy.attrib, etree.QName(element).localname, etree.QName(parent_element).localname, @@ -3055,8 +3258,8 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, elif etree.QName(parent_element).localname == 'elements': standard_element_expected_string = True if etree.QName(element).localname == 'boolean': - # a boolean element that has no children will add a REG_DWORD == 1 on true - # or delete the value on false + # a boolean element that has no children will add a REG_DWORD == 1 + # on true or delete the value on false # https://msdn.microsoft.com/en-us/library/dn605978(v=vs.85).aspx if this_element_value is False: check_deleted = True @@ -3070,6 +3273,8 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, if this_element_value is not None: temp_val = '' for v in struct.unpack('2H', struct.pack('I', int(this_element_value))): + # Not Python 3 compliant + # `unichr` not available in Python 3 temp_val = temp_val + unichr(v) this_element_value = temp_val if 'storeAsText' in element.attrib: @@ -3085,6 +3290,8 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, if this_element_value is not None: temp_val = '' for v in struct.unpack('4H', struct.pack('I', int(this_element_value))): + # Not Python 3 compliant + # `unichr` not available in Python 3 temp_val = temp_val + unichr(v) this_element_value = temp_val if 'storeAsText' in element.attrib: @@ -3168,6 +3375,8 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, reg_key, reg_valuename, chr(registry.vtype[this_vtype]), + # Not Python 3 compliant + # `unichr` not available in Python 3 unichr(len(this_element_value.encode('utf-16-le'))), this_element_value) else: @@ -3184,6 +3393,8 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, reg_key, reg_valuename, chr(registry.vtype[this_vtype]), + # Not Python 3 compliant + # `unichr` not available in Python 3 unichr(len(' {0}'.format(chr(0)).encode('utf-16-le'))), ' ') else: @@ -3192,6 +3403,8 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, reg_key, reg_valuename, chr(registry.vtype[this_vtype]), + # Not Python 3 compliant + # `unichr` not available in Python 3 unichr(len(this_element_value.encode('utf-16-le'))), this_element_value) return expected_string @@ -3204,9 +3417,10 @@ def _checkAllAdmxPolicies(policy_class, hierarchical_return=False, return_not_configured=False): ''' - rewrite of _getAllAdminTemplateSettingsFromRegPolFile where instead of looking only - at the contents of the file, we're going to loop through every policy and look - in the registry.pol file to determine if it is enabled/disabled/not configured + rewrite of _getAllAdminTemplateSettingsFromRegPolFile where instead of + looking only at the contents of the file, we're going to loop through every + policy and look in the registry.pol file to determine if it is + enabled/disabled/not configured ''' log.debug('POLICY CLASS == {0}'.format(policy_class)) module_policy_data = _policy_info() @@ -3262,14 +3476,16 @@ def _checkAllAdmxPolicies(policy_class, if 'key' in admx_policy.attrib: this_key = admx_policy.attrib['key'] else: - log.error('policy item {0} does not have the required "key" attribute'.format(admx_policy.attrib)) + log.error('policy item {0} does not have the required "key" ' + 'attribute'.format(admx_policy.attrib)) break if 'valueName' in admx_policy.attrib: this_valuename = admx_policy.attrib['valueName'] if 'name' in admx_policy.attrib: this_policyname = admx_policy.attrib['name'] else: - log.error('policy item {0} does not have the required "name" attribute'.format(admx_policy.attrib)) + log.error('policy item {0} does not have the required "name" ' + 'attribute'.format(admx_policy.attrib)) break if ENABLED_VALUE_XPATH(admx_policy) and this_policy_setting == 'Not Configured': element_only_enabled_disabled = False @@ -3311,7 +3527,8 @@ def _checkAllAdmxPolicies(policy_class, policy_vals[this_policyname] = this_policy_setting if not explicit_enable_disable_value_setting and this_valuename: - # the policy has a key/valuename but no explicit enabled/Disabled Value or List + # the policy has a key/valuename but no explicit enabled/Disabled + # Value or List # these seem to default to a REG_DWORD 1 = "Enabled" **del. = "Disabled" if _regexSearchRegPolData(re.escape(_buildKnownDataSearchString(this_key, this_valuename, @@ -3375,7 +3592,7 @@ def _checkAllAdmxPolicies(policy_class, policy_disabled_elements = policy_disabled_elements + 1 msg = 'element {0} is configured false' log.debug(msg.format(child_item.attrib['id'])) - # WARNING - no standard admx files use true/falseList + # WARNING - no standard ADMX files use true/falseList # so this hasn't actually been tested if TRUE_LIST_XPATH(child_item) and this_element_name not in configured_elements: log.debug('checking trueList') @@ -3599,7 +3816,8 @@ def _build_parent_list(policy_definition, return_full_policy_names, adml_policy_resources): ''' - helper functioon to build a list containing parent elements of the ADMX policy + helper function to build a list containing parent elements of the ADMX + policy ''' parent_list = [] policy_namespace = policy_definition.nsmap.keys()[0] @@ -3629,7 +3847,8 @@ def _admx_policy_parent_walk(path, return_full_policy_names, adml_policy_resources): ''' - helper function to recursively walk up the ADMX namespaces and build the hierarchy for the policy + helper function to recursively walk up the ADMX namespaces and build the + hierarchy for the policy ''' category_xpath_string = '/policyDefinitions/categories/{0}:category[@name="{1}"]' using_xpath_string = '/policyDefinitions/policyNamespaces/{0}:using' @@ -3712,8 +3931,8 @@ def _write_regpol_data(data_to_write, policy_file_path): def _policyFileReplaceOrAppendList(string_list, policy_data): ''' - helper function to take a list of strings for registry.pol file data - and update existing strings or append the strings + helper function to take a list of strings for registry.pol file data and + update existing strings or append the strings ''' if not policy_data: policy_data = '' @@ -3739,8 +3958,8 @@ def _policyFileReplaceOrAppendList(string_list, policy_data): def _policyFileReplaceOrAppend(this_string, policy_data, append_only=False): ''' - helper function to take a admx policy string for registry.pol file data - and update existing string or append the string to the data + helper function to take a ADMX policy string for registry.pol file data and + update existing string or append the string to the data ''' # we are going to clean off the special pre-fixes, so we get only the valuename if not policy_data: @@ -3775,7 +3994,8 @@ def _writeAdminTemplateRegPolFile(admtemplate_data, u''' helper function to prep/write adm template data to the Registry.pol file - each file begins with REGFILE_SIGNATURE (u'\u5250\u6765') and REGISTRY_FILE_VERSION (u'\x01\00') + each file begins with REGFILE_SIGNATURE (u'\u5250\u6765') and + REGISTRY_FILE_VERSION (u'\x01\00') https://msdn.microsoft.com/en-us/library/aa374407(VS.85).aspx [Registry Path;Reg Value;Reg Type;SizeInBytes;Data] @@ -4133,8 +4353,11 @@ def _writeGpoScript(psscript=False): ''' helper function to write local GPO startup/shutdown script - scripts are stored in scripts.ini and psscripts.ini files in WINDIR\\System32\\GroupPolicy\\Machine|User\\Scripts + scripts are stored in scripts.ini and psscripts.ini files in + ``WINDIR\\System32\\GroupPolicy\\Machine|User\\Scripts`` + these files have the hidden attribute set + files have following format: empty line [Startup] @@ -4143,12 +4366,17 @@ def _writeGpoScript(psscript=False): [Shutdown] 0CmdLine= 0Parameters= + Number is incremented for each script added - psscript file also has the option of a [ScriptsConfig] section, which has the following two parameters: + + psscript file also has the option of a [ScriptsConfig] section, which has + the following two parameters: StartExecutePSFirst EndExecutePSFirst - these can be set to true/false to denote if the powershell startup/shutdown scripts execute - first (true) or last (false), if the value isn't set, then it is 'Not Configured' in the GUI + + these can be set to True/False to denote if the powershell startup/shutdown + scripts execute first (True) or last (False), if the value isn't set, then + it is 'Not Configured' in the GUI ''' _machineScriptPolicyPath = os.path.join(os.getenv('WINDIR'), 'System32', @@ -4211,7 +4439,7 @@ def _lookup_admin_template(policy_name, policy_aliases.append('\\'.join(full_path_list)) return (True, the_policy, policy_aliases, None) else: - msg = 'Admx policy name/id "{0}" is used in multiple admx files' + msg = 'ADMX policy name/id "{0}" is used in multiple ADMX files' return (False, None, [], msg) else: adml_search_results = ADML_SEARCH_XPATH(adml_policy_resources, @@ -4230,7 +4458,7 @@ def _lookup_admin_template(policy_name, if len(adml_search_results) > 1: multiple_adml_entries = True for adml_search_result in adml_search_results: - dmsg = 'found an adml entry matching the string! {0} -- {1}' + dmsg = 'found an ADML entry matching the string! {0} -- {1}' log.debug(dmsg.format(adml_search_result.tag, adml_search_result.attrib)) display_name_searchval = '$({0}.{1})'.format( @@ -4257,7 +4485,7 @@ def _lookup_admin_template(policy_name, else: found = True if found: - dmsg = ('found the admx policy matching ' + dmsg = ('found the ADMX policy matching ' 'the display name {1} -- {0}') log.debug(dmsg.format(search_result, policy_name)) if 'name' in search_result.attrib: @@ -4277,7 +4505,7 @@ def _lookup_admin_template(policy_name, policy_aliases.append('\\'.join(full_path_list)) return (True, search_result, policy_aliases, None) else: - msg = ('Admx policy with the display name {0} does not' + msg = ('ADMX policy with the display name {0} does not' 'have the required name attribtue') msg = msg.format(policy_name) return (False, None, [], msg) @@ -4301,7 +4529,7 @@ def _lookup_admin_template(policy_name, msg = 'Unable to find a policy with the name "{0}".'.format(policy_name) return (False, None, [], msg) if suggested_policies: - msg = ('Adml policy name "{0}" is used as the display name' + msg = ('ADML policy name "{0}" is used as the display name' ' for multiple policies.' ' These policies matched: {1}' '. You can utilize these long names to' @@ -4322,16 +4550,24 @@ def get_policy_info(policy_name, policy_class, adml_language='en-US'): ''' - returns information about a specified policy + Returns information about a specified policy - :str: policy_name - the name of the policy to lookup + Args: + policy_name (str): + The name of the policy to lookup + policy_class (str): + The class of policy, i.e. machine, user, both + adml_language (str): + The ADML language to use for Administrative Template data lookup + + Returns: + dict: Information about the specified policy + + CLI Example: - :str: policy_class - the class of policy, i.e. machine, user, both + .. code-block:: bash - :str: adml_language - the adml language to use for Administrative Template data lookup + salt '*' lgpo.get_policy_info 'Max password age' machine ''' # return the possible policy names and element names ret = {'policy_name': policy_name, @@ -4398,34 +4634,40 @@ def get(policy_class=None, return_full_policy_names=True, ''' Get a policy value - :param string policy_class: - Some policies are both user and computer, by default all policies will be pulled, - but this can be used to retrieve only a specific policy class - User/USER/user = retrieve user policies - Machine/MACHINE/machine/Computer/COMPUTER/computer = retrieve machine/computer policies + Args: + + policy_class (str): + Some policies are both user and computer, by default all policies + will be pulled, but this can be used to retrieve only a specific + policy class User/USER/user = retrieve user policies + Machine/MACHINE/machine/Computer/COMPUTER/computer = retrieve + machine/computer policies - :param boolean return_full_policy_names: - True/False to return the policy name as it is seen in the gpedit.msc GUI or - to only return the policy key/id. + return_full_policy_names (bool): + True/False to return the policy name as it is seen in the + ``gpedit.msc`` GUI or to only return the policy key/id. - :param boolean hierarchical_return: - True/False to return the policy data in the hierarchy as seen in the gpedit.msc gui - The default of False will return data split only into User/Computer configuration sections + hierarchical_return (bool): + True/False to return the policy data in the hierarchy as seen in the + ``gpedit.msc`` GUI. The default of False will return data split only + into User/Computer configuration sections - :param str adml_language: - The adml language to use for processing display/descriptive names - and enumeration values of admx template data, defaults to en-US + adml_language (str): + The ADML language to use for processing display/descriptive names + and enumeration values of ADMX template data, defaults to en-US - :param boolean return_not_configured: - Include Administrative Template policies that are 'Not Configured' in the return data + return_not_configured (bool): + Include Administrative Template policies that are 'Not Configured' + in the return data - :rtype: dict + Returns: + dict: A dictionary containing the policy values for the specified class CLI Example: .. code-block:: bash - salt '*' lgpo.get return_full_policy_names=True + salt '*' lgpo.get machine return_full_policy_names=True ''' vals = {} @@ -4533,21 +4775,27 @@ def set_computer_policy(name, ''' Set a single computer policy - :param string name - the name of the policy to configure + Args: + name (str): + The name of the policy to configure - :param object setting - the setting to configure the named policy with + setting (str): + The setting to configure the named policy with - :param bool cumulative_rights_assignments - Determine how user rights assignment policies are configured. + cumulative_rights_assignments (bool): Determine how user rights + assignment policies are configured. If True, user right assignment + specifications are simply added to the existing policy. If False, + only the users specified will get the right (any existing will have + the right revoked) - If True, user right assignment specifications are simply added to the existing policy - If False, only the users specified will get the right (any existing will have the right revoked) + adml_language (str): The language files to use for looking up + Administrative Template policy data (i.e. how the policy is + displayed in the GUI). Defaults to 'en-US' (U.S. English). - :param str adml_language: - The language files to use for looking up Administrative Template policy data (i.e. how the policy is - displayed in the GUI). Defaults to 'en-US' (U.S. English). + Returns: + bool: True if successful, otherwise False + + CLI Example: .. code-block:: bash @@ -4568,15 +4816,23 @@ def set_user_policy(name, ''' Set a single user policy - :param string name - the name of the policy to configure + Args: - :param object setting - the setting to configure the named policy with + name (str): + The name of the policy to configure - :param str adml_language: - The language files to use for looking up Administrative Template policy data (i.e. how the policy is - displayed in the GUI). Defaults to 'en-US' (U.S. English). + setting (str): + The setting to configure the named policy with + + adml_language (str): + The language files to use for looking up Administrative Template + policy data (i.e. how the policy is displayed in the GUI). Defaults + to 'en-US' (U.S. English). + + Returns: + bool: True if successful, Otherwise False + + CLI Example: .. code-block:: bash @@ -4597,48 +4853,70 @@ def set_(computer_policy=None, user_policy=None, ''' Set a local server policy. - :param dict computer_policy: - A dictionary of "policyname: value" pairs of computer policies to set - 'value' should be how it is displayed in the gpedit gui, i.e. if a setting can - be 'Enabled'/'Disabled', then that should be passed + Args: + + computer_policy (dict): + A dictionary of "policyname: value" pairs of computer policies to + set. 'value' should be how it is displayed in the gpedit GUI, i.e. + if a setting can be 'Enabled'/'Disabled', then that should be passed + + Administrative Template data may require dicts within dicts, to + specify each element of the Administrative Template policy. + Administrative Templates policies are always cumulative. + + Policy names can be specified in a number of ways based on the type + of policy: + + Windows Settings Policies: + + These policies can be specified using the GUI display name + or the key name from the _policy_info class in this module. + The GUI display name is also contained in the _policy_info + class in this module. + + Administrative Template Policies: + + These can be specified using the policy name as displayed in + the GUI (case sensitive). Some policies have the same name, + but a different location (for example, "Access data sources + across domains"). These can be differentiated by the "path" + in the GUI (for example, "Windows Components\\Internet + Explorer\\Internet Control Panel\\Security Page\\Internet + Zone\\Access data sources across domains"). - Administrative Template data may require dicts within dicts, to specify each element - of the Administrative Template policy. Administrative Templates policies are always cumulative. + Additionally, policies can be specified using the "name" and + "id" attributes from the ADMX files. - Policy names can be specified in a number of ways based on the type of policy: - Windows Settings Policies: - These policies can be specified using the GUI display name or the key name from - the _policy_info class in this module. The GUI display name is also contained in - the _policy_info class in this module. - Administrative Template Policies: - These can be specified using the policy name as displayed in the GUI (case sensitive). - Some policies have the same name, but a different location (for example, "Access data - sources across domains"). These can be differentiated by the "path" in the GUI - (for example, "Windows Components\\Internet Explorer\\Internet Control Panel\\Security Page\\Internet Zone\\Access data sources across domains"). - Additionally, policies can be specified using the "name" and "id" attributes from the - admx files. + For Administrative Templates that have policy elements, each + element can be specified using the text string as seen in + the GUI or using the ID attribute from the ADMX file. Due to + the way some of the GUI text is laid out, some policy + element names could include descriptive text that appears + lbefore the policy element in the GUI. - For Administrative Templates that have policy elements, each element can be specified using the text string - as seen in the GUI or using the ID attribute from the admx file. Due to the way some of the GUI text is laid out, - some policy element names could include descriptive text that appears before the policy element in the gui. + Use the get_policy_info function for the policy name to view + the element ID/names that the module will accept. - Use the get_policy_info function for the policy name to view the element ID/names that the module will accept. + user_policy (dict): + The same setup as the computer_policy, except with data to configure + the local user policy. - :param dict user_policy: - The same setup as the computer_policy, except with data to configure the local - user policy. + cumulative_rights_assignments (bool): + Determine how user rights assignment policies are configured. - :param boolean cumulative_rights_assignments: - Determine how user rights assignment policies are configured. + If True, user right assignment specifications are simply added to + the existing policy - If True, user right assignment specifications are simply added to the existing policy - If False, only the users specified will get the right (any existing will have the right revoked) + If False, only the users specified will get the right (any existing + will have the right revoked) - :param str adml_language: - The language files to use for looking up Administrative Template policy data (i.e. how the policy is - displayed in the GUI). Defaults to 'en-US' (U.S. English). + adml_language (str): + The language files to use for looking up Administrative Template + policy data (i.e. how the policy is displayed in the GUI). Defaults + to 'en-US' (U.S. English). - :rtype: boolean + Returns: + bool: True is successful, otherwise False CLI Example: From 981ec89a4d6678915374cb9a22b6ad3886fc25b3 Mon Sep 17 00:00:00 2001 From: Ethan Moore Date: Wed, 21 Sep 2016 21:38:46 +0000 Subject: [PATCH 07/59] update command line example to correct policy name --- salt/modules/win_lgpo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/win_lgpo.py b/salt/modules/win_lgpo.py index aea23b27bb99..770899cc4ccf 100644 --- a/salt/modules/win_lgpo.py +++ b/salt/modules/win_lgpo.py @@ -4567,7 +4567,7 @@ def get_policy_info(policy_name, .. code-block:: bash - salt '*' lgpo.get_policy_info 'Max password age' machine + salt '*' lgpo.get_policy_info 'Maximum password age' machine ''' # return the possible policy names and element names ret = {'policy_name': policy_name, From f833bf3a7945fe347352781515e516678b240689 Mon Sep 17 00:00:00 2001 From: David Boucha Date: Thu, 2 Feb 2017 11:38:01 -0700 Subject: [PATCH 08/59] Don't overwrite the minion_ids var that was passed The minion_ids var that was passed into the function was being overwritten by a list of ALL the minion ids found in the minion cache Fixes: #38003 ZD1187 --- salt/utils/master.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/salt/utils/master.py b/salt/utils/master.py index de78625e7363..de4cb1ab6ad0 100644 --- a/salt/utils/master.py +++ b/salt/utils/master.py @@ -109,7 +109,6 @@ def _get_cached_mine_data(self, *minion_ids): log.debug('Skipping cached mine data minion_data_cache' 'and enfore_mine_cache are both disabled.') return mine_data - minion_ids = self.cache.list('minions') for minion_id in minion_ids: if not salt.utils.verify.valid_id(self.opts, minion_id): continue @@ -127,7 +126,6 @@ def _get_cached_minion_data(self, *minion_ids): log.debug('Skipping cached data because minion_data_cache is not ' 'enabled.') return grains, pillars - minion_ids = self.cache.list('minions') for minion_id in minion_ids: if not salt.utils.verify.valid_id(self.opts, minion_id): continue From 0340614d159fc0aaa5d89607106871d9eef7f5a6 Mon Sep 17 00:00:00 2001 From: David Boucha Date: Thu, 2 Feb 2017 11:58:42 -0700 Subject: [PATCH 09/59] return all minions' grains if no tgt --- salt/utils/master.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/salt/utils/master.py b/salt/utils/master.py index de4cb1ab6ad0..483c0568e309 100644 --- a/salt/utils/master.py +++ b/salt/utils/master.py @@ -109,6 +109,8 @@ def _get_cached_mine_data(self, *minion_ids): log.debug('Skipping cached mine data minion_data_cache' 'and enfore_mine_cache are both disabled.') return mine_data + if not minion_ids: + minion_ids = self.cache.list('minions') for minion_id in minion_ids: if not salt.utils.verify.valid_id(self.opts, minion_id): continue @@ -126,6 +128,8 @@ def _get_cached_minion_data(self, *minion_ids): log.debug('Skipping cached data because minion_data_cache is not ' 'enabled.') return grains, pillars + if not minion_ids: + minion_ids = self.cache.list('minions') for minion_id in minion_ids: if not salt.utils.verify.valid_id(self.opts, minion_id): continue From 6858658cc2a4dab2d0361002996b3438600b7c5b Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Thu, 2 Feb 2017 22:26:49 -0500 Subject: [PATCH 10/59] The selinux.mode state only checked the current status of SELinux in memory (getenforce) when determining if changes needed to be made. The /etc/selinux/config file could have a different value, and it would not be changed. This commit enhances idempotency of the state in regards to both the in-memory and configuration file enforcement of SELinux. --- salt/modules/selinux.py | 21 +++++++++++++++++++++ salt/states/selinux.py | 7 +++++++ 2 files changed, 28 insertions(+) diff --git a/salt/modules/selinux.py b/salt/modules/selinux.py index 9913dc223269..dd809682cb74 100644 --- a/salt/modules/selinux.py +++ b/salt/modules/selinux.py @@ -89,6 +89,27 @@ def getenforce(): return 'Disabled' +def getconfig(): + ''' + Return the selinux mode from the config file + + CLI Example: + + .. code-block:: bash + + salt '*' selinux.getconfig + ''' + try: + config = '/etc/selinux/config' + with salt.utils.fopen(config, 'r') as _fp: + for line in _fp: + if line.strip().startswith('SELINUX='): + return line.split('=')[1].capitalize().strip() + except (IOError, OSError, AttributeError): + return None + return None + + def setenforce(mode): ''' Set the SELinux enforcing mode diff --git a/salt/states/selinux.py b/salt/states/selinux.py index 024cb790b750..e64a1f5af1d4 100644 --- a/salt/states/selinux.py +++ b/salt/states/selinux.py @@ -94,7 +94,14 @@ def mode(name): if tmode == 'unknown': ret['comment'] = '{0} is not an accepted mode'.format(name) return ret + # Either the current mode in memory or a non-matching config value + # will trigger setenforce mode = __salt__['selinux.getenforce']() + config = __salt__['selinux.getconfig']() + # Just making sure the oldmode reflects the thing that didn't match tmode + if mode == tmode and mode != config and tmode != config: + mode = config + if mode == tmode: ret['result'] = True ret['comment'] = 'SELinux is already in {0} mode'.format(tmode) From 3ef809fb0f6f4b6ceccc785fbee8042f6feebd12 Mon Sep 17 00:00:00 2001 From: Clint Armstrong Date: Mon, 30 Jan 2017 08:11:12 -0500 Subject: [PATCH 11/59] passphrase for csr --- salt/modules/x509.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/salt/modules/x509.py b/salt/modules/x509.py index 39880688ca51..8a40f9a9cce0 100644 --- a/salt/modules/x509.py +++ b/salt/modules/x509.py @@ -1584,8 +1584,15 @@ def create_csr(path=None, text=False, **kwargs): if 'public_key' not in kwargs: kwargs['public_key'] = kwargs['private_key'] + if 'private_key_passphrase' not in kwargs: + kwargs['private_key_passphrase'] = None if 'public_key_passphrase' not in kwargs: kwargs['public_key_passphrase'] = None + if kwargs['public_key_passphrase'] and not kwargs['private_key_passphrase']: + kwargs['private_key_passphrase'] = kwargs['public_key_passphrase'] + if kwargs['private_key_passphrase'] and not kwargs['public_key_passphrase']: + kwargs['public_key_passphrase'] = kwargs['private_key_passphrase'] + csr.set_pubkey(get_public_key(kwargs['public_key'], passphrase=kwargs['public_key_passphrase'], asObj=True)) @@ -1626,7 +1633,7 @@ def create_csr(path=None, text=False, **kwargs): csr.add_extensions(extstack) csr.sign(_get_private_key_obj(kwargs['private_key'], - passphrase=kwargs['public_key_passphrase']), kwargs['algorithm']) + passphrase=kwargs['private_key_passphrase']), kwargs['algorithm']) if path: return write_pem( From 6497094ba7ccf3794eee12eacb4974ad809ff617 Mon Sep 17 00:00:00 2001 From: Clint Armstrong Date: Mon, 30 Jan 2017 08:48:41 -0500 Subject: [PATCH 12/59] passphrase for crl --- salt/modules/x509.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/salt/modules/x509.py b/salt/modules/x509.py index 8a40f9a9cce0..f1a0f008e43a 100644 --- a/salt/modules/x509.py +++ b/salt/modules/x509.py @@ -848,6 +848,7 @@ def create_private_key(path=None, def create_crl( # pylint: disable=too-many-arguments,too-many-locals path=None, text=False, signing_private_key=None, + signing_private_key_passphrase=None, signing_cert=None, revoked=None, include_expired=False, days_valid=100, digest=''): ''' @@ -866,6 +867,9 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals A path or string of the private key in PEM format that will be used to sign this crl. This is required. + signing_private_key_passphrase: + Passphrase to decrypt the private key. + signing_cert: A certificate matching the private key that will be used to sign this crl. This is required. @@ -969,7 +973,8 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals cert = OpenSSL.crypto.load_certificate( OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type='CERTIFICATE')) - signing_private_key = _text_or_file(signing_private_key) + signing_private_key = _get_private_key_obj(signing_private_key, + passphrase=signing_private_key_passphrase).as_pem(cipher=None) key = OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key)) From 403000d375e8a5bc8e515f4925ff5abf169bca20 Mon Sep 17 00:00:00 2001 From: Clint Armstrong Date: Mon, 30 Jan 2017 07:50:59 -0500 Subject: [PATCH 13/59] recreate cert on bad password --- salt/states/x509.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/salt/states/x509.py b/salt/states/x509.py index 1c63604e5682..a96108690876 100644 --- a/salt/states/x509.py +++ b/salt/states/x509.py @@ -168,6 +168,10 @@ # Import 3rd-party libs import salt.ext.six as six +try: + from M2Crypto.RSA import RSAError +except ImportError: + pass def __virtual__(): ''' @@ -227,7 +231,7 @@ def _check_private_key(name, bits=2048, passphrase=None, new=False): try: current_bits = __salt__['x509.get_private_key_size']( private_key=name, passphrase=passphrase) - except salt.exceptions.SaltInvocationError: + except (salt.exceptions.SaltInvocationError, RSAError): pass return current_bits == bits and not new From 36dcf5f3da4bc042c3caccf32b8f06d701c94d75 Mon Sep 17 00:00:00 2001 From: Clint Armstrong Date: Fri, 3 Feb 2017 08:07:18 -0500 Subject: [PATCH 14/59] only overwrite if overwrite option is specified --- salt/states/x509.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/salt/states/x509.py b/salt/states/x509.py index a96108690876..996a7aeabf18 100644 --- a/salt/states/x509.py +++ b/salt/states/x509.py @@ -173,6 +173,7 @@ except ImportError: pass + def __virtual__(): ''' only load this module if the corresponding execution module is loaded @@ -225,14 +226,20 @@ def _get_file_args(name, **kwargs): return file_args, extra_args -def _check_private_key(name, bits=2048, passphrase=None, new=False): +def _check_private_key(name, bits=2048, passphrase=None, + overwrite=False, new=False): current_bits = 0 if os.path.isfile(name): try: current_bits = __salt__['x509.get_private_key_size']( private_key=name, passphrase=passphrase) - except (salt.exceptions.SaltInvocationError, RSAError): + except salt.exceptions.SaltInvocationError: pass + except RSAError: + if overwrite: + pass + raise salt.exceptions.CommandExecutionError( + 'The provided passphrase cannot decrypt the private key.') return current_bits == bits and not new @@ -242,6 +249,7 @@ def private_key_managed(name, passphrase=None, cipher='aes_128_cbc', new=False, + overwrite=False, verbose=True, **kwargs): ''' @@ -263,6 +271,9 @@ def private_key_managed(name, Always create a new key. Defaults to False. Combining new with :mod:`prereq `, or when used as part of a `managed_private_key` can allow key rotation whenever a new certificiate is generated. + overwrite: + Overwrite an existing private key if the provided passphrase cannot decrypt it. + verbose: Provide visual feedback on stdout, dots while key is generated. Default is True. @@ -290,7 +301,7 @@ def private_key_managed(name, ''' file_args, kwargs = _get_file_args(name, **kwargs) new_key = False - if _check_private_key(name, bits, passphrase, new): + if _check_private_key(name, bits, passphrase, overwrite, new): file_args['contents'] = __salt__['x509.get_pem_entry']( name, pem_type='RSA PRIVATE KEY') else: @@ -414,6 +425,7 @@ def certificate_managed(name, private_key_args = { 'name': name, 'new': False, + 'overwrite': False, 'bits': 2048, 'passphrase': None, 'cipher': 'aes_128_cbc', @@ -429,6 +441,7 @@ def certificate_managed(name, if _check_private_key(private_key_args['name'], private_key_args['bits'], private_key_args['passphrase'], + private_key_args['overwrite'], private_key_args['new']): private_key = __salt__['x509.get_pem_entry']( private_key_args['name'], pem_type='RSA PRIVATE KEY') From 6a8046970ed4de811b9705384839079624ba5142 Mon Sep 17 00:00:00 2001 From: Clint Armstrong Date: Fri, 3 Feb 2017 08:30:24 -0500 Subject: [PATCH 15/59] fix csr bugs and pep8 --- salt/modules/x509.py | 11 ++++++++--- salt/states/x509.py | 34 ++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/salt/modules/x509.py b/salt/modules/x509.py index f1a0f008e43a..349997988af5 100644 --- a/salt/modules/x509.py +++ b/salt/modules/x509.py @@ -974,7 +974,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type='CERTIFICATE')) signing_private_key = _get_private_key_obj(signing_private_key, - passphrase=signing_private_key_passphrase).as_pem(cipher=None) + passphrase=signing_private_key_passphrase).as_pem(cipher=None) key = OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key)) @@ -1593,9 +1593,11 @@ def create_csr(path=None, text=False, **kwargs): kwargs['private_key_passphrase'] = None if 'public_key_passphrase' not in kwargs: kwargs['public_key_passphrase'] = None - if kwargs['public_key_passphrase'] and not kwargs['private_key_passphrase']: + if kwargs['public_key_passphrase'] and not kwargs[ + 'private_key_passphrase']: kwargs['private_key_passphrase'] = kwargs['public_key_passphrase'] - if kwargs['private_key_passphrase'] and not kwargs['public_key_passphrase']: + if kwargs['private_key_passphrase'] and not kwargs[ + 'public_key_passphrase']: kwargs['public_key_passphrase'] = kwargs['private_key_passphrase'] csr.set_pubkey(get_public_key(kwargs['public_key'], @@ -1619,6 +1621,9 @@ def create_csr(path=None, text=False, **kwargs): critical = True extval = extval[9:] + if extname == 'subjectKeyIdentifier' and 'hash' in extval: + extval = extval.replace('hash', _get_pubkey_hash(csr)) + if extname == 'subjectAltName': extval = extval.replace('IP Address', 'IP') diff --git a/salt/states/x509.py b/salt/states/x509.py index 996a7aeabf18..71a8b9ee2d40 100644 --- a/salt/states/x509.py +++ b/salt/states/x509.py @@ -227,7 +227,7 @@ def _get_file_args(name, **kwargs): def _check_private_key(name, bits=2048, passphrase=None, - overwrite=False, new=False): + new=False, overwrite=False): current_bits = 0 if os.path.isfile(name): try: @@ -236,10 +236,10 @@ def _check_private_key(name, bits=2048, passphrase=None, except salt.exceptions.SaltInvocationError: pass except RSAError: - if overwrite: - pass - raise salt.exceptions.CommandExecutionError( - 'The provided passphrase cannot decrypt the private key.') + if not overwrite: + raise salt.exceptions.CommandExecutionError( + 'The provided passphrase cannot decrypt the private key.') + pass return current_bits == bits and not new @@ -301,7 +301,8 @@ def private_key_managed(name, ''' file_args, kwargs = _get_file_args(name, **kwargs) new_key = False - if _check_private_key(name, bits, passphrase, overwrite, new): + if _check_private_key( + name, bits=bits, passphrase=passphrase, new=new, overwrite=overwrite): file_args['contents'] = __salt__['x509.get_pem_entry']( name, pem_type='RSA PRIVATE KEY') else: @@ -344,7 +345,12 @@ def csr_managed(name, - L: Salt Lake City - keyUsage: 'critical dataEncipherment' ''' - old = __salt__['x509.read_csr'](name) + try: + old = __salt__['x509.read_csr'](name) + except salt.exceptions.SaltInvocationError: + old = '{0} is not a valid csr.'.format(name) + pass + file_args, kwargs = _get_file_args(name, **kwargs) file_args['contents'] = __salt__['x509.create_csr'](text=True, **kwargs) @@ -439,10 +445,10 @@ def certificate_managed(name, private_key_args['new'] = False if _check_private_key(private_key_args['name'], - private_key_args['bits'], - private_key_args['passphrase'], - private_key_args['overwrite'], - private_key_args['new']): + bits=private_key_args['bits'], + passphrase=private_key_args['passphrase'], + new=private_key_args['new'], + overwrite=private_key_args['overwrite']): private_key = __salt__['x509.get_pem_entry']( private_key_args['name'], pem_type='RSA PRIVATE KEY') else: @@ -568,6 +574,7 @@ def certificate_managed(name, def crl_managed(name, signing_private_key, + signing_private_key_passphrase=None, signing_cert=None, revoked=None, days_valid=100, @@ -585,6 +592,9 @@ def crl_managed(name, The private key that will be used to sign this crl. This is usually your CA's private key. + signing_private_key_passphrase: + Passphrase to decrypt the private key. + signing_cert: The certificate of the authority that will be used to sign this crl. This is usually your CA's certificate. @@ -654,7 +664,7 @@ def crl_managed(name, else: current = '{0} does not exist.'.format(name) - new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key, + new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key, signing_private_key_passphrase=signing_private_key_passphrase, signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, digest=digest, include_expired=include_expired) new = __salt__['x509.read_crl'](crl=new_crl) From a6a24e1a1b0d30920db0f1a4752cebf580fc8df6 Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Fri, 3 Feb 2017 09:08:31 -0500 Subject: [PATCH 16/59] Addressed edge case when attempting to set the config file to 'Disabled'. The state should only check the file, since the in-memory setting won't disappear until after reboot. --- salt/states/selinux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/states/selinux.py b/salt/states/selinux.py index e64a1f5af1d4..7b1823a01336 100644 --- a/salt/states/selinux.py +++ b/salt/states/selinux.py @@ -116,7 +116,7 @@ def mode(name): return ret oldmode, mode = mode, __salt__['selinux.setenforce'](tmode) - if mode == tmode: + if mode == tmode or (tmode == 'Disabled' and __salt__['selinux.getconfig']() == tmode): ret['result'] = True ret['comment'] = 'SELinux has been set to {0} mode'.format(tmode) ret['changes'] = {'old': oldmode, From eaf5de9dce1d5bf3acac5c97b444df785c081c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 27 Jan 2017 17:07:25 +0000 Subject: [PATCH 17/59] Snapper: Adding support for deleting snapshots --- salt/modules/snapper.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index 318ce9b99da5..4f93b0b7b649 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -364,6 +364,42 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None, return new_nr +def delete_snapshot(snapshots_ids=None, config="root"): + ''' + Deletes an snapshot + + config + Configuration name. (Default: root) + + snapshots_ids + List of the snapshots IDs to be deleted. + + CLI example: + + .. code-block:: bash + + salt '*' snapper.delete_snapshot 54 + salt '*' snapper.delete_snapshot config=root 54 + salt '*' snapper.delete_snapshot config=root snapshots_ids=[54,55,56] + ''' + if not snapshots_ids: + raise CommandExecutionError('Error: No snapshot ID has been provided') + try: + current_snapshots_ids = [x['id'] for x in list_snapshots(config)] + if not isinstance(snapshots_ids, list): + snapshots_ids = [snapshots_ids] + if not set(snapshots_ids).issubset(set(current_snapshots_ids)): + raise CommandExecutionError( + "Error: Snapshots '{0}' not found".format(", ".join( + [str(x) for x in set(snapshots_ids).difference( + set(current_snapshots_ids))])) + ) + snapper.DeleteSnapshots(config, snapshots_ids) + return {config: {"ids": snapshots_ids, "status": "deleted"}} + except dbus.DBusException as exc: + raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) + + def _get_num_interval(config, num_pre, num_post): ''' Returns numerical interval based on optionals num_pre, num_post values From 041e54d42a90cd974ce9dd828ac65e4f8b7b935e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 3 Feb 2017 11:40:51 +0000 Subject: [PATCH 18/59] Snapper: Adding support for snapshot metadata modification --- salt/modules/snapper.py | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index 4f93b0b7b649..bc76931eaac1 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -400,6 +400,59 @@ def delete_snapshot(snapshots_ids=None, config="root"): raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) +def modify_snapshot(snapshot_id=None, + description=None, + userdata=None, + cleanup=None, + config="root"): + ''' + Modify attributes of an existing snapshot. + + config + Configuration name. (Default: root) + + snapshot_id + ID of the snapshot to be modified. + + cleanup + Change the cleanup method of the snapshot. (str) + + description + Change the description of the snapshot. (str) + + userdata + Change the userdata dictionary of the snapshot. (dict) + + CLI example: + + .. code-block:: bash + + salt '*' snapper.modify_snapshot 54 description="my snapshot description" + salt '*' snapper.modify_snapshot 54 description="my snapshot description" + salt '*' snapper.modify_snapshot 54 userdata='{"foo": "bar"}' + salt '*' snapper.modify_snapshot snapshot_id=54 cleanup="number" + ''' + if not snapshot_id: + raise CommandExecutionError('Error: No snapshot ID has been provided') + + snapshot = get_snapshot(config=config, number=snapshot_id) + try: + # Updating only the explicitely provided attributes by the user + updated_opts = { + 'description': description if description != None else snapshot['description'], + 'cleanup': cleanup if cleanup != None else snapshot['cleanup'], + 'userdata': userdata if userdata != None else snapshot['userdata'], + } + snapper.SetSnapshot(config, + snapshot_id, + updated_opts['description'], + updated_opts['cleanup'], + updated_opts['userdata']) + return get_snapshot(config=config, number=snapshot_id) + except dbus.DBusException as exc: + raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) + + def _get_num_interval(config, num_pre, num_post): ''' Returns numerical interval based on optionals num_pre, num_post values From 3eecb6076bdf6e96ae1b1622fd5206208598e194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 3 Feb 2017 14:09:37 +0000 Subject: [PATCH 19/59] Snapper: Adding support for creating configurations --- salt/modules/snapper.py | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index bc76931eaac1..27d2cc67f562 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -290,6 +290,60 @@ def get_config(name='root'): ) +def create_config(name=None, + subvolume=None, + fstype=None, + template=None, + extra_opts=None): + ''' + Creates a new Snapper configuration + + name + Name of the new Snapper configuration. + subvolume + Path to the related subvolume. + fstype + Filesystem type of the subvolume. + template + Configuration template to use. (Default: default) + extra_opts + Extra Snapper configuration opts dictionary. It will override the values provided + by the given template (if any). + + CLI example: + + .. code-block:: bash + + salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs + salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs template="default" + salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs extra_opts='{"NUMBER_CLEANUP": False}' + ''' + def raise_arg_error(argname): + raise CommandExecutionError( + 'You must provide a "{0}" for the new configuration'.format(argname) + ) + + if not name: + raise_arg_error("name") + if not subvolume: + raise_arg_error("subvolume") + if not fstype: + raise_arg_error("fstype") + if not template: + template = "" + + try: + snapper.CreateConfig(name, subvolume, fstype, template) + if extra_opts: + set_config(name, **extra_opts) + return get_config(name) + except dbus.DBusException as exc: + raise CommandExecutionError( + 'Error encountered while creating the new configuration: {0}' + .format(_dbus_exception_to_reason(exc, locals())) + ) + + def create_snapshot(config='root', snapshot_type='single', pre_number=None, description=None, cleanup_algorithm='number', userdata=None, **kwargs): From d5496ccc990efac1921d7cdb752571af82fed41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 3 Feb 2017 09:50:02 +0000 Subject: [PATCH 20/59] Adds 'snapper.delete_snapshots' unit tests --- tests/unit/modules/snapper_test.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit/modules/snapper_test.py b/tests/unit/modules/snapper_test.py index ca985cfd055d..2007f22ba90a 100644 --- a/tests/unit/modules/snapper_test.py +++ b/tests/unit/modules/snapper_test.py @@ -216,6 +216,19 @@ def test_create_snapshot(self): } self.assertEqual(snapper.create_snapshot(**opts), 1234) + @patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()) + @patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])) + def test_delete_snapshot_id_success(self): + self.assertEqual(snapper.delete_snapshot(snapshots_ids=43), {"root": {"ids": [43], "status": "deleted"}}) + self.assertEqual(snapper.delete_snapshot(snapshots_ids=[42, 43]), {"root": {"ids": [42, 43], "status": "deleted"}}) + + @patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()) + @patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])) + def test_delete_snapshot_id_fail(self): + self.assertRaises(CommandExecutionError, snapper.delete_snapshot) + self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1) + self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2]) + @patch('salt.modules.snapper._get_last_snapshot', MagicMock(return_value={'id': 42})) def test__get_num_interval(self): self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=None), (42, 0)) # pylint: disable=protected-access From d38ed505f87d6b880f48ba8351eb5d2325bdb12d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 3 Feb 2017 12:44:36 +0000 Subject: [PATCH 21/59] Adds 'snapper.modify_snapshots' unit tests --- tests/unit/modules/snapper_test.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/modules/snapper_test.py b/tests/unit/modules/snapper_test.py index 2007f22ba90a..e11f7f19240d 100644 --- a/tests/unit/modules/snapper_test.py +++ b/tests/unit/modules/snapper_test.py @@ -229,6 +229,23 @@ def test_delete_snapshot_id_fail(self): self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1) self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2]) + @patch('salt.modules.snapper.snapper.SetSnapshot', MagicMock()) + def test_modify_snapshot(self): + _ret = { + 'userdata': {'userdata2': 'uservalue2'}, + 'description': 'UPDATED DESCRIPTION', 'timestamp': 1457006571, + 'cleanup': 'number', 'user': 'root', 'type': 'pre', 'id': 42 + } + _opts = { + 'config': 'root', + 'snapshot_id': 42, + 'cleanup': 'number', + 'description': 'UPDATED DESCRIPTION', + 'userdata': {'userdata2': 'uservalue2'}, + } + with patch('salt.modules.snapper.get_snapshot', MagicMock(side_effect=[DBUS_RET['ListSnapshots'][0], _ret])): + self.assertDictEqual(snapper.modify_snapshot(**_opts), _ret) + @patch('salt.modules.snapper._get_last_snapshot', MagicMock(return_value={'id': 42})) def test__get_num_interval(self): self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=None), (42, 0)) # pylint: disable=protected-access From 9d6a33f25738e089b3677a0a9933995bfa31048f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 3 Feb 2017 15:32:39 +0000 Subject: [PATCH 22/59] Adds 'snapper.create_config' unit tests --- tests/unit/modules/snapper_test.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/unit/modules/snapper_test.py b/tests/unit/modules/snapper_test.py index e11f7f19240d..a5d9b7686e8a 100644 --- a/tests/unit/modules/snapper_test.py +++ b/tests/unit/modules/snapper_test.py @@ -202,6 +202,26 @@ def test_status_to_string(self): self.assertEqual(snapper.status_to_string(128), ["extended attributes changed"]) self.assertEqual(snapper.status_to_string(256), ["ACL info changed"]) + @patch('salt.modules.snapper.snapper.CreateConfig', MagicMock()) + @patch('salt.modules.snapper.snapper.GetConfig', MagicMock(return_value=DBUS_RET['ListConfigs'][0])) + def test_create_config(self): + opts = { + 'name': 'testconfig', + 'subvolume': '/foo/bar/', + 'fstype': 'btrfs', + 'template': 'mytemplate', + 'extra_opts': {"NUMBER_CLEANUP": False}, + } + with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: + self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) + set_config_mock.assert_called_with("testconfig", **opts['extra_opts']) + + with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: + del opts['extra_opts'] + self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) + assert not set_config_mock.called + self.assertRaises(CommandExecutionError, snapper.create_config) + @patch('salt.modules.snapper.snapper.CreateSingleSnapshot', MagicMock(return_value=1234)) @patch('salt.modules.snapper.snapper.CreatePreSnapshot', MagicMock(return_value=1234)) @patch('salt.modules.snapper.snapper.CreatePostSnapshot', MagicMock(return_value=1234)) From aa2f9906e04158ed7bfc5f12191f6a1a049838a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 3 Feb 2017 14:10:19 +0000 Subject: [PATCH 23/59] Removing extra spaces --- salt/modules/snapper.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index 27d2cc67f562..2191ddf4090e 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -363,14 +363,14 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None, cleanup_algorithm Set the cleanup algorithm for the snapshot. - number - Deletes old snapshots when a certain number of snapshots - is reached. - timeline - Deletes old snapshots but keeps a number of hourly, - daily, weekly, monthly and yearly snapshots. - empty-pre-post - Deletes pre/post snapshot pairs with empty diffs. + number + Deletes old snapshots when a certain number of snapshots + is reached. + timeline + Deletes old snapshots but keeps a number of hourly, + daily, weekly, monthly and yearly snapshots. + empty-pre-post + Deletes pre/post snapshot pairs with empty diffs. userdata Set userdata for the snapshot (key-value pairs). @@ -491,7 +491,7 @@ def modify_snapshot(snapshot_id=None, snapshot = get_snapshot(config=config, number=snapshot_id) try: - # Updating only the explicitely provided attributes by the user + # Updating only the explicitely provided attributes by the user updated_opts = { 'description': description if description != None else snapshot['description'], 'cleanup': cleanup if cleanup != None else snapshot['cleanup'], From f950732fa03d145d1098c898f94d9a2a59748ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 3 Feb 2017 16:28:42 +0000 Subject: [PATCH 24/59] pylint fixes --- salt/modules/snapper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index 2191ddf4090e..d5f1181743fc 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -493,9 +493,9 @@ def modify_snapshot(snapshot_id=None, try: # Updating only the explicitely provided attributes by the user updated_opts = { - 'description': description if description != None else snapshot['description'], - 'cleanup': cleanup if cleanup != None else snapshot['cleanup'], - 'userdata': userdata if userdata != None else snapshot['userdata'], + 'description': description if description is not None else snapshot['description'], + 'cleanup': cleanup if cleanup is not None else snapshot['cleanup'], + 'userdata': userdata if userdata is not None else snapshot['userdata'], } snapper.SetSnapshot(config, snapshot_id, From 4ebf7a3df40de133101be75d7c2e2be0138bd34b Mon Sep 17 00:00:00 2001 From: Mike Place Date: Fri, 3 Feb 2017 09:53:08 -0700 Subject: [PATCH 25/59] Remove unnecessary pass statement --- salt/states/x509.py | 1 - 1 file changed, 1 deletion(-) diff --git a/salt/states/x509.py b/salt/states/x509.py index 71a8b9ee2d40..ff19f509c063 100644 --- a/salt/states/x509.py +++ b/salt/states/x509.py @@ -239,7 +239,6 @@ def _check_private_key(name, bits=2048, passphrase=None, if not overwrite: raise salt.exceptions.CommandExecutionError( 'The provided passphrase cannot decrypt the private key.') - pass return current_bits == bits and not new From cf548ac717bac511ff1cba6dd8d57fce375666dd Mon Sep 17 00:00:00 2001 From: Mike Place Date: Fri, 3 Feb 2017 09:54:02 -0700 Subject: [PATCH 26/59] Remove unnecessary pass --- salt/states/x509.py | 1 - 1 file changed, 1 deletion(-) diff --git a/salt/states/x509.py b/salt/states/x509.py index ff19f509c063..bcd08972b7dd 100644 --- a/salt/states/x509.py +++ b/salt/states/x509.py @@ -348,7 +348,6 @@ def csr_managed(name, old = __salt__['x509.read_csr'](name) except salt.exceptions.SaltInvocationError: old = '{0} is not a valid csr.'.format(name) - pass file_args, kwargs = _get_file_args(name, **kwargs) file_args['contents'] = __salt__['x509.create_csr'](text=True, **kwargs) From 90af6963313e629281496c0f9c9a4fd9639f10ce Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Fri, 3 Feb 2017 10:23:12 -0700 Subject: [PATCH 27/59] fix boto ec2 module create_image doc --- salt/modules/boto_ec2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/modules/boto_ec2.py b/salt/modules/boto_ec2.py index 8c362c56bf1a..ad032d8fa628 100644 --- a/salt/modules/boto_ec2.py +++ b/salt/modules/boto_ec2.py @@ -495,8 +495,8 @@ def create_image(ami_name, instance_id=None, instance_name=None, tags=None, regi .. code-block:: bash - salt myminion boto_ec2.create_instance ami_name instance_name=myinstance - salt myminion boto_ec2.create_instance another_ami_name tags='{"mytag": "value"}' description='this is my ami' + salt myminion boto_ec2.create_image ami_name instance_name=myinstance + salt myminion boto_ec2.create_image another_ami_name tags='{"mytag": "value"}' description='this is my ami' ''' From 671282656aae3a5245c6a46d98febd9e6472a974 Mon Sep 17 00:00:00 2001 From: Joseph Hall Date: Fri, 3 Feb 2017 14:21:59 -0700 Subject: [PATCH 28/59] Raise an error for a disk size that is too small --- salt/cloud/clouds/vmware.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index 97fe1bc9aea2..755f2d0c876f 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -644,9 +644,17 @@ def _manage_devices(devices, vm=None, container_ref=None): ' Using existing disk size.') size_kb = device.capacityInKB + log.debug('device capacity is {0}'.format(device.capacityInKB)) + log.debug('specified capacity is {0}'.format(size_kb)) if device.capacityInKB < size_kb: # expand the disk disk_spec = _edit_existing_hard_disk_helper(device, size_kb) + elif device.capacityInKB < size_kb: + raise SaltCloudSystemExit( + 'The specified disk size is smaller than the ' + 'size of the disk image. It must be equal to ' + 'or greater than the disk image' + ) if 'mode' in devices['disk'][device.deviceInfo.label]: if devices['disk'][device.deviceInfo.label]['mode'] \ From 2ed2932387a7fa04d23349ac452ec2cc766edfcd Mon Sep 17 00:00:00 2001 From: Joseph Hall Date: Fri, 3 Feb 2017 14:24:51 -0700 Subject: [PATCH 29/59] Clean up debug logs --- salt/cloud/clouds/vmware.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index 755f2d0c876f..777238130667 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -644,8 +644,6 @@ def _manage_devices(devices, vm=None, container_ref=None): ' Using existing disk size.') size_kb = device.capacityInKB - log.debug('device capacity is {0}'.format(device.capacityInKB)) - log.debug('specified capacity is {0}'.format(size_kb)) if device.capacityInKB < size_kb: # expand the disk disk_spec = _edit_existing_hard_disk_helper(device, size_kb) From ec57a39c00c3d29925d7eb244e684c0de2910563 Mon Sep 17 00:00:00 2001 From: Joseph Hall Date: Fri, 3 Feb 2017 14:27:45 -0700 Subject: [PATCH 30/59] Typo --- salt/cloud/clouds/vmware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index 777238130667..e659be0c78f1 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -647,7 +647,7 @@ def _manage_devices(devices, vm=None, container_ref=None): if device.capacityInKB < size_kb: # expand the disk disk_spec = _edit_existing_hard_disk_helper(device, size_kb) - elif device.capacityInKB < size_kb: + elif device.capacityInKB > size_kb: raise SaltCloudSystemExit( 'The specified disk size is smaller than the ' 'size of the disk image. It must be equal to ' From 5aeddf42a0568f90a885d16d6c4bef5770a8f62e Mon Sep 17 00:00:00 2001 From: rallytime Date: Fri, 3 Feb 2017 16:40:53 -0700 Subject: [PATCH 31/59] Restore "Salt Community" doc section This was moved to the `topics/index.rst` page in PR #10792, but seems to have been accidentally removed in PR #30770. --- doc/topics/index.rst | 95 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/doc/topics/index.rst b/doc/topics/index.rst index 28176b230447..77c735446db1 100644 --- a/doc/topics/index.rst +++ b/doc/topics/index.rst @@ -89,3 +89,98 @@ the Salt project so that we can all benefit together as Salt grows. Please feel free to sprinkle Salt around your systems and let the deliciousness come forth. +Salt Community +============== + +Join the Salt! + +There are many ways to participate in and communicate with the Salt community. + +Salt has an active IRC channel and a mailing list. + +Mailing List +============ + +Join the `salt-users mailing list`_. It is the best place to ask questions +about Salt and see whats going on with Salt development! The Salt mailing list +is hosted by Google Groups. It is open to new members. + +.. _`salt-users mailing list`: https://groups.google.com/forum/#!forum/salt-users + + +IRC +=== + +The ``#salt`` IRC channel is hosted on the popular `Freenode`_ network. You +can use the `Freenode webchat client`_ right from your browser. + +`Logs of the IRC channel activity`_ are being collected courtesy of Moritz Lenz. + +.. _Freenode:: http://freenode.net/irc_servers.shtml +.. _Freenode webchat client:: http://webchat.freenode.net/?channels=salt&uio=Mj10cnVlJjk9dHJ1ZSYxMD10cnVl83 +.. _Logs of the IRC channel activity:: http://irclog.perlgeek.de/salt/ + +If you wish to discuss the development of Salt itself join us in +``#salt-devel``. + + +Follow on Github +================ + +The Salt code is developed via Github. Follow Salt for constant updates on what +is happening in Salt development: + +|saltrepo| + + +Blogs +===== + +SaltStack Inc. keeps a `blog`_ with recent news and advancements: + +http://www.saltstack.com/blog/ + +.. _`blog`: http://www.saltstack.com/blog/ + + +Example Salt States +=================== + +The official ``salt-states`` repository is: +https://github.com/saltstack/salt-states + +A few examples of salt states from the community: + +* https://github.com/blast-hardcheese/blast-salt-states +* https://github.com/kevingranade/kevingranade-salt-state +* https://github.com/uggedal/states +* https://github.com/mattmcclean/salt-openstack/tree/master/salt +* https://github.com/rentalita/ubuntu-setup/ +* https://github.com/brutasse/states +* https://github.com/bclermont/states +* https://github.com/pcrews/salt-data + +Follow on ohloh +=============== + +https://www.ohloh.net/p/salt + +Other community links +===================== + +- `Salt Stack Inc. `_ +- `Subreddit `_ +- `Google+ `_ +- `YouTube `_ +- `Facebook `_ +- `Twitter `_ +- `Wikipedia page `_ + +Hack the Source +=============== + +If you want to get involved with the development of source code or the +documentation efforts, please review the :ref:`contributing documentation +`! + +.. _`Apache 2.0 license`: http://www.apache.org/licenses/LICENSE-2.0.html From 6750ccd78ef08c2a7732bfb9eb3fd50003eedc98 Mon Sep 17 00:00:00 2001 From: Michael Calmer Date: Sat, 4 Feb 2017 16:05:45 +0100 Subject: [PATCH 32/59] fix error parsing --- salt/modules/dockerng.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py index 679f791d2825..29af703ffac6 100644 --- a/salt/modules/dockerng.py +++ b/salt/modules/dockerng.py @@ -1208,7 +1208,7 @@ def _error_detail(data, item): ''' err = item['errorDetail'] if 'code' in err: - msg = '{1}: {2}'.format( + msg = '{0}: {1}'.format( item['errorDetail']['code'], item['errorDetail']['message'], ) From d7e05091a2e0051f9f35c3e8652c517b6a3317b8 Mon Sep 17 00:00:00 2001 From: Morgan Willcock Date: Sat, 4 Feb 2017 19:00:36 +0000 Subject: [PATCH 33/59] Remove global options from Install --- setup.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/setup.py b/setup.py index f9e9437e7152..3d831391e32b 100755 --- a/setup.py +++ b/setup.py @@ -716,31 +716,6 @@ def run(self): class Install(install): - user_options = install.user_options + [ - ('salt-transport=', None, 'The transport to prepare salt for. Choices are \'zeromq\' ' - '\'raet\' or \'both\'. Defaults to \'zeromq\'', 'zeromq'), - ('salt-root-dir=', None, - 'Salt\'s pre-configured root directory'), - ('salt-config-dir=', None, - 'Salt\'s pre-configured configuration directory'), - ('salt-cache-dir=', None, - 'Salt\'s pre-configured cache directory'), - ('salt-sock-dir=', None, - 'Salt\'s pre-configured socket directory'), - ('salt-srv-root-dir=', None, - 'Salt\'s pre-configured service directory'), - ('salt-base-file-roots-dir=', None, - 'Salt\'s pre-configured file roots directory'), - ('salt-base-pillar-roots-dir=', None, - 'Salt\'s pre-configured pillar roots directory'), - ('salt-base-master-roots-dir=', None, - 'Salt\'s pre-configured master roots directory'), - ('salt-logs-dir=', None, - 'Salt\'s pre-configured logs directory'), - ('salt-pidfile-dir=', None, - 'Salt\'s pre-configured pidfiles directory'), - ] - def initialize_options(self): install.initialize_options(self) From 64b693195cefc6d3620d45361532c338e37bb832 Mon Sep 17 00:00:00 2001 From: Morgan Willcock Date: Mon, 6 Feb 2017 11:08:31 +0000 Subject: [PATCH 34/59] Fix NetBSD sockstat parsing Fixes NetBSD sockstat parsing so that presence events are fired correctly. --- salt/utils/network.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/utils/network.py b/salt/utils/network.py index b4e4710b2f37..c89db3bca32e 100644 --- a/salt/utils/network.py +++ b/salt/utils/network.py @@ -1057,14 +1057,14 @@ def _netbsd_remotes_on(port, which_end): Parses output of shell 'sockstat' (NetBSD) to get connections - $ sudo sockstat -4 + $ sudo sockstat -4 -n USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root python2.7 1456 29 tcp4 *.4505 *.* root python2.7 1445 17 tcp4 *.4506 *.* root python2.7 1294 14 tcp4 127.0.0.1.11813 127.0.0.1.4505 root python2.7 1294 41 tcp4 127.0.0.1.61115 127.0.0.1.4506 - $ sudo sockstat -4 -c -p 4506 + $ sudo sockstat -4 -c -n -p 4506 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root python2.7 1294 41 tcp4 127.0.0.1.61115 127.0.0.1.4506 ''' @@ -1073,7 +1073,7 @@ def _netbsd_remotes_on(port, which_end): remotes = set() try: - cmd = salt.utils.shlex_split('sockstat -4 -c -p {0}'.format(port)) + cmd = salt.utils.shlex_split('sockstat -4 -c -n -p {0}'.format(port)) data = subprocess.check_output(cmd) # pylint: disable=minimum-python-version except subprocess.CalledProcessError as ex: log.error('Failed "sockstat" with returncode = {0}'.format(ex.returncode)) From 344d13eff50937c9687ced92286b7395952e2d87 Mon Sep 17 00:00:00 2001 From: Morgan Willcock Date: Mon, 6 Feb 2017 11:38:34 +0000 Subject: [PATCH 35/59] Fix NetBSD sockstat example --- salt/utils/network.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/salt/utils/network.py b/salt/utils/network.py index c89db3bca32e..df47c80f31f3 100644 --- a/salt/utils/network.py +++ b/salt/utils/network.py @@ -1059,14 +1059,14 @@ def _netbsd_remotes_on(port, which_end): $ sudo sockstat -4 -n USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS - root python2.7 1456 29 tcp4 *.4505 *.* - root python2.7 1445 17 tcp4 *.4506 *.* - root python2.7 1294 14 tcp4 127.0.0.1.11813 127.0.0.1.4505 - root python2.7 1294 41 tcp4 127.0.0.1.61115 127.0.0.1.4506 + root python2.7 1456 29 tcp *.4505 *.* + root python2.7 1445 17 tcp *.4506 *.* + root python2.7 1294 14 tcp 127.0.0.1.11813 127.0.0.1.4505 + root python2.7 1294 41 tcp 127.0.0.1.61115 127.0.0.1.4506 $ sudo sockstat -4 -c -n -p 4506 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS - root python2.7 1294 41 tcp4 127.0.0.1.61115 127.0.0.1.4506 + root python2.7 1294 41 tcp 127.0.0.1.61115 127.0.0.1.4506 ''' port = int(port) @@ -1085,7 +1085,7 @@ def _netbsd_remotes_on(port, which_end): chunks = line.split() if not chunks: continue - # ['root', 'python2.7', '1456', '37', 'tcp4', + # ['root', 'python2.7', '1456', '37', 'tcp', # '127.0.0.1.4505-', '127.0.0.1.55703'] # print chunks if 'COMMAND' in chunks[1]: From 8d8ba9c7d246573b9a67a0bdb102d49f39cf83da Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Mon, 6 Feb 2017 09:55:09 -0500 Subject: [PATCH 36/59] added the new getconfig function to the test --- tests/unit/states/selinux_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/states/selinux_test.py b/tests/unit/states/selinux_test.py index 026698ad68ff..6b5982bea647 100644 --- a/tests/unit/states/selinux_test.py +++ b/tests/unit/states/selinux_test.py @@ -47,6 +47,7 @@ def test_mode(self): mock_pr = MagicMock(side_effect=['Permissive', 'Enforcing']) with patch.dict(selinux.__salt__, {'selinux.getenforce': mock_en, + 'selinux.getconfig': mock_en, 'selinux.setenforce': mock_pr}): comt = ('SELinux is already in Enforcing mode') ret = {'name': 'Enforcing', 'comment': comt, 'result': True, 'changes': {}} From f3d35fb5c68478c43f1c7540ec00af35cf0d50e8 Mon Sep 17 00:00:00 2001 From: Mike Place Date: Mon, 6 Feb 2017 12:20:08 -0700 Subject: [PATCH 37/59] Lint fixes --- salt/modules/win_ip.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index e06b3b3c747d..6b90952289bf 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -6,7 +6,6 @@ # Import python libs import logging -import socket import time # Import salt libs @@ -61,9 +60,9 @@ def _interface_configs(): if ':' not in line: if current_ip_list: - current_ip_list.append( line ) + current_ip_list.append(line) else: - log.warning( 'Cannot parse "{0}"'.format(line) ) + log.warning('Cannot parse "{0}"'.format(line)) continue key, val = line.split(':', 1) @@ -74,10 +73,10 @@ def _interface_configs(): if ('dns servers' in lkey) or ('wins servers' in lkey): current_ip_list = [] current_iface[key] = current_ip_list - current_ip_list.append( val ) + current_ip_list.append(val) elif 'ip address' in lkey: - current_iface.setdefault( 'ip_addrs', [] ).append( {key:val} ) + current_iface.setdefault('ip_addrs', []).append({key:val}) elif 'subnet prefix' in lkey: subnet, _, netmask = val.split(' ', 2) From c12990531005338f56213200bbadc7147400b7ee Mon Sep 17 00:00:00 2001 From: Raine Curtis Date: Fri, 3 Feb 2017 13:59:01 -0700 Subject: [PATCH 38/59] Added missing source_hash_name argument in get_managed function Additional fix to #33187 Customer was still seeing errors, this should now work. Tested with 2015.8.13 and 2016.11.2 --- salt/states/jboss7.py | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/states/jboss7.py b/salt/states/jboss7.py index 0272e96e4c0e..7eb6ad91c01b 100644 --- a/salt/states/jboss7.py +++ b/salt/states/jboss7.py @@ -412,6 +412,7 @@ def __get_artifact(salt_source): template=None, source=salt_source['source'], source_hash=None, + source_hash_name=None, user=None, group=None, mode=None, From 7bed68743e315d947c37c32f9e55b5d9337d5ad6 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Mon, 6 Feb 2017 14:42:30 -0700 Subject: [PATCH 39/59] [2016.3] Pylint fix (#39202) --- salt/modules/win_ip.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index 6b90952289bf..dc7416b78eb5 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -76,7 +76,7 @@ def _interface_configs(): current_ip_list.append(val) elif 'ip address' in lkey: - current_iface.setdefault('ip_addrs', []).append({key:val}) + current_iface.setdefault('ip_addrs', []).append({key: val}) elif 'subnet prefix' in lkey: subnet, _, netmask = val.split(' ', 2) @@ -90,7 +90,6 @@ def _interface_configs(): return ret - def raw_interface_configs(): ''' Return raw configs for all interfaces From be31e0559ca970aaab24fdb50c83ac6137ca5a2c Mon Sep 17 00:00:00 2001 From: Mike Place Date: Mon, 6 Feb 2017 16:25:48 -0700 Subject: [PATCH 40/59] Ignore empty dicts in highstate outputter Closes #37174 --- salt/output/highstate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/output/highstate.py b/salt/output/highstate.py index 5e7a897874ce..e743c8af8df5 100644 --- a/salt/output/highstate.py +++ b/salt/output/highstate.py @@ -179,7 +179,7 @@ def _format_host(host, data): # Verify that the needed data is present data_tmp = {} for tname, info in six.iteritems(data): - if isinstance(info, dict) and '__run_num__' not in info: + if isinstance(info, dict) and info and '__run_num__' not in info: err = (u'The State execution failed to record the order ' 'in which all states were executed. The state ' 'return missing data is:') From 2f74dcf685d09785199415026bb298f0c564e784 Mon Sep 17 00:00:00 2001 From: rallytime Date: Fri, 3 Feb 2017 15:32:42 -0700 Subject: [PATCH 41/59] Various doc updates to fix warnings in doc build --- doc/glossary.rst | 3 +++ .../all/salt.cloud.clouds.virtualbox.rst | 4 ++-- doc/ref/configuration/examples.rst | 2 ++ doc/ref/configuration/master.rst | 2 +- doc/ref/modules/all/index.rst | 2 +- doc/ref/modules/all/salt.modules.cytest.rst | 1 - doc/ref/returners/index.rst | 10 +++++----- doc/topics/cloud/misc.rst | 2 +- doc/topics/development/contributing.rst | 1 + doc/topics/development/modular_systems.rst | 2 +- doc/topics/execution/index.rst | 2 +- doc/topics/highavailability/index.rst | 2 +- doc/topics/index.rst | 4 ++++ doc/topics/jinja/index.rst | 2 +- doc/topics/jobs/external_cache.rst | 2 +- doc/topics/jobs/job_cache.rst | 2 +- doc/topics/mine/index.rst | 2 +- doc/topics/reactor/index.rst | 2 +- doc/topics/releases/2014.1.0.rst | 4 ++-- doc/topics/releases/2015.8.11.rst | 6 ++++-- doc/topics/releases/2015.8.4.rst | 6 +++--- doc/topics/releases/2015.8.8.rst | 10 ++++++++++ doc/topics/releases/2015.8.9.rst | 12 ++++++++++-- doc/topics/releases/2016.3.0.rst | 2 +- doc/topics/releases/2016.3.1.rst | 6 ++++++ doc/topics/releases/2016.3.4.rst | 4 ++++ doc/topics/tutorials/gitfs.rst | 17 +++++++++-------- doc/topics/tutorials/http.rst | 3 ++- doc/topics/tutorials/modules.rst | 2 +- doc/topics/tutorials/starting_states.rst | 2 +- doc/topics/tutorials/walkthrough.rst | 4 ++-- doc/topics/tutorials/walkthrough_macosx.rst | 10 +++++----- doc/topics/tutorials/writing_tests.rst | 2 +- doc/topics/yaml/index.rst | 5 ++--- salt/modules/solaris_user.py | 2 ++ salt/renderers/jinja.py | 2 +- salt/returners/postgres.py | 2 +- salt/returners/postgres_local_cache.py | 2 +- salt/runners/salt.py | 6 +++--- salt/states/boto_apigateway.py | 18 +++++++++--------- salt/states/cmd.py | 2 +- 41 files changed, 109 insertions(+), 67 deletions(-) diff --git a/doc/glossary.rst b/doc/glossary.rst index ed6f0d6fc853..72bfe8eed4a9 100644 --- a/doc/glossary.rst +++ b/doc/glossary.rst @@ -1,3 +1,6 @@ +.. _glossary: + +======== Glossary ======== diff --git a/doc/ref/clouds/all/salt.cloud.clouds.virtualbox.rst b/doc/ref/clouds/all/salt.cloud.clouds.virtualbox.rst index 410b823f3367..3f687e36809c 100644 --- a/doc/ref/clouds/all/salt.cloud.clouds.virtualbox.rst +++ b/doc/ref/clouds/all/salt.cloud.clouds.virtualbox.rst @@ -1,6 +1,6 @@ -======================== +============================ salt.cloud.clouds.virtualbox -======================== +============================ .. automodule:: salt.cloud.clouds.virtualbox :members: diff --git a/doc/ref/configuration/examples.rst b/doc/ref/configuration/examples.rst index 0493cc1100a2..373ca9c013d6 100644 --- a/doc/ref/configuration/examples.rst +++ b/doc/ref/configuration/examples.rst @@ -1,3 +1,5 @@ +.. _configuration-file-examples: + =========================== Configuration file examples =========================== diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index 55c4e3b81281..3464ecc2ae16 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -735,7 +735,7 @@ Pass in an alternative location for the salt-ssh roster file. .. conf_master:: ssh_log_file ``ssh_log_file`` -------------------- +---------------- .. versionadded:: 2016.3.5 diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index 06694312d4b8..09a22e52b902 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -1,4 +1,4 @@ -.. _all-salt_modules: +.. _all-salt.modules: ================= execution modules diff --git a/doc/ref/modules/all/salt.modules.cytest.rst b/doc/ref/modules/all/salt.modules.cytest.rst index cf30b20cf409..cf808dafb67d 100644 --- a/doc/ref/modules/all/salt.modules.cytest.rst +++ b/doc/ref/modules/all/salt.modules.cytest.rst @@ -3,4 +3,3 @@ salt.modules.cytest module .. automodule:: salt.modules.cytest :members: - diff --git a/doc/ref/returners/index.rst b/doc/ref/returners/index.rst index 628a8ebbcbbb..138d681576cd 100644 --- a/doc/ref/returners/index.rst +++ b/doc/ref/returners/index.rst @@ -47,7 +47,7 @@ Writing a Returner A returner is a Python module containing at minimum a ``returner`` function. Other optional functions can be included to add support for -:ref:`master_job_cache`, :ref:`external_job_cache`, and `Event Returners`_. +:conf_master:`master_job_cache`, :ref:`external-job-cache`, and `Event Returners`_. ``returner`` The ``returner`` function must accept a single argument. The argument @@ -85,8 +85,8 @@ serializes the data as JSON and sets it in redis. Master Job Cache Support ------------------------ -:ref:`master_job_cache`, :ref:`external_job_cache`, and `Event Returners`_. -Salt's :ref:`master_job_cache` allows returners to be used as a pluggable +:conf_master:`master_job_cache`, :ref:`external-job-cache`, and `Event Returners`_. +Salt's :conf_master:`master_job_cache` allows returners to be used as a pluggable replacement for the :ref:`default_job_cache`. In order to do so, a returner must implement the following functions: @@ -176,7 +176,7 @@ must implement the following functions: External Job Cache Support -------------------------- -Salt's :ref:`external_job_cache` extends the :ref:`master_job_cache`. External +Salt's :ref:`external-job-cache` extends the :conf_master:`master_job_cache`. External Job Cache support requires the following functions in addition to what is required for Master Job Cache support: @@ -328,7 +328,7 @@ Testing the Returner The ``returner``, ``prep_jid``, ``save_load``, ``get_load``, and ``event_return`` functions can be tested by configuring the -:ref:`master_job_cache` and `Event Returners`_ in the master config +:conf_master:`master_job_cache` and `Event Returners`_ in the master config file and submitting a job to ``test.ping`` each minion from the master. Once you have successfully exercised the Master Job Cache functions, test the diff --git a/doc/topics/cloud/misc.rst b/doc/topics/cloud/misc.rst index 952abc10f42e..3ef1518923c9 100644 --- a/doc/topics/cloud/misc.rst +++ b/doc/topics/cloud/misc.rst @@ -94,7 +94,7 @@ profile or map: Setting Up a Salt Syndic with Salt Cloud ======================================== -In addition to `setting up new Salt Masters`_, :ref:`syndic`s can also be +In addition to `setting up new Salt Masters`_, :ref:`syndics ` can also be provisioned using Salt Cloud. In order to set up a Salt Syndic via Salt Cloud, a Salt Master needs to be installed on the new machine and a master configuration file needs to be set up using the ``make_master`` setting. This setting can be diff --git a/doc/topics/development/contributing.rst b/doc/topics/development/contributing.rst index 3c879c991bea..2c0afa048b5e 100644 --- a/doc/topics/development/contributing.rst +++ b/doc/topics/development/contributing.rst @@ -418,3 +418,4 @@ and bug resolution. See the :ref:`Labels and Milestones .. _'Git resources`: https://help.github.com/articles/good-resources-for-learning-git-and-github/ .. _`Closing issues via commit message`: https://help.github.com/articles/closing-issues-via-commit-messages .. _`git format-patch`: https://www.kernel.org/pub/software/scm/git/docs/git-format-patch.html +.. _salt-users: https://groups.google.com/forum/#!forum/salt-users diff --git a/doc/topics/development/modular_systems.rst b/doc/topics/development/modular_systems.rst index b2943353568f..6060ceea88cc 100644 --- a/doc/topics/development/modular_systems.rst +++ b/doc/topics/development/modular_systems.rst @@ -25,7 +25,7 @@ modules is that the defined functions always return a JSON serializable object. For a list of all built in execution modules, click :ref:`here -` +` For information on writing execution modules, see :ref:`this page `. diff --git a/doc/topics/execution/index.rst b/doc/topics/execution/index.rst index c3e439044dac..0876542e5f4a 100644 --- a/doc/topics/execution/index.rst +++ b/doc/topics/execution/index.rst @@ -15,7 +15,7 @@ a wide variety of tasks. These modules provide functionality such as installing packages, restarting a service, running a remote command, transferring files, and so on. - :ref:`Full list of execution modules ` + :ref:`Full list of execution modules ` Contains: a list of core modules that ship with Salt. :ref:`Writing execution modules ` diff --git a/doc/topics/highavailability/index.rst b/doc/topics/highavailability/index.rst index 088e42b6db1d..6f9d5f07afab 100644 --- a/doc/topics/highavailability/index.rst +++ b/doc/topics/highavailability/index.rst @@ -6,7 +6,7 @@ High Availability Features in Salt Salt supports several features for high availability and fault tolerance. Brief documentation for these features is listed alongside their configuration -parameters in :ref:`Configuration file examples `. +parameters in :ref:`Configuration file examples `. Multimaster =========== diff --git a/doc/topics/index.rst b/doc/topics/index.rst index 77c735446db1..a4d9f6805298 100644 --- a/doc/topics/index.rst +++ b/doc/topics/index.rst @@ -6,6 +6,7 @@ Introduction to Salt The 30 second summary ===================== + Salt is: * a configuration management system, capable of maintaining remote nodes @@ -89,6 +90,8 @@ the Salt project so that we can all benefit together as Salt grows. Please feel free to sprinkle Salt around your systems and let the deliciousness come forth. +.. _salt-community: + Salt Community ============== @@ -184,3 +187,4 @@ documentation efforts, please review the :ref:`contributing documentation `! .. _`Apache 2.0 license`: http://www.apache.org/licenses/LICENSE-2.0.html + diff --git a/doc/topics/jinja/index.rst b/doc/topics/jinja/index.rst index c5c15fbdb573..2a896769a3fa 100644 --- a/doc/topics/jinja/index.rst +++ b/doc/topics/jinja/index.rst @@ -1,4 +1,4 @@ -.. _jinja: +.. _understanding-jinja: =================== Understanding Jinja diff --git a/doc/topics/jobs/external_cache.rst b/doc/topics/jobs/external_cache.rst index ff5c6fe7509e..f34f0d1a43a6 100644 --- a/doc/topics/jobs/external_cache.rst +++ b/doc/topics/jobs/external_cache.rst @@ -1,4 +1,4 @@ -.. _external-master-cache: +.. _external-job-cache: ========================================= Storing Job Results in an External System diff --git a/doc/topics/jobs/job_cache.rst b/doc/topics/jobs/job_cache.rst index 734feecc9dc2..6b08eb850340 100644 --- a/doc/topics/jobs/job_cache.rst +++ b/doc/topics/jobs/job_cache.rst @@ -67,6 +67,6 @@ Many deployments may wish to use an external database to maintain a long term register of executed jobs. Salt comes with two main mechanisms to do this, the master job cache and the external job cache. -See :ref:`Storing Job Results in an External System `. +See :ref:`Storing Job Results in an External System `. diff --git a/doc/topics/mine/index.rst b/doc/topics/mine/index.rst index 7186d1f7667a..9e836f8e5cc7 100644 --- a/doc/topics/mine/index.rst +++ b/doc/topics/mine/index.rst @@ -22,7 +22,7 @@ refreshed on a very limited basis and are largely static data. Mines are designed to replace slow peer publishing calls when Minions need data from other Minions. Rather than having a Minion reach out to all the other Minions for a piece of data, the Salt Mine, running on the Master, can collect it from -all the Minions every :ref:`mine-interval`, resulting in +all the Minions every :ref:`mine_interval`, resulting in almost fresh data at any given time, with much less overhead. Mine Functions diff --git a/doc/topics/reactor/index.rst b/doc/topics/reactor/index.rst index b61093d42d72..c3b7ddd36150 100644 --- a/doc/topics/reactor/index.rst +++ b/doc/topics/reactor/index.rst @@ -395,7 +395,7 @@ sets up and listens to the minions event bus, instead of to the masters. The biggest difference is that you have to use the caller method on the Reactor, which is the equivalent of salt-call, to run your commands. -:ref:`Reactor Engine setup` +:mod:`Reactor Engine setup ` .. code-block:: yaml diff --git a/doc/topics/releases/2014.1.0.rst b/doc/topics/releases/2014.1.0.rst index 5043c7afd151..a8593bc38e5a 100644 --- a/doc/topics/releases/2014.1.0.rst +++ b/doc/topics/releases/2014.1.0.rst @@ -9,7 +9,7 @@ Salt 2014.1.0 Release Notes - Codename Hydrogen Due to a change in master to minion communication, 2014.1.0 minions are not compatible with older-version masters. Please upgrade masters first. More info on backwards-compatibility policy :ref:`here - `, under the "Upgrading Salt" subheading. .. note:: @@ -248,7 +248,7 @@ Proxy Minions ------------- Initial basic support for Proxy Minions is in this release. Documentation can -be found :ref:`here `. +be found :ref:`here `. Proxy minions are a developing feature in Salt that enables control of devices that cannot run a minion. Examples include network gear like switches and diff --git a/doc/topics/releases/2015.8.11.rst b/doc/topics/releases/2015.8.11.rst index 5d01f7cd05f9..8cec2335e403 100644 --- a/doc/topics/releases/2015.8.11.rst +++ b/doc/topics/releases/2015.8.11.rst @@ -17,7 +17,7 @@ New Configuration Parameter: ``rotate_aes_key`` - ``Rotate_aes_key`` causes Salt to generate a new AES key whenever a minion key is deleted. This eliminates the chance that a deleted minion could continue to eavesdrop on communications with the master if it continues to run after its - key is deleted. See the entry in the documentation for `rotate_aes_key`_. + key is deleted. See the entry in the documentation for :conf_master:`rotate_aes_key`. Ubuntu 16.04 Packages ===================== @@ -404,11 +404,13 @@ Changes: .. _`#20226`: https://github.com/saltstack/salt/pull/20226 .. _`#22480`: https://github.com/saltstack/salt/pull/22480 +.. _`#23643`: https://github.com/saltstack/salt/issues/23643 .. _`#25089`: https://github.com/saltstack/salt/pull/25089 .. _`#28325`: https://github.com/saltstack/salt/pull/28325 .. _`#28467`: https://github.com/saltstack/salt/pull/28467 .. _`#32484`: https://github.com/saltstack/salt/pull/32484 .. _`#32857`: https://github.com/saltstack/salt/pull/32857 +.. _`#33266`: https://github.com/saltstack/salt/issues/33266 .. _`#33282`: https://github.com/saltstack/salt/pull/33282 .. _`#33286`: https://github.com/saltstack/salt/pull/33286 .. _`#33287`: https://github.com/saltstack/salt/pull/33287 @@ -491,6 +493,7 @@ Changes: .. _`#33555`: https://github.com/saltstack/salt/pull/33555 .. _`#33558`: https://github.com/saltstack/salt/pull/33558 .. _`#33581`: https://github.com/saltstack/salt/pull/33581 +.. _`#33582`: https://github.com/saltstack/salt/issues/33582 .. _`#33599`: https://github.com/saltstack/salt/pull/33599 .. _`#33611`: https://github.com/saltstack/salt/pull/33611 .. _`#33615`: https://github.com/saltstack/salt/pull/33615 @@ -647,4 +650,3 @@ Changes: .. _`#34647`: https://github.com/saltstack/salt/pull/34647 .. _`#34651`: https://github.com/saltstack/salt/pull/34651 .. _`#34676`: https://github.com/saltstack/salt/pull/34676 -.. _ `rotate_aes_key`: https://docs.saltstack.com/en/2015.8/ref/configuration/master.html#rotate-aes-key diff --git a/doc/topics/releases/2015.8.4.rst b/doc/topics/releases/2015.8.4.rst index e6e605517071..56d84195c058 100644 --- a/doc/topics/releases/2015.8.4.rst +++ b/doc/topics/releases/2015.8.4.rst @@ -48,8 +48,6 @@ Core Changes salt '*' state.highstate mock=True salt '*' state.apply edit.vim mock=True -.. _`#28994`: https://github.com/saltstack/salt/pull/28994 - Changes for v2015.8.3..v2015.8.4 -------------------------------- @@ -515,7 +513,7 @@ Changes: - **PR** `#29317`_: (*basepi*) [2015.8] Merge forward from 2015.5 to 2015.8 -- **PR** `#29240`_: (*clan*) handle acl_type [[d]efault:][user|group|mask|other] +- **PR** `#29240`_: (*clan*) handle acl_type ``[[d]efault:][user|group|mask|other]`` - **PR** `#29305`_: (*lorengordon*) Add 'file' as a source_hash proto @@ -578,11 +576,13 @@ Changes: - **PR** `#29178`_: (*whytewolf*) Profile not being passed to keystone.endpoint_get in _auth. so if a p… .. _`#19332`: https://github.com/saltstack/salt/issues/19332 +.. _`#24237`: https://github.com/saltstack/salt/issues/24237 .. _`#29116`: https://github.com/saltstack/salt/issues/29116 .. _`#29187`: https://github.com/saltstack/salt/issues/29187 .. _`#23825`: https://github.com/saltstack/salt/pull/23825 .. _`#26511`: https://github.com/saltstack/salt/pull/26511 .. _`#26853`: https://github.com/saltstack/salt/pull/26853 +.. _`#26845`: https://github.com/saltstack/salt/issues/26845 .. _`#26962`: https://github.com/saltstack/salt/pull/26962 .. _`#27104`: https://github.com/saltstack/salt/pull/27104 .. _`#27606`: https://github.com/saltstack/salt/pull/27606 diff --git a/doc/topics/releases/2015.8.8.rst b/doc/topics/releases/2015.8.8.rst index fbe8d253f93d..fd5991cb4ee8 100644 --- a/doc/topics/releases/2015.8.8.rst +++ b/doc/topics/releases/2015.8.8.rst @@ -599,13 +599,16 @@ Changes: - **PR** `#30625`_: (*jfindlay*) doc.topics.eauth: clarify client_acl vs eauth +.. _`#27796`: https://github.com/saltstack/salt/issues/27796 .. _`#29650`: https://github.com/saltstack/salt/pull/29650 .. _`#29718`: https://github.com/saltstack/salt/pull/29718 .. _`#30062`: https://github.com/saltstack/salt/pull/30062 .. _`#30217`: https://github.com/saltstack/salt/pull/30217 .. _`#30279`: https://github.com/saltstack/salt/pull/30279 +.. _`#30300`: https://github.com/saltstack/salt/issues/30300 .. _`#30378`: https://github.com/saltstack/salt/pull/30378 .. _`#30458`: https://github.com/saltstack/salt/pull/30458 +.. _`#30461`: https://github.com/saltstack/salt/issues/30461 .. _`#30542`: https://github.com/saltstack/salt/pull/30542 .. _`#30586`: https://github.com/saltstack/salt/pull/30586 .. _`#30591`: https://github.com/saltstack/salt/pull/30591 @@ -664,6 +667,7 @@ Changes: .. _`#30813`: https://github.com/saltstack/salt/pull/30813 .. _`#30815`: https://github.com/saltstack/salt/pull/30815 .. _`#30818`: https://github.com/saltstack/salt/pull/30818 +.. _`#30820`: https://github.com/saltstack/salt/issues/30820 .. _`#30822`: https://github.com/saltstack/salt/pull/30822 .. _`#30823`: https://github.com/saltstack/salt/pull/30823 .. _`#30826`: https://github.com/saltstack/salt/pull/30826 @@ -700,6 +704,7 @@ Changes: .. _`#30920`: https://github.com/saltstack/salt/pull/30920 .. _`#30922`: https://github.com/saltstack/salt/pull/30922 .. _`#30924`: https://github.com/saltstack/salt/pull/30924 +.. _`#30934`: https://github.com/saltstack/salt/issues/30934 .. _`#30940`: https://github.com/saltstack/salt/pull/30940 .. _`#30941`: https://github.com/saltstack/salt/pull/30941 .. _`#30942`: https://github.com/saltstack/salt/pull/30942 @@ -713,10 +718,12 @@ Changes: .. _`#30978`: https://github.com/saltstack/salt/pull/30978 .. _`#30987`: https://github.com/saltstack/salt/pull/30987 .. _`#30998`: https://github.com/saltstack/salt/pull/30998 +.. _`#30999`: https://github.com/saltstack/salt/issues/30999 .. _`#31002`: https://github.com/saltstack/salt/pull/31002 .. _`#31004`: https://github.com/saltstack/salt/pull/31004 .. _`#31007`: https://github.com/saltstack/salt/pull/31007 .. _`#31012`: https://github.com/saltstack/salt/pull/31012 +.. _`#31014`: https://github.com/saltstack/salt/issues/31014 .. _`#31015`: https://github.com/saltstack/salt/pull/31015 .. _`#31024`: https://github.com/saltstack/salt/pull/31024 .. _`#31026`: https://github.com/saltstack/salt/pull/31026 @@ -737,6 +744,7 @@ Changes: .. _`#31100`: https://github.com/saltstack/salt/pull/31100 .. _`#31103`: https://github.com/saltstack/salt/pull/31103 .. _`#31105`: https://github.com/saltstack/salt/pull/31105 +.. _`#31106`: https://github.com/saltstack/salt/issues/31106 .. _`#31107`: https://github.com/saltstack/salt/pull/31107 .. _`#31108`: https://github.com/saltstack/salt/pull/31108 .. _`#31110`: https://github.com/saltstack/salt/pull/31110 @@ -769,6 +777,7 @@ Changes: .. _`#31191`: https://github.com/saltstack/salt/pull/31191 .. _`#31196`: https://github.com/saltstack/salt/pull/31196 .. _`#31201`: https://github.com/saltstack/salt/pull/31201 +.. _`#31223`: https://github.com/saltstack/salt/issues/31223 .. _`#31225`: https://github.com/saltstack/salt/pull/31225 .. _`#31226`: https://github.com/saltstack/salt/pull/31226 .. _`#31233`: https://github.com/saltstack/salt/pull/31233 @@ -868,6 +877,7 @@ Changes: .. _`#31598`: https://github.com/saltstack/salt/pull/31598 .. _`#31601`: https://github.com/saltstack/salt/pull/31601 .. _`#31604`: https://github.com/saltstack/salt/pull/31604 +.. _`#31617`: https://github.com/saltstack/salt/issues/31617 .. _`#31622`: https://github.com/saltstack/salt/pull/31622 .. _`#31627`: https://github.com/saltstack/salt/pull/31627 .. _`#31629`: https://github.com/saltstack/salt/pull/31629 diff --git a/doc/topics/releases/2015.8.9.rst b/doc/topics/releases/2015.8.9.rst index 3ce305c7117f..8053ed83fb41 100644 --- a/doc/topics/releases/2015.8.9.rst +++ b/doc/topics/releases/2015.8.9.rst @@ -297,7 +297,7 @@ Changes: - **PR** `#32425`_: (*cachedout*) Fix salt-cloud parallel provisioning -* 51fb2ac FreeBSD supports packages in format java/openjdk7 so the prior commit broke that functionality. Check freebsd/pkg`#1409`_ for more info. +* 51fb2ac FreeBSD supports packages in format java/openjdk7 so the prior commit broke that functionality. Check freebsd/pkg #1409 for more info. * 709410a Improve git_pillar documentation/logging @@ -325,7 +325,7 @@ Changes: - **PR** `#32321`_: (*abednarik*) Better message when minion fail to start -- **PR** `#32345`_: (*nmadhok*) [2015.8] Check if profile key exists in vm_ dict +- **PR** `#32345`_: (*nmadhok*) [2015.8] Check if profile key exists in ``vm_`` dict - **PR** `#32343`_: (*Ferbla*) Fixed win_wua example documentation @@ -467,15 +467,21 @@ Changes: - **PR** `#31912`_: (*jfindlay*) log.mixins: remove extermporaneous .record +.. _`#14277`: https://github.com/saltstack/salt/issues/14277 +.. _`#23714`: https://github.com/saltstack/salt/issues/23714 +.. _`#24237`: https://github.com/saltstack/salt/issues/24237 .. _`#26518`: https://github.com/saltstack/salt/pull/26518 .. _`#26648`: https://github.com/saltstack/salt/pull/26648 .. _`#26676`: https://github.com/saltstack/salt/pull/26676 +.. _`#28262`: https://github.com/saltstack/salt/issues/28262 .. _`#28639`: https://github.com/saltstack/salt/pull/28639 +.. _`#28706`: https://github.com/saltstack/salt/issues/28706 .. _`#29322`: https://github.com/saltstack/salt/pull/29322 .. _`#30824`: https://github.com/saltstack/salt/pull/30824 .. _`#31139`: https://github.com/saltstack/salt/pull/31139 .. _`#31162`: https://github.com/saltstack/salt/pull/31162 .. _`#31164`: https://github.com/saltstack/salt/pull/31164 +.. _`#31270`: https://github.com/saltstack/salt/issues/31270 .. _`#31364`: https://github.com/saltstack/salt/pull/31364 .. _`#31382`: https://github.com/saltstack/salt/pull/31382 .. _`#31598`: https://github.com/saltstack/salt/pull/31598 @@ -778,6 +784,7 @@ Changes: .. _`#33224`: https://github.com/saltstack/salt/pull/33224 .. _`#33236`: https://github.com/saltstack/salt/pull/33236 .. _`#33237`: https://github.com/saltstack/salt/pull/33237 +.. _`#33238`: https://github.com/saltstack/salt/issues/33238 .. _`#33239`: https://github.com/saltstack/salt/pull/33239 .. _`#33244`: https://github.com/saltstack/salt/pull/33244 .. _`#33245`: https://github.com/saltstack/salt/pull/33245 @@ -789,5 +796,6 @@ Changes: .. _`#33274`: https://github.com/saltstack/salt/pull/33274 .. _`#33293`: https://github.com/saltstack/salt/pull/33293 .. _`#33294`: https://github.com/saltstack/salt/pull/33294 +.. _`#33299`: https://github.com/saltstack/salt/issues/33299 .. _`#33300`: https://github.com/saltstack/salt/pull/33300 .. _`#33305`: https://github.com/saltstack/salt/pull/33305 diff --git a/doc/topics/releases/2016.3.0.rst b/doc/topics/releases/2016.3.0.rst index 44e2b100cdf7..240e67f23325 100644 --- a/doc/topics/releases/2016.3.0.rst +++ b/doc/topics/releases/2016.3.0.rst @@ -351,7 +351,7 @@ Documentation of the Dimension Data SaltStack integration is found on `developer Minion Blackout --------------- During a blackout, minions will not execute any remote execution commands, -except for :ref:`saltutil.refresh_pillar +except for :mod:`saltutil.refresh_pillar `. Blackouts are enabled using a special pillar key, ``minion_blackout`` set to ``True``. diff --git a/doc/topics/releases/2016.3.1.rst b/doc/topics/releases/2016.3.1.rst index f9a36a3eb5d2..509c9ad854e3 100644 --- a/doc/topics/releases/2016.3.1.rst +++ b/doc/topics/releases/2016.3.1.rst @@ -155,14 +155,18 @@ Changes: .. _`#33519`: https://github.com/saltstack/salt/pull/33519 .. _`#33520`: https://github.com/saltstack/salt/pull/33520 .. _`#33522`: https://github.com/saltstack/salt/pull/33522 +.. _`#33530`: https://github.com/saltstack/salt/issues/33530 .. _`#33538`: https://github.com/saltstack/salt/pull/33538 .. _`#33549`: https://github.com/saltstack/salt/pull/33549 .. _`#33550`: https://github.com/saltstack/salt/pull/33550 .. _`#33555`: https://github.com/saltstack/salt/pull/33555 .. _`#33558`: https://github.com/saltstack/salt/pull/33558 .. _`#33562`: https://github.com/saltstack/salt/pull/33562 +.. _`#33565`: https://github.com/saltstack/salt/issues/33565 .. _`#33576`: https://github.com/saltstack/salt/pull/33576 +.. _`#33578`: https://github.com/saltstack/salt/issues/33578 .. _`#33581`: https://github.com/saltstack/salt/pull/33581 +.. _`#33590`: https://github.com/saltstack/salt/issues/33590 .. _`#33599`: https://github.com/saltstack/salt/pull/33599 .. _`#33603`: https://github.com/saltstack/salt/pull/33603 .. _`#33604`: https://github.com/saltstack/salt/pull/33604 @@ -170,6 +174,7 @@ Changes: .. _`#33613`: https://github.com/saltstack/salt/pull/33613 .. _`#33615`: https://github.com/saltstack/salt/pull/33615 .. _`#33631`: https://github.com/saltstack/salt/pull/33631 +.. _`#33632`: https://github.com/saltstack/salt/issues/33632 .. _`#33637`: https://github.com/saltstack/salt/pull/33637 .. _`#33638`: https://github.com/saltstack/salt/pull/33638 .. _`#33641`: https://github.com/saltstack/salt/pull/33641 @@ -205,6 +210,7 @@ Changes: .. _`#33743`: https://github.com/saltstack/salt/pull/33743 .. _`#33745`: https://github.com/saltstack/salt/pull/33745 .. _`#33748`: https://github.com/saltstack/salt/pull/33748 +.. _`#33754`: https://github.com/saltstack/salt/issues/33754 .. _`#33757`: https://github.com/saltstack/salt/pull/33757 .. _`#33759`: https://github.com/saltstack/salt/pull/33759 .. _`#33763`: https://github.com/saltstack/salt/pull/33763 diff --git a/doc/topics/releases/2016.3.4.rst b/doc/topics/releases/2016.3.4.rst index 4da922a103e6..4cd6e5a7b3f9 100644 --- a/doc/topics/releases/2016.3.4.rst +++ b/doc/topics/releases/2016.3.4.rst @@ -423,6 +423,7 @@ Changes: .. _`#36016`: https://github.com/saltstack/salt/pull/36016 .. _`#36018`: https://github.com/saltstack/salt/pull/36018 .. _`#36019`: https://github.com/saltstack/salt/pull/36019 +.. _`#36021`: https://github.com/saltstack/salt/issues/36021 .. _`#36022`: https://github.com/saltstack/salt/pull/36022 .. _`#36023`: https://github.com/saltstack/salt/pull/36023 .. _`#36024`: https://github.com/saltstack/salt/pull/36024 @@ -434,6 +435,7 @@ Changes: .. _`#36039`: https://github.com/saltstack/salt/pull/36039 .. _`#36040`: https://github.com/saltstack/salt/pull/36040 .. _`#36047`: https://github.com/saltstack/salt/pull/36047 +.. _`#36055`: https://github.com/saltstack/salt/issues/36055 .. _`#36061`: https://github.com/saltstack/salt/pull/36061 .. _`#36062`: https://github.com/saltstack/salt/pull/36062 .. _`#36068`: https://github.com/saltstack/salt/pull/36068 @@ -474,6 +476,7 @@ Changes: .. _`#36227`: https://github.com/saltstack/salt/pull/36227 .. _`#36235`: https://github.com/saltstack/salt/pull/36235 .. _`#36238`: https://github.com/saltstack/salt/pull/36238 +.. _`#36240`: https://github.com/saltstack/salt/issues/36240 .. _`#36241`: https://github.com/saltstack/salt/pull/36241 .. _`#36244`: https://github.com/saltstack/salt/pull/36244 .. _`#36245`: https://github.com/saltstack/salt/pull/36245 @@ -731,6 +734,7 @@ Changes: .. _`#37179`: https://github.com/saltstack/salt/pull/37179 .. _`#37183`: https://github.com/saltstack/salt/pull/37183 .. _`#37186`: https://github.com/saltstack/salt/pull/37186 +.. _`#37187`: https://github.com/saltstack/salt/issues/37187 .. _`#37188`: https://github.com/saltstack/salt/pull/37188 .. _`#37206`: https://github.com/saltstack/salt/pull/37206 .. _`#37207`: https://github.com/saltstack/salt/pull/37207 diff --git a/doc/topics/tutorials/gitfs.rst b/doc/topics/tutorials/gitfs.rst index 16e6dc3b10a5..3f8f4c1228a6 100644 --- a/doc/topics/tutorials/gitfs.rst +++ b/doc/topics/tutorials/gitfs.rst @@ -67,7 +67,8 @@ be used to install it: If pygit2_ is not packaged for the platform on which the Master is running, the -pygit2_ website has installation instructions here__. Keep in mind however that +pygit2_ website has installation instructions +`here `_. Keep in mind however that following these instructions will install libgit2_ and pygit2_ without system packages. Additionally, keep in mind that :ref:`SSH authentication in pygit2 ` requires libssh2_ (*not* libssh) development @@ -80,26 +81,26 @@ advisable as several other applications depend on it, so on older LTS linux releases pygit2_ 0.20.3 and libgit2_ 0.20.0 is the recommended combination. While these are not packaged in the official repositories for Debian and Ubuntu, SaltStack is actively working on adding packages for these to our -repositories_. The progress of this effort can be tracked here__. +repositories_. The progress of this effort can be tracked `here `_. .. warning:: - pygit2_ is actively developed and :ref:`frequently makes - non-backwards-compatible API changes `, even in + pygit2_ is actively developed and `frequently makes + non-backwards-compatible API changes `_, even in minor releases. It is not uncommon for pygit2_ upgrades to result in errors in Salt. Please take care when upgrading pygit2_, and pay close attention to the changelog_, keeping an eye out for API changes. Errors can be - reported on the :ref:`SaltStack issue tracker `. + reported on the `SaltStack issue tracker `_. .. _pygit2-version-policy: http://www.pygit2.org/install.html#version-numbers .. _changelog: https://github.com/libgit2/pygit2#changelog .. _saltstack-issue-tracker: https://github.com/saltstack/salt/issues -.. __: http://www.pygit2.org/install.html +.. _pygit2-install-instructions: http://www.pygit2.org/install.html .. _libgit2: https://libgit2.github.com/ .. _libssh2: http://www.libssh2.org/ .. _python-cffi: https://pypi.python.org/pypi/cffi .. _libffi: http://sourceware.org/libffi/ .. _repositories: https://repo.saltstack.com -.. __: https://github.com/saltstack/salt-pack/issues/70 +.. _salt-pack-70: https://github.com/saltstack/salt-pack/issues/70 GitPython --------- @@ -931,7 +932,7 @@ the minion is running. .. _`post-receive hook`: http://www.git-scm.com/book/en/Customizing-Git-Git-Hooks#Server-Side-Hooks -.. _git-as-ext_pillar +.. _git-as-ext_pillar: Using Git as an External Pillar Source ====================================== diff --git a/doc/topics/tutorials/http.rst b/doc/topics/tutorials/http.rst index c01bf7fcabad..1eaee6207196 100644 --- a/doc/topics/tutorials/http.rst +++ b/doc/topics/tutorials/http.rst @@ -377,7 +377,8 @@ using the ``ca_bundle`` variable. ) Updating CA Bundles -''''''''''''''''''' +``````````````````` + The ``update_ca_bundle()`` function can be used to update the bundle file at a specified location. If the target location is not specified, then it will attempt to auto-detect the location of the bundle file. If the URL to download diff --git a/doc/topics/tutorials/modules.rst b/doc/topics/tutorials/modules.rst index 415c9e9f547c..e1dbde7d764f 100644 --- a/doc/topics/tutorials/modules.rst +++ b/doc/topics/tutorials/modules.rst @@ -82,7 +82,7 @@ Run an arbitrary shell command: salt '*' cmd.run 'uname -a' -.. seealso:: :ref:`the full list of modules ` +.. seealso:: :ref:`the full list of modules ` arguments --------- diff --git a/doc/topics/tutorials/starting_states.rst b/doc/topics/tutorials/starting_states.rst index bc0fa2bc8dcb..16448b608f70 100644 --- a/doc/topics/tutorials/starting_states.rst +++ b/doc/topics/tutorials/starting_states.rst @@ -348,7 +348,7 @@ gives you a `"Pythonic"`_ interface to building state data. They can also be used in :mod:`file.managed ` states, making file management much more dynamic and flexible. Some examples for using templates in managed files can be found in the - documentation for the :ref:`file state `, as well as the + documentation for the :mod:`file state `, as well as the :ref:`MooseFS example` below. diff --git a/doc/topics/tutorials/walkthrough.rst b/doc/topics/tutorials/walkthrough.rst index 4a7783e590d2..9f691c5cc08b 100644 --- a/doc/topics/tutorials/walkthrough.rst +++ b/doc/topics/tutorials/walkthrough.rst @@ -294,7 +294,7 @@ This will display a very large list of available functions and documentation on them. .. note:: - Module documentation is also available :ref:`on the web `. + Module documentation is also available :ref:`on the web `. These functions cover everything from shelling out to package management to manipulating database servers. They comprise a powerful system management API @@ -304,7 +304,7 @@ of Salt. .. note:: Salt comes with many plugin systems. The functions that are available via - the ``salt`` command are called :ref:`Execution Modules `. + the ``salt`` command are called :ref:`Execution Modules `. Helpful Functions to Know diff --git a/doc/topics/tutorials/walkthrough_macosx.rst b/doc/topics/tutorials/walkthrough_macosx.rst index af5a860d7c9d..93183d9dd484 100644 --- a/doc/topics/tutorials/walkthrough_macosx.rst +++ b/doc/topics/tutorials/walkthrough_macosx.rst @@ -7,14 +7,11 @@ The macOS (Maverick) Developer Step By Step Guide To Salt Installation This document provides a step-by-step guide to installing a Salt cluster consisting of one master, and one minion running on a local VM hosted on macOS. - .. note:: This guide is aimed at developers who wish to run Salt in a virtual machine. The official (Linux) walkthrough can be found `here `_. - - The 5 Cent Salt Intro --------------------- @@ -63,18 +60,19 @@ Here's a brief overview of a Salt cluster: that. - Before Digging In, The Architecture Of The Salt Cluster ------------------------------------------------------- Salt Master +++++++++++ + The "Salt master" server is going to be the Mac OS machine, directly. Commands will be run from a terminal app, so Salt will need to be installed on the Mac. This is going to be more convenient for toying around with configuration files. Salt Minion +++++++++++ + We'll only have one "Salt minion" server. It is going to be running on a Virtual Machine running on the Mac, using VirtualBox. It will run an Ubuntu distribution. @@ -194,6 +192,7 @@ There should be no errors when running the above command. Now that the master is set, let's configure a minion on a VM. + Step 2 - Configuring The Minion VM ================================== @@ -324,6 +323,7 @@ following: It's now time to connect the VM to the salt master + Step 3 - Connecting Master and Minion ===================================== @@ -369,7 +369,6 @@ Then copy the .pub file into the list of accepted minions: sudo cp minion1.pub /etc/salt/pki/master/minions/minion1 - Modify Vagrantfile to Use Salt Provisioner ------------------------------------------ @@ -415,6 +414,7 @@ following: You should see your minion answering the ping. It's now time to do some configuration. + Step 4 - Configure Services to Install On the Minion ==================================================== diff --git a/doc/topics/tutorials/writing_tests.rst b/doc/topics/tutorials/writing_tests.rst index 56cedddefb76..3583c26c172c 100644 --- a/doc/topics/tutorials/writing_tests.rst +++ b/doc/topics/tutorials/writing_tests.rst @@ -8,7 +8,7 @@ Salt's Test Suite: An Introduction This tutorial makes a couple of assumptions. The first assumption is that you have a basic knowledge of Salt. To get up to speed, check out the - :ref:`Salt Walkthrough `. + :ref:`Salt Walkthrough `. The second assumption is that your Salt development environment is already configured and that you have a basic understanding of contributing to the diff --git a/doc/topics/yaml/index.rst b/doc/topics/yaml/index.rst index 54600d1e2778..bd2a7767f6c3 100644 --- a/doc/topics/yaml/index.rst +++ b/doc/topics/yaml/index.rst @@ -78,7 +78,7 @@ And in Python: } Rule Three: Dashes -------------------- +------------------ To represent lists of items, a single dash followed by a space is used. Multiple items are a part of the same list as a function of their having the same level of indentation. @@ -115,5 +115,4 @@ One excellent choice for experimenting with YAML parsing is: http://yaml-online- Templating ---------- Jinja statements and expressions are allowed by default in SLS files. See -:ref:`Understanding Jinja `. - +:ref:`Understanding Jinja `. diff --git a/salt/modules/solaris_user.py b/salt/modules/solaris_user.py index 67adc8a5e7f0..f135d1dcf4f1 100644 --- a/salt/modules/solaris_user.py +++ b/salt/modules/solaris_user.py @@ -3,10 +3,12 @@ Manage users with the useradd command .. important:: + If you feel that Salt should be using this module to manage users on a minion, and it is using a different module (or gives an error similar to *'user.info' is not available*), see :ref:`here `. + ''' # Import python libs diff --git a/salt/renderers/jinja.py b/salt/renderers/jinja.py index 0bab045a0e25..24f19b0d2489 100644 --- a/salt/renderers/jinja.py +++ b/salt/renderers/jinja.py @@ -2,7 +2,7 @@ ''' Jinja loading utils to enable a more powerful backend for jinja templates -For Jinja usage information see :ref:`Understanding Jinja `. +For Jinja usage information see :ref:`Understanding Jinja `. ''' # Import python libs diff --git a/salt/returners/postgres.py b/salt/returners/postgres.py index bec3d926fbe7..a2642b70f5ad 100644 --- a/salt/returners/postgres.py +++ b/salt/returners/postgres.py @@ -5,7 +5,7 @@ .. note:: :mod:`returners.postgres_local_cache ` is recommended instead of this module when using PostgreSQL as a - :ref:`master job cache `. These two modules + :ref:`master job cache `. These two modules provide different functionality so you should compare each to see which module best suits your particular needs. diff --git a/salt/returners/postgres_local_cache.py b/salt/returners/postgres_local_cache.py index 9397c355f9d0..ff33965f6662 100644 --- a/salt/returners/postgres_local_cache.py +++ b/salt/returners/postgres_local_cache.py @@ -6,7 +6,7 @@ .. note:: :mod:`returners.postgres ` is also available if you are not using PostgreSQL as a :ref:`master job cache - `. These two modules provide different + `. These two modules provide different functionality so you should compare each to see which module best suits your particular needs. diff --git a/salt/runners/salt.py b/salt/runners/salt.py index b699a7d92d32..825dbb7932dc 100644 --- a/salt/runners/salt.py +++ b/salt/runners/salt.py @@ -7,14 +7,14 @@ on the salt master. .. _salt_salt_runner: + Salt's execution modules are normally available on the salt minion. Use this runner to call execution modules on the salt master. Salt :ref:`execution modules ` are the functions called by the ``salt`` command. -Execution modules can be -called with ``salt-run``: +Execution modules can be called with ``salt-run``: .. code-block:: bash @@ -43,7 +43,7 @@ def cmd(fun, *args, **kwargs): ''' Execute ``fun`` with the given ``args`` and ``kwargs``. - Parameter ``fun`` should be the string :ref:`name ` + Parameter ``fun`` should be the string :ref:`name ` of the execution module to call. Note that execution modules will be *loaded every time* diff --git a/salt/states/boto_apigateway.py b/salt/states/boto_apigateway.py index d7bea14e4bf1..f26699d8d6f3 100644 --- a/salt/states/boto_apigateway.py +++ b/salt/states/boto_apigateway.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ''' Manage Apigateway Rest APIs -================= +=========================== .. versionadded:: 2016.11.0 @@ -31,18 +31,18 @@ .. code-block:: yaml myprofile: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs - region: us-east-1 + keyid: GKTADJGHEIQSXMKKRBJ08H + key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs + region: us-east-1 .. code-block:: yaml Ensure Apigateway API exists: - boto_apigateway.present: - - name: myfunction - - region: us-east-1 - - keyid: GKTADJGHEIQSXMKKRBJ08H - - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs + boto_apigateway.present: + - name: myfunction + - region: us-east-1 + - keyid: GKTADJGHEIQSXMKKRBJ08H + - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs ''' diff --git a/salt/states/cmd.py b/salt/states/cmd.py index 3ffc735c359e..27541196b682 100644 --- a/salt/states/cmd.py +++ b/salt/states/cmd.py @@ -579,7 +579,7 @@ def wait_script(name, **no**, **on**, **off**, **true**, and **false** are all loaded as boolean ``True`` and ``False`` values, and must be enclosed in quotes to be used as strings. More info on this (and other) PyYAML - idiosyncrasies can be found :ref:`here `. + idiosyncrasies can be found :ref:`here `. Variables as values are not evaluated. So $PATH in the following example is a literal '$PATH': From e6dda4a625bb95e0e6c6170b5da299b003dd66c7 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Mon, 6 Feb 2017 21:30:13 -0600 Subject: [PATCH 42/59] Sort the return list from the fileserver.envs runner --- salt/runners/fileserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/runners/fileserver.py b/salt/runners/fileserver.py index 7c0383379e3e..b2761cdaf2b6 100644 --- a/salt/runners/fileserver.py +++ b/salt/runners/fileserver.py @@ -37,7 +37,7 @@ def envs(backend=None, sources=False, outputter=None): salt-run fileserver.envs git ''' fileserver = salt.fileserver.Fileserver(__opts__) - output = fileserver.envs(back=backend, sources=sources) + output = sorted(fileserver.envs(back=backend, sources=sources)) if outputter: salt.utils.warn_until( From 2a99ff46d36eee1933a67bf23a73ed59d8771e0e Mon Sep 17 00:00:00 2001 From: Junya Hu Date: Tue, 7 Feb 2017 15:08:51 +0800 Subject: [PATCH 43/59] check if django_auth_path has been in sys.path --- salt/auth/django.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/salt/auth/django.py b/salt/auth/django.py index 37327fb96ee3..ea8593c47bd5 100644 --- a/salt/auth/django.py +++ b/salt/auth/django.py @@ -120,7 +120,9 @@ def auth(username, password): ''' Simple Django auth ''' - sys.path.append(__opts__['django_auth_path']) + django_auth_path = __opts__['django_auth_path'] + if django_auth_path not in sys.path: + sys.path.append(django_auth_path) os.environ.setdefault('DJANGO_SETTINGS_MODULE', __opts__['django_auth_settings']) django_auth_setup() From fd3284f0c75a14bd354307c3536a9adc1305e403 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 7 Feb 2017 12:18:28 -0600 Subject: [PATCH 44/59] Put legacy git_pillar on a deprecation path for Oxygen (#39225) This adds a warning when legacy git_pillar is used, and a notice in the docs. --- salt/pillar/git_pillar.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/salt/pillar/git_pillar.py b/salt/pillar/git_pillar.py index 32a6e2838472..b3e687c866ce 100644 --- a/salt/pillar/git_pillar.py +++ b/salt/pillar/git_pillar.py @@ -56,6 +56,10 @@ Configuring git_pillar for Salt releases before 2015.8.0 ======================================================== +.. note:: + This legacy configuration for git_pillar will no longer be supported as of + the **Oxygen** release of Salt. + For Salt releases earlier than :ref:`2015.8.0 `, GitPython is the only supported provider for git_pillar. Individual repositories can be configured under the :conf_master:`ext_pillar` @@ -539,6 +543,14 @@ def _legacy_git_pillar(minion_id, repo_string, pillar_dirs): ''' Support pre-Beryllium config schema ''' + salt.utils.warn_until( + 'Oxygen', + 'The git ext_pillar configuration is deprecated. Please refer to the ' + 'documentation at ' + 'https://docs.saltstack.com/en/latest/ref/pillar/all/salt.pillar.git_pillar.html ' + 'for more information. This configuration will no longer be supported ' + 'as of the Oxygen release of Salt.' + ) if pillar_dirs is None: return # split the branch, repo name and optional extra (key=val) parameters. From c75066294668a7ea7bf3c429985fe31bc30f5165 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 7 Feb 2017 13:50:41 -0600 Subject: [PATCH 45/59] Loader optimzation Use a str.join instead of str.format to make the full function name. --- salt/loader.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/salt/loader.py b/salt/loader.py index 9d3f5fec97fc..522f58a3992d 100644 --- a/salt/loader.py +++ b/salt/loader.py @@ -1416,8 +1416,12 @@ def _load_module(self, name): # It default's of course to the found callable attribute name # if no alias is defined. funcname = getattr(mod, '__func_alias__', {}).get(attr, attr) + try: + full_funcname = '.'.join((module_name, funcname)) + except TypeError: + full_funcname = '{0}.{1}'.format(module_name, funcname) # Save many references for lookups - self._dict['{0}.{1}'.format(module_name, funcname)] = func + self._dict[full_funcname] = func setattr(mod_dict, funcname, func) mod_dict[funcname] = func self._apply_outputter(func, mod) From afee047b082f18a4cd83c712d667ce1b818488f2 Mon Sep 17 00:00:00 2001 From: Daniel Wallace Date: Tue, 7 Feb 2017 14:27:01 -0600 Subject: [PATCH 46/59] default to utf8 encoding if not specified On errors, for some reason AWS does not always have an encoding on the message. Default to 'utf-8'. --- salt/utils/aws.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/utils/aws.py b/salt/utils/aws.py index a416eaaa02da..152e68f2020c 100644 --- a/salt/utils/aws.py +++ b/salt/utils/aws.py @@ -87,7 +87,7 @@ def creds(provider): proxies={'http': ''}, timeout=AWS_METADATA_TIMEOUT, ) result.raise_for_status() - role = result.text.encode(result.encoding) + role = result.text.encode(result.encoding or 'utf-8') except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError): return provider['id'], provider['key'], '' @@ -460,7 +460,7 @@ def query(params=None, setname=None, requesturl=None, location=None, ) LOG.trace( 'AWS Response Text: {0}'.format( - result.text.encode(result.encoding) + result.text.encode(result.encoding or 'utf-8') ) ) result.raise_for_status() @@ -501,7 +501,7 @@ def query(params=None, setname=None, requesturl=None, location=None, return {'error': data}, requesturl return {'error': data} - response = result.text.encode(result.encoding) + response = result.text.encode(result.encoding or 'utf-8') root = ET.fromstring(response) items = root[1] From ad1b1255f233e551cf75683b54c3b500232a261e Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 7 Feb 2017 15:03:11 -0600 Subject: [PATCH 47/59] Add clarification for jenkins execution module There are two modules on PyPI which import as "jenkins". Installing the wrong one will result in a traceback when the execution module tries to use the Python bindings. This commit adds information about the dependency to the docstring for the module. It also checks to make sure that jenkins.Jenkins is present in the __virtual__() func, and if not it will fail to load the execution module with a meaningful error message in the log. --- salt/modules/jenkins.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/salt/modules/jenkins.py b/salt/modules/jenkins.py index 07dfad0567cd..2b3999522aff 100644 --- a/salt/modules/jenkins.py +++ b/salt/modules/jenkins.py @@ -4,6 +4,11 @@ .. versionadded:: 2016.3.0 +:depends: python-jenkins_ Python module (not to be confused with jenkins_) + +.. _python-jenkins: https://pypi.python.org/pypi/python-jenkins +.. _jenkins: https://pypi.python.org/pypi/jenkins + :configuration: This module can be used by either passing an api key and version directly or by specifying both in a configuration profile in the salt master/minion config. @@ -45,9 +50,15 @@ def __virtual__(): :return: The virtual name of the module. ''' if HAS_JENKINS: - return __virtualname__ + if hasattr(jenkins, 'Jenkins'): + return __virtualname__ + else: + return (False, + 'The wrong Python module appears to be installed. Please ' + 'make sure that \'python-jenkins\' is installed, not ' + '\'jenkins\'.') return (False, 'The jenkins execution module cannot be loaded: ' - 'python jenkins library is not installed.') + 'python-jenkins is not installed.') def _connect(): From c88896c27707b01b28ec9d9f11a9dc98c5f1c195 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 7 Feb 2017 15:38:53 -0600 Subject: [PATCH 48/59] Avoid recursion in s3/svn ext_pillars This passes ``ext=False`` when compiling pillar data in these ext_pillar sources. --- salt/pillar/s3.py | 2 +- salt/pillar/svn_pillar.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/pillar/s3.py b/salt/pillar/s3.py index 6c5b9c724206..d821aa02a9aa 100644 --- a/salt/pillar/s3.py +++ b/salt/pillar/s3.py @@ -172,7 +172,7 @@ def ext_pillar(minion_id, pil = Pillar(opts, __grains__, minion_id, environment) - compiled_pillar = pil.compile_pillar() + compiled_pillar = pil.compile_pillar(ext=False) return compiled_pillar diff --git a/salt/pillar/svn_pillar.py b/salt/pillar/svn_pillar.py index 7be9ad9407e4..01f1fb0c1e9a 100644 --- a/salt/pillar/svn_pillar.py +++ b/salt/pillar/svn_pillar.py @@ -193,4 +193,4 @@ def ext_pillar(minion_id, opts = deepcopy(__opts__) opts['pillar_roots'][branch] = [pillar_dir] pil = Pillar(opts, __grains__, minion_id, branch) - return pil.compile_pillar() + return pil.compile_pillar(ext=False) From ef4e437bbc91df06f43d1a9c081be6dec131e126 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Tue, 7 Feb 2017 14:53:59 -0700 Subject: [PATCH 49/59] Fix the win_ip_test failures (#39230) The changes made in #38793 changes the "get_all_ointerfaces" call to be a list of DNS servers. This change adjusts the tests structure from a string to a list and fixes the test failure. --- tests/unit/modules/win_ip_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/modules/win_ip_test.py b/tests/unit/modules/win_ip_test.py index 301e8929b049..29c4b6a3a90a 100644 --- a/tests/unit/modules/win_ip_test.py +++ b/tests/unit/modules/win_ip_test.py @@ -65,12 +65,12 @@ def test_get_all_interfaces(self): Test if it return configs for all interfaces. ''' ret = {'Ethernet': {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': '1.2.3.4', + 'DNS servers configured through DHCP': ['1.2.3.4'], 'Default Gateway': '1.2.3.1', 'Gateway Metric': '0', 'InterfaceMetric': '20', 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': 'None', + 'WINS servers configured through DHCP': ['None'], 'ip_addrs': [{'IP Address': '1.2.3.74', 'Netmask': '255.255.255.0', 'Subnet': '1.2.3.0/24'}]}} @@ -86,11 +86,11 @@ def test_get_interface(self): Test if it return the configuration of a network interface. ''' ret = {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': '1.2.3.4', + 'DNS servers configured through DHCP': ['1.2.3.4'], 'Default Gateway': '1.2.3.1', 'Gateway Metric': '0', 'InterfaceMetric': '20', 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': 'None', + 'WINS servers configured through DHCP': ['None'], 'ip_addrs': [{'IP Address': '1.2.3.74', 'Netmask': '255.255.255.0', 'Subnet': '1.2.3.0/24'}]} From 84ff63875ce0a71172fc5cc1c82288bca42184ba Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Tue, 7 Feb 2017 16:06:56 -0700 Subject: [PATCH 50/59] [2016.11] Merge forward from 2016.3 to 2016.11 (#39234) * Fix for #38697 * Lint fixes * Added missing source_hash_name argument in get_managed function Additional fix to #33187 Customer was still seeing errors, this should now work. Tested with 2015.8.13 and 2016.11.2 * [2016.3] Pylint fix (#39202) * Ignore empty dicts in highstate outputter Closes #37174 * Sort the return list from the fileserver.envs runner * Fix the win_ip_test failures (#39230) The changes made in #38793 changes the "get_all_ointerfaces" call to be a list of DNS servers. This change adjusts the tests structure from a string to a list and fixes the test failure. --- salt/modules/win_ip.py | 86 ++++++++++++++----------------- salt/output/highstate.py | 2 +- salt/runners/fileserver.py | 2 +- salt/states/jboss7.py | 1 + tests/unit/modules/win_ip_test.py | 8 +-- 5 files changed, 45 insertions(+), 54 deletions(-) diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index 4729099b5c1f..dc7416b78eb5 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -6,7 +6,6 @@ # Import python libs import logging -import socket import time # Import salt libs @@ -41,62 +40,53 @@ def _interface_configs(): ''' cmd = ['netsh', 'interface', 'ip', 'show', 'config'] lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - iface = '' - ip = 0 - dns_flag = None - wins_flag = None ret = {} + current_iface = None + current_ip_list = None + for line in lines: - if dns_flag: - try: - socket.inet_aton(line.strip()) - ret[iface][dns_flag].append(line.strip()) - dns_flag = None - continue - except socket.error as exc: - dns_flag = None - if wins_flag: - try: - socket.inet_aton(line.strip()) - ret[iface][wins_flag].append(line.strip()) - wins_flag = None - continue - except socket.error as exc: - wins_flag = None + + line = line.strip() if not line: - iface = '' + current_iface = None + current_ip_list = None continue + if 'Configuration for interface' in line: _, iface = line.rstrip('"').split('"', 1) # get iface name - ret[iface] = {} - ip = 0 - continue - try: - key, val = line.split(':', 1) - except ValueError as exc: - log.debug('Could not split line. Error was {0}.'.format(exc)) - continue - if 'DNS Servers' in line: - dns_flag = key.strip() - ret[iface][key.strip()] = [val.strip()] - continue - if 'WINS Servers' in line: - wins_flag = key.strip() - ret[iface][key.strip()] = [val.strip()] - continue - if 'IP Address' in key: - if 'ip_addrs' not in ret[iface]: - ret[iface]['ip_addrs'] = [] - ret[iface]['ip_addrs'].append(dict([(key.strip(), val.strip())])) + current_iface = {} + ret[iface] = current_iface continue - if 'Subnet Prefix' in key: - subnet, _, netmask = val.strip().split(' ', 2) - ret[iface]['ip_addrs'][ip]['Subnet'] = subnet.strip() - ret[iface]['ip_addrs'][ip]['Netmask'] = netmask.lstrip().rstrip(')') - ip = ip + 1 + + if ':' not in line: + if current_ip_list: + current_ip_list.append(line) + else: + log.warning('Cannot parse "{0}"'.format(line)) continue + + key, val = line.split(':', 1) + key = key.strip() + val = val.strip() + + lkey = key.lower() + if ('dns servers' in lkey) or ('wins servers' in lkey): + current_ip_list = [] + current_iface[key] = current_ip_list + current_ip_list.append(val) + + elif 'ip address' in lkey: + current_iface.setdefault('ip_addrs', []).append({key: val}) + + elif 'subnet prefix' in lkey: + subnet, _, netmask = val.split(' ', 2) + last_ip = current_iface['ip_addrs'][-1] + last_ip['Subnet'] = subnet.strip() + last_ip['Netmask'] = netmask.lstrip().rstrip(')') + else: - ret[iface][key.strip()] = val.strip() + current_iface[key] = val + return ret diff --git a/salt/output/highstate.py b/salt/output/highstate.py index 72daca5210af..013a750800f8 100644 --- a/salt/output/highstate.py +++ b/salt/output/highstate.py @@ -190,7 +190,7 @@ def _format_host(host, data): # Verify that the needed data is present data_tmp = {} for tname, info in six.iteritems(data): - if isinstance(info, dict) and tname is not 'changes' and '__run_num__' not in info: + if isinstance(info, dict) and tname is not 'changes' and info and '__run_num__' not in info: err = (u'The State execution failed to record the order ' 'in which all states were executed. The state ' 'return missing data is:') diff --git a/salt/runners/fileserver.py b/salt/runners/fileserver.py index c5c416ddc08e..6322aa78427d 100644 --- a/salt/runners/fileserver.py +++ b/salt/runners/fileserver.py @@ -36,7 +36,7 @@ def envs(backend=None, sources=False): salt-run fileserver.envs git ''' fileserver = salt.fileserver.Fileserver(__opts__) - return fileserver.envs(back=backend, sources=sources) + return sorted(fileserver.envs(back=backend, sources=sources)) def clear_file_list_cache(saltenv=None, backend=None): diff --git a/salt/states/jboss7.py b/salt/states/jboss7.py index 0272e96e4c0e..7eb6ad91c01b 100644 --- a/salt/states/jboss7.py +++ b/salt/states/jboss7.py @@ -412,6 +412,7 @@ def __get_artifact(salt_source): template=None, source=salt_source['source'], source_hash=None, + source_hash_name=None, user=None, group=None, mode=None, diff --git a/tests/unit/modules/win_ip_test.py b/tests/unit/modules/win_ip_test.py index 301e8929b049..29c4b6a3a90a 100644 --- a/tests/unit/modules/win_ip_test.py +++ b/tests/unit/modules/win_ip_test.py @@ -65,12 +65,12 @@ def test_get_all_interfaces(self): Test if it return configs for all interfaces. ''' ret = {'Ethernet': {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': '1.2.3.4', + 'DNS servers configured through DHCP': ['1.2.3.4'], 'Default Gateway': '1.2.3.1', 'Gateway Metric': '0', 'InterfaceMetric': '20', 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': 'None', + 'WINS servers configured through DHCP': ['None'], 'ip_addrs': [{'IP Address': '1.2.3.74', 'Netmask': '255.255.255.0', 'Subnet': '1.2.3.0/24'}]}} @@ -86,11 +86,11 @@ def test_get_interface(self): Test if it return the configuration of a network interface. ''' ret = {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': '1.2.3.4', + 'DNS servers configured through DHCP': ['1.2.3.4'], 'Default Gateway': '1.2.3.1', 'Gateway Metric': '0', 'InterfaceMetric': '20', 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': 'None', + 'WINS servers configured through DHCP': ['None'], 'ip_addrs': [{'IP Address': '1.2.3.74', 'Netmask': '255.255.255.0', 'Subnet': '1.2.3.0/24'}]} From 6f4be8b69c597515724efb07cf493ef75c6d1268 Mon Sep 17 00:00:00 2001 From: Evgeny Akhmetkhanov Date: Tue, 7 Feb 2017 15:46:31 -0800 Subject: [PATCH 51/59] fix rds subnet group creation --- salt/modules/boto_rds.py | 5 ++++- salt/states/boto_rds.py | 19 ++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/salt/modules/boto_rds.py b/salt/modules/boto_rds.py index 776e65a21611..5032ba691ea0 100644 --- a/salt/modules/boto_rds.py +++ b/salt/modules/boto_rds.py @@ -220,7 +220,10 @@ def subnet_group_exists(name, tags=None, region=None, key=None, keyid=None, rds = conn.describe_db_subnet_groups(DBSubnetGroupName=name) return {'exists': bool(rds)} except ClientError as e: - return {'error': salt.utils.boto3.get_error(e)} + if "DBSubnetGroupNotFoundFault" in e.message: + return {'exists': False} + else: + return {'error': salt.utils.boto3.get_error(e)} def create(name, allocated_storage, db_instance_class, engine, diff --git a/salt/states/boto_rds.py b/salt/states/boto_rds.py index 6475624f0747..15163add8a57 100644 --- a/salt/states/boto_rds.py +++ b/salt/states/boto_rds.py @@ -490,21 +490,17 @@ def subnet_group_present(name, description, subnet_ids=None, subnet_names=None, exists = __salt__['boto_rds.subnet_group_exists'](name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile) - if not exists: + if not exists.get('exists'): if __opts__['test']: ret['comment'] = 'Subnet group {0} is set to be created.'.format(name) ret['result'] = None return ret - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create {0} subnet group.'.format(r['error']['message']) - return ret created = __salt__['boto_rds.create_subnet_group'](name=name, - description=description, - subnet_ids=subnet_ids, - tags=tags, region=region, - key=key, keyid=keyid, - profile=profile) + description=description, + subnet_ids=subnet_ids, + tags=tags, region=region, + key=key, keyid=keyid, + profile=profile) if not created: ret['result'] = False @@ -513,6 +509,7 @@ def subnet_group_present(name, description, subnet_ids=None, subnet_names=None, ret['changes']['old'] = None ret['changes']['new'] = name ret['comment'] = 'Subnet {0} created.'.format(name) + return ret else: ret['comment'] = 'Subnet {0} present.'.format(name) @@ -673,7 +670,7 @@ def parameter_present(name, db_parameter_group_family, description, parameters=N 'changes': {} } res = __salt__['boto_rds.parameter_group_exists'](name=name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) + keyid=keyid, profile=profile) if not res.get('exists'): if __opts__['test']: ret['comment'] = 'Parameter group {0} is set to be created.'.format(name) From 1a32b2cc89a3c0a21555987406d665d221486d20 Mon Sep 17 00:00:00 2001 From: David Murphy < dmurphy@saltstack.com> Date: Tue, 7 Feb 2017 15:10:19 -0700 Subject: [PATCH 52/59] Updated disk functionality for fstype, inodeuage and percent on AIX --- salt/modules/disk.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/salt/modules/disk.py b/salt/modules/disk.py index c2f9c491e8fe..d6de6b90c432 100644 --- a/salt/modules/disk.py +++ b/salt/modules/disk.py @@ -141,7 +141,10 @@ def inodeusage(args=None): salt '*' disk.inodeusage ''' flags = _clean_flags(args, 'disk.inodeusage') - cmd = 'df -iP' + if __grains__['kernel'] == 'AIX': + cmd = 'df -i' + else: + cmd = 'df -iP' if flags: cmd += ' -{0}'.format(flags) ret = {} @@ -163,6 +166,14 @@ def inodeusage(args=None): 'use': comps[7], 'filesystem': comps[0], } + elif __grains__['kernel'] == 'AIX': + ret[comps[6]] = { + 'inodes': comps[4], + 'used': comps[5], + 'free': comps[2], + 'use': comps[5], + 'filesystem': comps[0], + } else: ret[comps[5]] = { 'inodes': comps[1], @@ -191,6 +202,8 @@ def percent(args=None): cmd = 'df -P' elif __grains__['kernel'] == 'OpenBSD': cmd = 'df -kP' + elif __grains__['kernel'] == 'AIX': + cmd = 'df -kP' else: cmd = 'df' ret = {} @@ -201,9 +214,11 @@ def percent(args=None): if line.startswith('Filesystem'): continue comps = line.split() - while not comps[1].isdigit(): + while len(comps) >= 2 and not comps[1].isdigit(): comps[0] = '{0} {1}'.format(comps[0], comps[1]) comps.pop(1) + if len(comps) < 2: + continue try: if __grains__['kernel'] == 'Darwin': ret[comps[8]] = comps[4] @@ -464,11 +479,18 @@ def fstype(device): if salt.utils.which('df'): # the fstype was not set on the block device, so inspect the filesystem # itself for its type - df_out = __salt__['cmd.run']('df -T {0}'.format(device)).splitlines() - if len(df_out) > 1: - fs_type = df_out[1] - if fs_type: - return fs_type + if __grains__['kernel'] == 'AIX' and os.stat('/usr/sysv/bin/df'): + df_out = __salt__['cmd.run']('/usr/sysv/bin/df -n {0}'.format(device)).split() + if len(df_out) > 2: + fs_type = df_out[2] + if fs_type: + return fs_type + else: + df_out = __salt__['cmd.run']('df -T {0}'.format(device)).splitlines() + if len(df_out) > 1: + fs_type = df_out[1] + if fs_type: + return fs_type return '' From 97c59a8d1c5bb6f0682991ae832118092cdff085 Mon Sep 17 00:00:00 2001 From: David Murphy < dmurphy@saltstack.com> Date: Tue, 7 Feb 2017 16:48:26 -0700 Subject: [PATCH 53/59] Updated mount functionality for active on AIX --- salt/modules/mount.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/salt/modules/mount.py b/salt/modules/mount.py index 79e9167900b8..6b2ad6e900ae 100644 --- a/salt/modules/mount.py +++ b/salt/modules/mount.py @@ -110,6 +110,27 @@ def _active_mounts(ret): return ret +def _active_mounts_aix(ret): + ''' + List active mounts on AIX systems + ''' + for line in __salt__['cmd.run_stdout']('mount -p').split('\n'): + comps = re.sub(r"\s+", " ", line).split() + if comps and comps[0] == 'node' or comps[0] == '--------': + continue + if len(comps) < 8: + ret[comps[1]] = {'device': comps[0], + 'fstype': comps[2], + 'opts': _resolve_user_group_names(comps[6].split(','))} + else: + ret[comps[2]] = {'node': comps[0], + 'device': comps[1], + 'fstype': comps[3], + 'opts': _resolve_user_group_names(comps[7].split(','))} + + return ret + + def _active_mounts_freebsd(ret): ''' List active mounts on FreeBSD systems @@ -202,6 +223,8 @@ def active(extended=False): ret = {} if __grains__['os'] == 'FreeBSD': _active_mounts_freebsd(ret) + elif __grains__['kernel'] == 'AIX': + _active_mounts_aix(ret) elif __grains__['kernel'] == 'SunOS': _active_mounts_solaris(ret) elif __grains__['os'] == 'OpenBSD': From 8fa0ffa4274814adf599bb122e2dc1248117a883 Mon Sep 17 00:00:00 2001 From: David Murphy < dmurphy@saltstack.com> Date: Wed, 8 Feb 2017 12:37:11 -0700 Subject: [PATCH 54/59] Updates due to code review comments --- salt/modules/disk.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/salt/modules/disk.py b/salt/modules/disk.py index d6de6b90c432..50bc1fd9a1c0 100644 --- a/salt/modules/disk.py +++ b/salt/modules/disk.py @@ -75,9 +75,7 @@ def usage(args=None): return {} if __grains__['kernel'] == 'Linux': cmd = 'df -P' - elif __grains__['kernel'] == 'OpenBSD': - cmd = 'df -kP' - elif __grains__['kernel'] == 'AIX': + elif __grains__['kernel'] == 'OpenBSD' or __grains__['kernel'] == 'AIX': cmd = 'df -kP' else: cmd = 'df' @@ -200,9 +198,7 @@ def percent(args=None): ''' if __grains__['kernel'] == 'Linux': cmd = 'df -P' - elif __grains__['kernel'] == 'OpenBSD': - cmd = 'df -kP' - elif __grains__['kernel'] == 'AIX': + elif __grains__['kernel'] == 'OpenBSD' or __grains__['kernel'] == 'AIX': cmd = 'df -kP' else: cmd = 'df' @@ -479,7 +475,7 @@ def fstype(device): if salt.utils.which('df'): # the fstype was not set on the block device, so inspect the filesystem # itself for its type - if __grains__['kernel'] == 'AIX' and os.stat('/usr/sysv/bin/df'): + if __grains__['kernel'] == 'AIX' and os.path.isfile('/usr/sysv/bin/df'): df_out = __salt__['cmd.run']('/usr/sysv/bin/df -n {0}'.format(device)).split() if len(df_out) > 2: fs_type = df_out[2] From a8a519c493ad0d38be1c96be79347d4a8c3ac095 Mon Sep 17 00:00:00 2001 From: David Murphy < dmurphy@saltstack.com> Date: Wed, 8 Feb 2017 12:42:14 -0700 Subject: [PATCH 55/59] Removed space for pylint --- salt/modules/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/disk.py b/salt/modules/disk.py index 50bc1fd9a1c0..6926894bf5d4 100644 --- a/salt/modules/disk.py +++ b/salt/modules/disk.py @@ -75,7 +75,7 @@ def usage(args=None): return {} if __grains__['kernel'] == 'Linux': cmd = 'df -P' - elif __grains__['kernel'] == 'OpenBSD' or __grains__['kernel'] == 'AIX': + elif __grains__['kernel'] == 'OpenBSD' or __grains__['kernel'] == 'AIX': cmd = 'df -kP' else: cmd = 'df' From c1d16cc3d05a10b7def4e0833409276bbc6c788e Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 8 Feb 2017 15:52:45 -0600 Subject: [PATCH 56/59] Better handling of enabled/disabled arguments in pkgrepo.managed (#39251) This reverses the decision made in #35055 to deprecate the "enabled" parameter in this state. This was a poorly-conceived decision, likely made because the "enabled" param was not included in the docstring for the pkgrepo.managed state under the "YUM/ZYPPER" section, while the "disabled" param *was* listed under the "APT" section. "disabled" isn't a thing in yum/dnf, so there was never any reason to try to shoehorn it in. Not to mention the fact that the "disabled" argument isn't even supported in Zypper, which effectively breaks Zypper support. This commit normalizes enabled/disabled based on platform, so passing "enabled" in APT will pass the opposite value as "disabled", and vice-versa on the other platforms. This allows enabled/disabled to be used interchangeably. It also more gracefully handles booleans in yum/dnf/zypper, so that a bool can be properly compared to a 1/0 value. --- salt/modules/yumpkg.py | 19 ++++---- salt/states/pkgrepo.py | 101 ++++++++++++++++++++++++++--------------- 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index a7dcdc0b870a..1bf4a87ed468 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -2265,18 +2265,12 @@ def mod_repo(repo, basedir=None, **kwargs): ) # Build a list of keys to be deleted - todelete = ['disabled'] + todelete = [] for key in repo_opts: if repo_opts[key] != 0 and not repo_opts[key]: del repo_opts[key] todelete.append(key) - # convert disabled to enabled respectively from pkgrepo state - if 'enabled' not in repo_opts: - repo_opts['enabled'] = int(str(repo_opts.pop('disabled', False)).lower() != 'true') - else: - repo_opts.pop('disabled', False) - # Add baseurl or mirrorlist to the 'todelete' list if the other was # specified in the repo_opts if 'mirrorlist' in repo_opts: @@ -2348,6 +2342,7 @@ def mod_repo(repo, basedir=None, **kwargs): if key in six.iterkeys(filerepos[repo].copy()): del filerepos[repo][key] + _bool_to_str = lambda x: '1' if x else '0' # Old file or new, write out the repos(s) filerepos[repo].update(repo_opts) content = header @@ -2358,7 +2353,12 @@ def mod_repo(repo, basedir=None, **kwargs): del filerepos[stanza]['comments'] content += '\n[{0}]'.format(stanza) for line in six.iterkeys(filerepos[stanza]): - content += '\n{0}={1}'.format(line, filerepos[stanza][line]) + content += '\n{0}={1}'.format( + line, + filerepos[stanza][line] + if not isinstance(filerepos[stanza][line], bool) + else _bool_to_str(filerepos[stanza][line]) + ) content += '\n{0}\n'.format(comments) with salt.utils.fopen(repofile, 'w') as fileout: @@ -2404,9 +2404,6 @@ def _parse_repo_file(filename): 'Failed to parse line in %s, offending line was ' '\'%s\'', filename, line.rstrip() ) - # YUM uses enabled field - create the disabled field so that comparisons works correctly in state - if comps[0].strip() == 'enabled': - repos[repo]['disabled'] = comps[1] != "1" return (header, repos) diff --git a/salt/states/pkgrepo.py b/salt/states/pkgrepo.py index 7901c0b95541..229feda6f66c 100644 --- a/salt/states/pkgrepo.py +++ b/salt/states/pkgrepo.py @@ -112,7 +112,7 @@ def managed(name, ppa=None, **kwargs): `, :mod:`apt `, and :mod:`zypper ` repositories are supported. - **YUM OR ZYPPER-BASED SYSTEMS** + **YUM/DNF/ZYPPER-BASED SYSTEMS** .. note:: One of ``baseurl`` or ``mirrorlist`` below is required. Additionally, @@ -126,6 +126,16 @@ def managed(name, ppa=None, **kwargs): repo. Secondly, it will be the name of the file as stored in /etc/yum.repos.d (e.g. ``/etc/yum.repos.d/foo.conf``). + enabled : True + Whether or not the repo is enabled. Can be specified as True/False or + 1/0. + + disabled : False + Included to reduce confusion due to APT's use of the ``disabled`` + argument. If this is passed for a yum/dnf/zypper-based distro, then the + reverse will be passed as ``enabled``. For example passing + ``disabled=True`` will assume ``enabled=False``. + humanname This is used as the "name" value in the repo file in ``/etc/yum.repos.d/`` (or ``/etc/zypp/repos.d`` for Suse distros). @@ -203,10 +213,16 @@ def managed(name, ppa=None, **kwargs): 'deb http://us.archive.ubuntu.com/ubuntu precise main': pkgrepo.managed - disabled + disabled : False Toggles whether or not the repo is used for resolving dependencies and/or installing packages. + enabled : True + Included to reduce confusion due to yum/dnf/zypper's use of the + ``enabled`` argument. If this is passed for an APT-based distro, then + the reverse will be passed as ``disabled``. For example, passing + ``enabled=False`` will assume ``disabled=False``. + comps On apt-based systems, comps dictate the types of packages to be installed from the repository (e.g. main, nonfree, ...). For @@ -281,14 +297,19 @@ def managed(name, ppa=None, **kwargs): 'intended.') return ret - if 'enabled' in kwargs: - salt.utils.warn_until( - 'Nitrogen', - 'The `enabled` argument has been deprecated in favor of ' - '`disabled`.' - ) + enabled = kwargs.pop('enabled', None) + disabled = kwargs.pop('disabled', None) + + if enabled is not None and disabled is not None: + ret['result'] = False + ret['comment'] = 'Only one of enabled/disabled is allowed' + return ret + elif enabled is None and disabled is None: + # If neither argument was passed we assume the repo will be enabled + enabled = True repo = name + os_family = __grains__['os_family'].lower() if __grains__['os'] in ('Ubuntu', 'Mint'): if ppa is not None: # overload the name/repo value for PPAs cleanly @@ -298,26 +319,26 @@ def managed(name, ppa=None, **kwargs): except TypeError: repo = ':'.join(('ppa', str(ppa))) - elif __grains__['os_family'].lower() in ('redhat', 'suse'): + kwargs['disabled'] = not salt.utils.is_true(enabled) \ + if enabled is not None \ + else salt.utils.is_true(disabled) + + elif os_family in ('redhat', 'suse'): if 'humanname' in kwargs: kwargs['name'] = kwargs.pop('humanname') - _val = lambda x: '1' if salt.utils.is_true(x) else '0' - if 'disabled' in kwargs: - if 'enabled' in kwargs: - ret['result'] = False - ret['comment'] = 'Only one of enabled/disabled is permitted' - return ret - _reverse = lambda x: '1' if x == '0' else '0' - kwargs['enabled'] = _reverse(_val(kwargs.pop('disabled'))) - elif 'enabled' in kwargs: - kwargs['enabled'] = _val(kwargs['enabled']) if 'name' not in kwargs: # Fall back to the repo name if humanname not provided kwargs['name'] = repo - # Replace 'enabled' from kwargs with 'disabled' - enabled = kwargs.pop('enabled', True) - kwargs['disabled'] = not salt.utils.is_true(enabled) + kwargs['enabled'] = not salt.utils.is_true(disabled) \ + if disabled is not None \ + else salt.utils.is_true(enabled) + + elif os_family == 'nilinuxrt': + # opkg is the pkg virtual + kwargs['enabled'] = not salt.utils.is_true(disabled) \ + if disabled is not None \ + else salt.utils.is_true(enabled) for kwarg in _STATE_INTERNAL_KEYWORDS: kwargs.pop(kwarg, None) @@ -342,11 +363,10 @@ def managed(name, ppa=None, **kwargs): else: sanitizedkwargs = kwargs - if __grains__['os_family'] == 'Debian': + if os_family == 'debian': repo = _strip_uri(repo) if pre: - needs_update = False for kwarg in sanitizedkwargs: if kwarg not in pre: if kwarg == 'enabled': @@ -354,33 +374,40 @@ def managed(name, ppa=None, **kwargs): # not explicitly set, so we don't need to update the repo # if it's desired to be enabled and the 'enabled' key is # missing from the repo definition - if __grains__['os_family'] == 'RedHat': + if os_family == 'redhat': if not salt.utils.is_true(sanitizedkwargs[kwarg]): - needs_update = True + break else: - needs_update = True + break else: - needs_update = True + break elif kwarg == 'comps': if sorted(sanitizedkwargs[kwarg]) != sorted(pre[kwarg]): - needs_update = True - elif kwarg == 'line' and __grains__['os_family'] == 'Debian': + break + elif kwarg == 'line' and os_family == 'debian': # split the line and sort everything after the URL sanitizedsplit = sanitizedkwargs[kwarg].split() sanitizedsplit[3:] = sorted(sanitizedsplit[3:]) reposplit = pre[kwarg].split() reposplit[3:] = sorted(reposplit[3:]) if sanitizedsplit != reposplit: - needs_update = True + break if 'comments' in kwargs: _line = pre[kwarg].split('#') if str(kwargs['comments']) not in _line: - needs_update = True + break else: - if str(sanitizedkwargs[kwarg]) != str(pre[kwarg]): - needs_update = True - - if not needs_update: + if os_family in ('redhat', 'suse') \ + and any(isinstance(x, bool) for x in + (sanitizedkwargs[kwarg], pre[kwarg])): + # This check disambiguates 1/0 from True/False + if salt.utils.is_true(sanitizedkwargs[kwarg]) != \ + salt.utils.is_true(pre[kwarg]): + break + else: + if str(sanitizedkwargs[kwarg]) != str(pre[kwarg]): + break + else: ret['result'] = True ret['comment'] = ('Package repo \'{0}\' already configured' .format(name)) @@ -401,7 +428,7 @@ def managed(name, ppa=None, **kwargs): pass try: - if __grains__['os_family'] == 'Debian': + if os_family == 'debian': __salt__['pkg.mod_repo'](repo, saltenv=__env__, **kwargs) else: __salt__['pkg.mod_repo'](repo, **kwargs) From 1b9217d6362702d9fd49dfc09f0ada9051a11191 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 8 Feb 2017 17:51:56 -0600 Subject: [PATCH 57/59] Update jsonschema tests to reflect change in jsonschema 2.6.0 (#39260) Version 2.6.0 changed the wording of one of the exceptions tested, causing tests to fail when jsonschema 2.6.0 is installed. This commit updates the tests to change the assert for 2.6.0 and later. --- tests/unit/config/schemas/ssh_test.py | 17 +++++++++++++---- tests/unit/utils/schema_test.py | 18 ++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/tests/unit/config/schemas/ssh_test.py b/tests/unit/config/schemas/ssh_test.py index 02b47ef7cc73..14f9bed3f617 100644 --- a/tests/unit/config/schemas/ssh_test.py +++ b/tests/unit/config/schemas/ssh_test.py @@ -7,6 +7,7 @@ ''' # Import python libs from __future__ import absolute_import, print_function +from distutils.version import LooseVersion as _LooseVersion # Import Salt Testing Libs from salttesting import TestCase, skipIf @@ -23,8 +24,10 @@ import jsonschema import jsonschema.exceptions HAS_JSONSCHEMA = True + JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__) except ImportError: HAS_JSONSCHEMA = False + JSONSCHEMA_VERSION = _LooseVersion('0') class RoosterEntryConfigTest(TestCase): @@ -296,7 +299,13 @@ def test_roster_config_validate(self): ssh_schemas.RosterItem.serialize(), format_checker=jsonschema.FormatChecker() ) - self.assertIn( - 'Additional properties are not allowed (\'target-1:1\' was unexpected)', - excinfo.exception.message - ) + if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'): + self.assertIn( + 'Additional properties are not allowed (\'target-1:1\' was unexpected)', + excinfo.exception.message + ) + else: + self.assertIn( + '\'target-1:1\' does not match any of the regexes', + excinfo.exception.message + ) diff --git a/tests/unit/utils/schema_test.py b/tests/unit/utils/schema_test.py index 73db06d7fd23..e4f50cf73cf4 100644 --- a/tests/unit/utils/schema_test.py +++ b/tests/unit/utils/schema_test.py @@ -6,7 +6,7 @@ # Import python libs from __future__ import absolute_import -from distutils.version import LooseVersion +from distutils.version import LooseVersion as _LooseVersion # Import Salt Testing Libs from salttesting import TestCase, skipIf @@ -22,10 +22,10 @@ import jsonschema import jsonschema.exceptions HAS_JSONSCHEMA = True - JSONSCHEMA_VERSION = jsonschema.__version__ + JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__) except ImportError: - JSONSCHEMA_VERSION = '' HAS_JSONSCHEMA = False + JSONSCHEMA_VERSION = _LooseVersion('0') # pylint: disable=unused-import @@ -749,8 +749,7 @@ def test_ipv4_config(self): } ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') - @skipIf(HAS_JSONSCHEMA and LooseVersion(jsonschema.__version__) <= LooseVersion('2.5.0'), 'Requires jsonschema 2.5.0 or greater') + @skipIf(JSONSCHEMA_VERSION <= _LooseVersion('2.5.0'), 'Requires jsonschema 2.5.0 or greater') def test_ipv4_config_validation(self): class TestConf(schema.Schema): item = schema.IPv4Item(title='Item', description='Item description') @@ -1702,7 +1701,14 @@ class TestConf(schema.Schema): with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'color': 'green', 'sides': 4, 'surfaces': 4}}, TestConf.serialize()) - self.assertIn('Additional properties are not allowed', excinfo.exception.message) + if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'): + self.assertIn( + 'Additional properties are not allowed', + excinfo.exception.message) + else: + self.assertIn( + '\'surfaces\' does not match any of the regexes', + excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( From 6635a9fd3b22e51534ff6fc810f3345e192956fc Mon Sep 17 00:00:00 2001 From: Daniel Wallace Date: Thu, 9 Feb 2017 09:44:26 -0600 Subject: [PATCH 58/59] _device_mismatch_ignored will never be True It will be the name of the device that has matched the regex --- salt/states/mount.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/salt/states/mount.py b/salt/states/mount.py index 43ecf7b20756..dd81bfa5ce9e 100644 --- a/salt/states/mount.py +++ b/salt/states/mount.py @@ -412,18 +412,19 @@ def mounted(name, opts.remove('remount') if real_device not in device_list: # name matches but device doesn't - need to umount - _device_mismatch_is_ignored = False + _device_mismatch_is_ignored = None for regex in list(device_name_regex): for _device in device_list: if re.match(regex, _device): _device_mismatch_is_ignored = _device + break if __opts__['test']: ret['result'] = None ret['comment'] = "An umount would have been forced " \ + "because devices do not match. Watched: " \ + device - elif _device_mismatch_is_ignored is True: - ret['result'] = None + elif _device_mismatch_is_ignored: + ret['result'] = True ret['comment'] = "An umount will not be forced " \ + "because device matched device_name_regex: " \ + _device_mismatch_is_ignored From a9c2c106c18d375087589e6f658b2be35e6e9d38 Mon Sep 17 00:00:00 2001 From: rallytime Date: Thu, 9 Feb 2017 08:50:56 -0700 Subject: [PATCH 59/59] Pylint fix --- tests/unit/utils/schema_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit/utils/schema_test.py b/tests/unit/utils/schema_test.py index 8cbeae3df741..3bc42edb1412 100644 --- a/tests/unit/utils/schema_test.py +++ b/tests/unit/utils/schema_test.py @@ -9,8 +9,6 @@ import json import yaml -from distutils.version import LooseVersion - from distutils.version import LooseVersion as _LooseVersion # Import Salt Testing Libs