From 80e7163b7bf512a45e2fa31494f3bdff9e9e2dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=BDidek?= Date: Wed, 4 Mar 2020 16:26:18 +0100 Subject: [PATCH 29/35] NSS: make memcache size configurable Added options to configure memcache size: memcache_size_passwd memcache_size_group memcache_size_initgroups Related: https://github.com/SSSD/sssd/issues/4578 Reviewed-by: Sumit Bose --- src/confdb/confdb.h | 3 + src/config/SSSDConfig/sssdoptions.py | 3 + src/config/cfg_rules.ini | 3 + src/man/sssd.conf.5.xml | 78 +++++++++ src/responder/nss/nsssrv.c | 104 ++++++++---- src/tests/intg/test_memory_cache.py | 236 +++++++++++++++++++++++++++ 6 files changed, 398 insertions(+), 29 deletions(-) diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index a5d35fd70..c96896da5 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -115,6 +115,9 @@ #define CONFDB_NSS_SHELL_FALLBACK "shell_fallback" #define CONFDB_NSS_DEFAULT_SHELL "default_shell" #define CONFDB_MEMCACHE_TIMEOUT "memcache_timeout" +#define CONFDB_NSS_MEMCACHE_SIZE_PASSWD "memcache_size_passwd" +#define CONFDB_NSS_MEMCACHE_SIZE_GROUP "memcache_size_group" +#define CONFDB_NSS_MEMCACHE_SIZE_INITGROUPS "memcache_size_initgroups" #define CONFDB_NSS_HOMEDIR_SUBSTRING "homedir_substring" #define CONFDB_DEFAULT_HOMEDIR_SUBSTRING "/home" diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py index 9c071f70a..16d85cfa3 100644 --- a/src/config/SSSDConfig/sssdoptions.py +++ b/src/config/SSSDConfig/sssdoptions.py @@ -72,6 +72,9 @@ class SSSDOptions(object): 'shell_fallback': _('If a shell stored in central directory is allowed but not available, use this fallback'), 'default_shell': _('Shell to use if the provider does not list one'), 'memcache_timeout': _('How long will be in-memory cache records valid'), + 'memcache_size_passwd': _('Number of slots in fast in-memory cache for passwd requests'), + 'memcache_size_group': _('Number of slots in fast in-memory cache for group requests'), + 'memcache_size_initgroups': _('Number of slots in fast in-memory cache for initgroups requests'), 'homedir_substring': _('The value of this option will be used in the expansion of the override_homedir option ' 'if the template contains the format string %H.'), 'get_domains_timeout': _('Specifies time in seconds for which the list of subdomains will be considered ' diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini index 1a7e2c5cd..2874ea048 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -92,6 +92,9 @@ option = shell_fallback option = default_shell option = get_domains_timeout option = memcache_timeout +option = memcache_size_passwd +option = memcache_size_group +option = memcache_size_initgroups [rule/allowed_pam_options] validator = ini_allowed_options diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index 9a9679a4b..9bc2e26e5 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -1100,6 +1100,84 @@ fallback_homedir = /home/%u + + memcache_size_passwd (integer) + + + Number of slots allocated inside fast in-memory + cache for passwd requests. Note that one entry + in fast in-memory cache can occupy more than one slot. + Setting the size to 0 will disable the passwd in-memory + cache. + + + Default: 200000 + + + WARNING: Disabled or too small in-memory cache can + have significant negative impact on SSSD's + performance. + + + NOTE: If the environment variable + SSS_NSS_USE_MEMCACHE is set to "NO", client + applications will not use the fast in-memory + cache. + + + + + memcache_size_group (integer) + + + Number of slots allocated inside fast in-memory + cache for group requests. Note that one entry + in fast in-memory cache can occupy more than one + slot. Setting the size to 0 will disable the group + in-memory cache. + + + Default: 150000 + + + WARNING: Disabled or too small in-memory cache can + have significant negative impact on SSSD's + performance. + + + NOTE: If the environment variable + SSS_NSS_USE_MEMCACHE is set to "NO", client + applications will not use the fast in-memory + cache. + + + + + memcache_size_initgroups (integer) + + + Number of slots allocated inside fast in-memory + cache for initgroups requests. Note that one entry + in fast in-memory cache can occupy more than one + slot. Setting the size to 0 will disable the + initgroups in-memory cache. + + + Default: 250000 + + + WARNING: Disabled or too small in-memory cache can + have significant negative impact on SSSD's + performance. + + + NOTE: If the environment variable + SSS_NSS_USE_MEMCACHE is set to "NO", client + applications will not use the fast in-memory + cache. + + + user_attributes (string) diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c index 21d93ae77..0a201d3ae 100644 --- a/src/responder/nss/nsssrv.c +++ b/src/responder/nss/nsssrv.c @@ -209,13 +209,16 @@ done: static int setup_memcaches(struct nss_ctx *nctx) { - /* TODO: read cache sizes from configuration */ + /* Default memcache sizes */ static const size_t SSS_MC_CACHE_PASSWD_SLOTS = 200000; /* 8mb */ static const size_t SSS_MC_CACHE_GROUP_SLOTS = 150000; /* 6mb */ static const size_t SSS_MC_CACHE_INITGROUP_SLOTS = 250000; /* 10mb */ int ret; int memcache_timeout; + int mc_size_passwd; + int mc_size_group; + int mc_size_initgroups; /* Remove the CLEAR_MC_FLAG file if exists. */ ret = unlink(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG); @@ -243,34 +246,77 @@ static int setup_memcaches(struct nss_ctx *nctx) return EOK; } - ret = sss_mmap_cache_init(nctx, "passwd", - nctx->mc_uid, nctx->mc_gid, - SSS_MC_PASSWD, - SSS_MC_CACHE_PASSWD_SLOTS, - (time_t)memcache_timeout, - &nctx->pwd_mc_ctx); - if (ret) { - DEBUG(SSSDBG_CRIT_FAILURE, "passwd mmap cache is DISABLED\n"); - } - - ret = sss_mmap_cache_init(nctx, "group", - nctx->mc_uid, nctx->mc_gid, - SSS_MC_GROUP, - SSS_MC_CACHE_GROUP_SLOTS, - (time_t)memcache_timeout, - &nctx->grp_mc_ctx); - if (ret) { - DEBUG(SSSDBG_CRIT_FAILURE, "group mmap cache is DISABLED\n"); - } - - ret = sss_mmap_cache_init(nctx, "initgroups", - nctx->mc_uid, nctx->mc_gid, - SSS_MC_INITGROUPS, - SSS_MC_CACHE_INITGROUP_SLOTS, - (time_t)memcache_timeout, - &nctx->initgr_mc_ctx); - if (ret) { - DEBUG(SSSDBG_CRIT_FAILURE, "initgroups mmap cache is DISABLED\n"); + /* Get all memcache sizes from confdb (pwd, grp, initgr) */ + + ret = confdb_get_int(nctx->rctx->cdb, + CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_MEMCACHE_SIZE_PASSWD, + SSS_MC_CACHE_PASSWD_SLOTS, + &mc_size_passwd); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to get 'memcache_size_passwd' option from confdb.\n"); + return ret; + } + + ret = confdb_get_int(nctx->rctx->cdb, + CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_MEMCACHE_SIZE_GROUP, + SSS_MC_CACHE_GROUP_SLOTS, + &mc_size_group); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to get 'memcache_size_group' option from confdb.\n"); + return ret; + } + + ret = confdb_get_int(nctx->rctx->cdb, + CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_MEMCACHE_SIZE_INITGROUPS, + SSS_MC_CACHE_INITGROUP_SLOTS, + &mc_size_initgroups); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to get 'memcache_size_nitgroups' option from confdb.\n"); + return ret; + } + + /* Initialize the fast in-memory caches if they were not disabled */ + + if (mc_size_passwd != 0) { + ret = sss_mmap_cache_init(nctx, "passwd", + nctx->mc_uid, nctx->mc_gid, + SSS_MC_PASSWD, + mc_size_passwd, + (time_t)memcache_timeout, + &nctx->pwd_mc_ctx); + if (ret) { + DEBUG(SSSDBG_CRIT_FAILURE, "passwd mmap cache is DISABLED\n"); + } + } + + if (mc_size_group != 0) { + ret = sss_mmap_cache_init(nctx, "group", + nctx->mc_uid, nctx->mc_gid, + SSS_MC_GROUP, + mc_size_group, + (time_t)memcache_timeout, + &nctx->grp_mc_ctx); + if (ret) { + DEBUG(SSSDBG_CRIT_FAILURE, "group mmap cache is DISABLED\n"); + } + } + + if (mc_size_initgroups != 0) { + ret = sss_mmap_cache_init(nctx, "initgroups", + nctx->mc_uid, nctx->mc_gid, + SSS_MC_INITGROUPS, + mc_size_initgroups, + (time_t)memcache_timeout, + &nctx->initgr_mc_ctx); + if (ret) { + DEBUG(SSSDBG_CRIT_FAILURE, "initgroups mmap cache is DISABLED\n"); + } } return EOK; diff --git a/src/tests/intg/test_memory_cache.py b/src/tests/intg/test_memory_cache.py index 322f76fe0..6ed696e00 100644 --- a/src/tests/intg/test_memory_cache.py +++ b/src/tests/intg/test_memory_cache.py @@ -135,6 +135,112 @@ def load_data_to_ldap(request, ldap_conn): create_ldap_fixture(request, ldap_conn, ent_list) +@pytest.fixture +def disable_memcache_rfc2307(request, ldap_conn): + load_data_to_ldap(request, ldap_conn) + + conf = unindent("""\ + [sssd] + domains = LDAP + services = nss + + [nss] + memcache_size_group = 0 + memcache_size_passwd = 0 + memcache_size_initgroups = 0 + + [domain/LDAP] + ldap_auth_disable_tls_never_use_in_production = true + ldap_schema = rfc2307 + id_provider = ldap + auth_provider = ldap + sudo_provider = ldap + ldap_uri = {ldap_conn.ds_inst.ldap_url} + ldap_search_base = {ldap_conn.ds_inst.base_dn} + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + return None + + +@pytest.fixture +def disable_pwd_mc_rfc2307(request, ldap_conn): + load_data_to_ldap(request, ldap_conn) + + conf = unindent("""\ + [sssd] + domains = LDAP + services = nss + + [nss] + memcache_size_passwd = 0 + + [domain/LDAP] + ldap_auth_disable_tls_never_use_in_production = true + ldap_schema = rfc2307 + id_provider = ldap + auth_provider = ldap + sudo_provider = ldap + ldap_uri = {ldap_conn.ds_inst.ldap_url} + ldap_search_base = {ldap_conn.ds_inst.base_dn} + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + return None + + +@pytest.fixture +def disable_grp_mc_rfc2307(request, ldap_conn): + load_data_to_ldap(request, ldap_conn) + + conf = unindent("""\ + [sssd] + domains = LDAP + services = nss + + [nss] + memcache_size_group = 0 + + [domain/LDAP] + ldap_auth_disable_tls_never_use_in_production = true + ldap_schema = rfc2307 + id_provider = ldap + auth_provider = ldap + sudo_provider = ldap + ldap_uri = {ldap_conn.ds_inst.ldap_url} + ldap_search_base = {ldap_conn.ds_inst.base_dn} + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + return None + + +@pytest.fixture +def disable_initgr_mc_rfc2307(request, ldap_conn): + load_data_to_ldap(request, ldap_conn) + + conf = unindent("""\ + [sssd] + domains = LDAP + services = nss + + [nss] + memcache_size_initgroups = 0 + + [domain/LDAP] + ldap_auth_disable_tls_never_use_in_production = true + ldap_schema = rfc2307 + id_provider = ldap + auth_provider = ldap + sudo_provider = ldap + ldap_uri = {ldap_conn.ds_inst.ldap_url} + ldap_search_base = {ldap_conn.ds_inst.base_dn} + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + return None + + @pytest.fixture def sanity_rfc2307(request, ldap_conn): load_data_to_ldap(request, ldap_conn) @@ -354,6 +460,19 @@ def test_getgrnam_simple_with_mc(ldap_conn, sanity_rfc2307): test_getgrnam_simple(ldap_conn, sanity_rfc2307) +def test_getgrnam_simple_disabled_pwd_mc(ldap_conn, disable_pwd_mc_rfc2307): + test_getgrnam_simple(ldap_conn, disable_pwd_mc_rfc2307) + stop_sssd() + test_getgrnam_simple(ldap_conn, disable_pwd_mc_rfc2307) + + +def test_getgrnam_simple_disabled_intitgr_mc(ldap_conn, + disable_initgr_mc_rfc2307): + test_getgrnam_simple(ldap_conn, disable_initgr_mc_rfc2307) + stop_sssd() + test_getgrnam_simple(ldap_conn, disable_initgr_mc_rfc2307) + + def test_getgrnam_membership(ldap_conn, sanity_rfc2307): ent.assert_group_by_name( "group1", @@ -919,3 +1038,120 @@ def test_mc_zero_timeout(ldap_conn, zero_timeout_rfc2307): grp.getgrnam('group1') with pytest.raises(KeyError): grp.getgrgid(2001) + + +def test_disabled_mc(ldap_conn, disable_memcache_rfc2307): + ent.assert_passwd_by_name( + 'user1', + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + ent.assert_passwd_by_uid( + 1001, + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + + ent.assert_group_by_name("group1", dict(name="group1", gid=2001)) + ent.assert_group_by_gid(2001, dict(name="group1", gid=2001)) + + assert_user_gids_equal('user1', [2000, 2001]) + + stop_sssd() + + # sssd is stopped and the memory cache is disabled; + # so pytest should not be able to find anything + with pytest.raises(KeyError): + pwd.getpwnam('user1') + with pytest.raises(KeyError): + pwd.getpwuid(1001) + + with pytest.raises(KeyError): + grp.getgrnam('group1') + with pytest.raises(KeyError): + grp.getgrgid(2001) + + with pytest.raises(KeyError): + (res, errno, gids) = sssd_id.get_user_gids('user1') + + +def test_disabled_passwd_mc(ldap_conn, disable_pwd_mc_rfc2307): + ent.assert_passwd_by_name( + 'user1', + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + ent.assert_passwd_by_uid( + 1001, + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + + assert_user_gids_equal('user1', [2000, 2001]) + + stop_sssd() + + # passwd cache is disabled + with pytest.raises(KeyError): + pwd.getpwnam('user1') + with pytest.raises(KeyError): + pwd.getpwuid(1001) + + # Initgroups looks up the user first, hence KeyError from the + # passwd database even if the initgroups cache is active. + with pytest.raises(KeyError): + (res, errno, gids) = sssd_id.get_user_gids('user1') + + +def test_disabled_group_mc(ldap_conn, disable_grp_mc_rfc2307): + ent.assert_passwd_by_name( + 'user1', + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + ent.assert_passwd_by_uid( + 1001, + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + + ent.assert_group_by_name("group1", dict(name="group1", gid=2001)) + ent.assert_group_by_gid(2001, dict(name="group1", gid=2001)) + + assert_user_gids_equal('user1', [2000, 2001]) + + stop_sssd() + + # group cache is disabled, other caches should work + ent.assert_passwd_by_name( + 'user1', + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + ent.assert_passwd_by_uid( + 1001, + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + + with pytest.raises(KeyError): + grp.getgrnam('group1') + with pytest.raises(KeyError): + grp.getgrgid(2001) + + assert_user_gids_equal('user1', [2000, 2001]) + + +def test_disabled_initgr_mc(ldap_conn, disable_initgr_mc_rfc2307): + # Even if initgroups is disabled, passwd should work + ent.assert_passwd_by_name( + 'user1', + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + ent.assert_passwd_by_uid( + 1001, + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + + stop_sssd() + + ent.assert_passwd_by_name( + 'user1', + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) + ent.assert_passwd_by_uid( + 1001, + dict(name='user1', passwd='*', uid=1001, gid=2001, + gecos='1001', shell='/bin/bash')) -- 2.21.3