From 306b24ee013292d8dba08a9e090506938a7bbc4c Mon Sep 17 00:00:00 2001 From: Andrew Walker Date: Sat, 14 Dec 2024 21:41:01 -0600 Subject: [PATCH] Adjust SMB server configuration for GPOS STIG Force client to use kerberos for authentication and disable NTLM authentication server-side. --- src/middlewared/middlewared/plugins/smb.py | 10 +++- .../middlewared/plugins/smb_/util_smbconf.py | 11 +++- tests/unit/test_smb_service.py | 60 +++++++++++-------- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/middlewared/middlewared/plugins/smb.py b/src/middlewared/middlewared/plugins/smb.py index b255dec249834..95cf896c5e4b1 100644 --- a/src/middlewared/middlewared/plugins/smb.py +++ b/src/middlewared/middlewared/plugins/smb.py @@ -162,9 +162,17 @@ def generate_smb_configuration(self): smb_shares = self.middleware.call_sync('sharing.smb.query') bind_ip_choices = self.middleware.call_sync('smb.bindip_choices') is_enterprise = self.middleware.call_sync('system.is_enterprise') + security_config = self.middleware.call_sync('system.security.config') return generate_smb_conf_dict( - ds_type, ds_config, smb_config, smb_shares, bind_ip_choices, idmap_config, is_enterprise + ds_type, + ds_config, + smb_config, + smb_shares, + bind_ip_choices, + idmap_config, + is_enterprise, + security_config, ) @api_method(SmbServiceBindIPChoicesArgs, SmbServiceBindIPChoicesResult) diff --git a/src/middlewared/middlewared/plugins/smb_/util_smbconf.py b/src/middlewared/middlewared/plugins/smb_/util_smbconf.py index 73747db0fc427..d000c36008936 100644 --- a/src/middlewared/middlewared/plugins/smb_/util_smbconf.py +++ b/src/middlewared/middlewared/plugins/smb_/util_smbconf.py @@ -266,7 +266,8 @@ def generate_smb_conf_dict( smb_shares: list, smb_bind_choices: dict, idmap_settings: list, - is_enterprise: bool = False + is_enterprise: bool, + security_config: dict[str, bool] ): guest_enabled = any(filter_list(smb_shares, [['guestok', '=', True]])) fsrvp_enabled = any(filter_list(smb_shares, [['fsrvp', '=', True]])) @@ -557,6 +558,14 @@ def generate_smb_conf_dict( param, value = entry.split('=', 1) smbconf[param.strip()] = value.strip() + # Ordering here is relevant. Do not permit smb_options to override required + # settings for the STIG. + if security_config['enable_gpos_stig']: + smbconf.update({ + 'client use kerberos': 'required', + 'ntlm auth': 'disabled' + }) + # The following parameters must come after processing includes in order to # prevent auxiliary parameters from overriding them smbconf.update({ diff --git a/tests/unit/test_smb_service.py b/tests/unit/test_smb_service.py index 655b78bae3d4b..53dd4480719cc 100644 --- a/tests/unit/test_smb_service.py +++ b/tests/unit/test_smb_service.py @@ -38,6 +38,8 @@ SMB_ENCRYPTION_NEGOTIATE = BASE_SMB_CONFIG | {'encryption': 'NEGOTIATE'} SMB_ENCRYPTION_DESIRED = BASE_SMB_CONFIG | {'encryption': 'DESIRED'} SMB_ENCRYPTION_REQUIRED = BASE_SMB_CONFIG | {'encryption': 'REQUIRED'} +SYSTEM_SECURITY_DEFAULT = {'id': 1, 'enable_fips': False, 'enable_gpos_stig': False} +SYSTEM_SECURITY_GPOS_STIG = {'id': 1, 'enable_fips': True, 'enable_gpos_stig': True} BASE_SMB_SHARE = { @@ -213,7 +215,7 @@ def test__base_smb(): conf = generate_smb_conf_dict( None, None, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['netbios name'] == 'TESTSERVER' assert conf['netbios aliases'] == 'BOB LARRY' @@ -238,7 +240,7 @@ def test__base_smb(): def test__base_smb_enterprise(): conf = generate_smb_conf_dict( None, None, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP, True + BIND_IP_CHOICES, BASE_IDMAP, True, SYSTEM_SECURITY_DEFAULT ) assert conf['zfs_core:zfs_integrity_streams'] is True assert conf['zfs_core:zfs_block_cloning'] is True @@ -247,7 +249,7 @@ def test__base_smb_enterprise(): def test__syslog(): conf = generate_smb_conf_dict( None, None, SMB_SYSLOG, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['logging'] == ('syslog@1 file') @@ -255,7 +257,7 @@ def test__syslog(): def test__localmaster(): conf = generate_smb_conf_dict( None, None, SMB_LOCALMASTER, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['local master'] is True @@ -263,7 +265,7 @@ def test__localmaster(): def test__guestaccount(): conf = generate_smb_conf_dict( None, None, SMB_GUEST, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['guest account'] == 'mike' @@ -271,7 +273,7 @@ def test__guestaccount(): def test__bindip(): conf = generate_smb_conf_dict( None, None, SMB_BINDIP, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert set(conf['interfaces'].split(' ')) == set(['192.168.0.250', '127.0.0.1']) @@ -279,7 +281,7 @@ def test__bindip(): def test__ntlmv1auth(): conf = generate_smb_conf_dict( None, None, SMB_NTLMV1, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['ntlm auth'] is True @@ -287,7 +289,7 @@ def test__ntlmv1auth(): def test__smb1_enable(): conf = generate_smb_conf_dict( None, None, SMB_SMB1, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['server min protocol'] == 'NT1' @@ -295,7 +297,7 @@ def test__smb1_enable(): def test__smb_options(): conf = generate_smb_conf_dict( None, None, SMB_OPTIONS, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['canary'] == 'bob' assert conf['canary2'] == 'bob2' @@ -304,7 +306,7 @@ def test__smb_options(): def test__multichannel(): conf = generate_smb_conf_dict( None, None, SMB_MULTICHANNEL, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['server multichannel support'] is True @@ -312,7 +314,7 @@ def test__multichannel(): def test__homes_share(): conf = generate_smb_conf_dict( None, None, BASE_SMB_CONFIG, [HOMES_SHARE], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert 'obey pam restrictions' in conf assert conf['obey pam restrictions'] is True @@ -321,7 +323,7 @@ def test__homes_share(): def test__guest_share(): conf = generate_smb_conf_dict( None, None, BASE_SMB_CONFIG, [GUEST_SHARE], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['restrict anonymous'] == 0 @@ -329,7 +331,7 @@ def test__guest_share(): def test__fsrvp_share(): conf = generate_smb_conf_dict( None, None, BASE_SMB_CONFIG, [FSRVP_SHARE], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['rpc_daemon:fssd'] == 'fork' assert conf['fss:prune stale'] is True @@ -339,7 +341,7 @@ def test__ad_base(): conf = generate_smb_conf_dict( DSType.AD, BASE_AD_CONFIG, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['realm'] == 'TESTDOMAIN.IXSYSTEMS.COM' assert conf['winbind use default domain'] is False @@ -359,7 +361,7 @@ def test__ad_homes_share(): conf = generate_smb_conf_dict( DSType.AD, BASE_AD_CONFIG, BASE_SMB_CONFIG, [HOMES_SHARE], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert 'obey pam restrictions' in conf assert conf['obey pam restrictions'] is True @@ -372,7 +374,7 @@ def test__ad_enumeration(): conf = generate_smb_conf_dict( DSType.AD, DISABLE_ENUM, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['winbind enum users'] is False assert conf['winbind enum groups'] is False @@ -382,7 +384,7 @@ def test__ad_trusted_doms(): conf = generate_smb_conf_dict( DSType.AD, TRUSTED_DOMS, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['allow trusted domains'] is True @@ -391,7 +393,7 @@ def test__ad_default_domain(): conf = generate_smb_conf_dict( DSType.AD, USE_DEFAULT_DOM, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['winbind use default domain'] is True @@ -400,7 +402,7 @@ def test__ad_additional_domain(): conf = generate_smb_conf_dict( DSType.AD, TRUSTED_DOMS, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP + [ADDITIONAL_DOMAIN] + BIND_IP_CHOICES, BASE_IDMAP + [ADDITIONAL_DOMAIN], False, SYSTEM_SECURITY_DEFAULT ) assert conf['idmap config BOBDOM : backend'] == 'rid' assert conf['idmap config BOBDOM : range'] == '200000001 - 300000000' @@ -410,7 +412,8 @@ def test__ad_autorid(): conf = generate_smb_conf_dict( DSType.AD, BASE_AD_CONFIG, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, [AUTORID_DOMAIN, BASE_IDMAP[1], BASE_IDMAP[2]] + BIND_IP_CHOICES, [AUTORID_DOMAIN, BASE_IDMAP[1], BASE_IDMAP[2]], + False, SYSTEM_SECURITY_DEFAULT ) assert conf['idmap config * : backend'] == 'autorid' assert conf['idmap config * : range'] == '10000 - 200000000' @@ -419,7 +422,7 @@ def test__ad_autorid(): def test__encryption_negotiate(): conf = generate_smb_conf_dict( None, None, SMB_ENCRYPTION_NEGOTIATE, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['server smb encrypt'] == 'if_required' @@ -427,7 +430,7 @@ def test__encryption_negotiate(): def test__encryption_desired(): conf = generate_smb_conf_dict( None, None, SMB_ENCRYPTION_DESIRED, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['server smb encrypt'] == 'desired' @@ -435,7 +438,7 @@ def test__encryption_desired(): def test__encryption_required(): conf = generate_smb_conf_dict( None, None, SMB_ENCRYPTION_REQUIRED, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['server smb encrypt'] == 'required' @@ -444,7 +447,7 @@ def test__ipa_base(): conf = generate_smb_conf_dict( DSType.IPA, BASE_IPA_CONFIG, BASE_SMB_CONFIG, [], - BIND_IP_CHOICES, BASE_IDMAP + BIND_IP_CHOICES, BASE_IDMAP, False, SYSTEM_SECURITY_DEFAULT ) assert conf['workgroup'] == 'TN' assert conf['server role'] == 'member server' @@ -453,3 +456,12 @@ def test__ipa_base(): assert conf['realm'] == 'TESTDOM.TEST' assert conf['idmap config TN : backend'] == 'sss' assert conf['idmap config TN : range'] == '925000000 - 925199999' + + +def test__enable_stig(): + conf = generate_smb_conf_dict( + None, None, BASE_SMB_CONFIG, [], + BIND_IP_CHOICES, BASE_IDMAP, True, SYSTEM_SECURITY_GPOS_STIG + ) + assert conf['client use kerberos'] == 'required' + assert conf['ntlm auth'] == 'disabled'