Add j2 template for enable pam_limit and limit SSH session (#10177)

#### Why I did it
    When too many user login concurrently and run commands, SONiC may kernel panic on some device which has very limited memory.

#### How I did it
    Add j2 template for setup pam_limit plugin for limit SSH session per-user.

#### How to verify it
    Manually validate the j2 template can generate correct config file.

#### Which release branch to backport (provide reason below if selected)

- [x] 201811
- [ ] 201911
- [ ] 202006
- [x] 202012
- [x] 202106
- [x] 202111

#### Description for the changelog
    Add j2 template for setup pam_limit plugin for limit SSH session per-user.

#### A picture of a cute animal (not mandatory but encouraged)
This commit is contained in:
Hua Liu 2022-03-31 17:33:43 +08:00 committed by GitHub
parent 6e51779efb
commit 271ef69e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 146 additions and 0 deletions

View File

@ -0,0 +1,69 @@
# /etc/security/limits.conf
#
# This file generate by j2 template file: src/sonic-host-services-data/templates/limits.conf.j2
#
# Each line describes a limit for a user in the form:
#
# <domain> <type> <item> <value>
#
# Where:
# <domain> can be:
# - a user name
# - a group name, with @group syntax
# - the wildcard *, for default entry
# - the wildcard %, can be also used with %group syntax,
# for maxlogin limit
# - NOTE: group and wildcard limits are not applied to root.
# To apply a limit to the root user, <domain> must be
# the literal username root.
#
# <type> can have the two values:
# - "soft" for enforcing the soft limits
# - "hard" for enforcing hard limits
#
# <item> can be one of the following:
# - core - limits the core file size (KB)
# - data - max data size (KB)
# - fsize - maximum filesize (KB)
# - memlock - max locked-in-memory address space (KB)
# - nofile - max number of open file descriptors
# - rss - max resident set size (KB)
# - stack - max stack size (KB)
# - cpu - max CPU time (MIN)
# - nproc - max number of processes
# - as - address space limit (KB)
# - maxlogins - max number of logins for this user
# - maxsyslogins - max number of logins on the system
# - priority - the priority to run user process with
# - locks - max number of file locks the user can hold
# - sigpending - max number of pending signals
# - msgqueue - max memory used by POSIX message queues (bytes)
# - nice - max nice priority allowed to raise to values: [-20, 19]
# - rtprio - max realtime priority
# - chroot - change root to directory (Debian-specific)
#
#
# <value> is related with <item>:
# All items support the values -1, unlimited or infinity indicating
# no limit, except for priority and nice.
#
# If a hard limit or soft limit of a resource is set to a valid value,
# but outside of the supported range of the local system, the system
# may reject the new limit or unexpected behavior may occur. If the
# control value required is used, the module will reject the login if
# a limit could not be set.
#
# <domain> <type> <item> <value>
#
# * soft core 0
# root hard core 100000
# * hard rss 10000
# @student hard nproc 20
# @faculty soft nproc 20
# @faculty hard nproc 50
# ftp hard nproc 0
# ftp - chroot /ftp
# @student - maxlogins 4
# End of file

View File

@ -0,0 +1,12 @@
#THIS IS AN AUTO-GENERATED FILE
#
# This file generate by j2 template file: src/sonic-host-services-data/templates/pam_limits.j2
#
# /etc/pam.d/pam-limits settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)

View File

@ -25,6 +25,10 @@ PAM_RADIUS_AUTH_CONF_TEMPLATE = "/usr/share/sonic/templates/pam_radius_auth.conf
NSS_CONF = "/etc/nsswitch.conf" NSS_CONF = "/etc/nsswitch.conf"
ETC_PAMD_SSHD = "/etc/pam.d/sshd" ETC_PAMD_SSHD = "/etc/pam.d/sshd"
ETC_PAMD_LOGIN = "/etc/pam.d/login" ETC_PAMD_LOGIN = "/etc/pam.d/login"
PAM_LIMITS_CONF_TEMPLATE = "/usr/share/sonic/templates/pam_limits.j2"
LIMITS_CONF_TEMPLATE = "/usr/share/sonic/templates/limits.conf.j2"
PAM_LIMITS_CONF = "/etc/pam.d/pam-limits-conf"
LIMITS_CONF = "/etc/security/limits.conf"
# TACACS+ # TACACS+
TACPLUS_SERVER_PASSKEY_DEFAULT = "" TACPLUS_SERVER_PASSKEY_DEFAULT = ""
@ -966,6 +970,64 @@ class NtpCfg(object):
syslog.syslog(syslog.LOG_INFO, 'ntp server update, restarting ntp-config, ntp servers configured {}'.format(self.ntp_servers)) syslog.syslog(syslog.LOG_INFO, 'ntp server update, restarting ntp-config, ntp servers configured {}'.format(self.ntp_servers))
run_cmd(cmd) run_cmd(cmd)
class PamLimitsCfg(object):
"""
PamLimit Config Daemon
1) The pam_limits PAM module sets limits on the system resources that can be obtained in a user-session.
2) Purpose of this daemon is to render pam_limits config file.
"""
def __init__(self, config_db):
self.config_db = config_db
self.hwsku = ""
self.type = ""
# Load config from ConfigDb and render config file/
def update_config_file(self):
device_metadata = self.config_db.get_table('DEVICE_METADATA')
if "localhost" not in device_metadata:
return
self.read_localhost_config(device_metadata["localhost"])
self.render_conf_file()
# Read localhost config
def read_localhost_config(self, localhost):
if "hwsku" in localhost:
self.hwsku = localhost["hwsku"]
else:
self.hwsku = ""
if "type" in localhost:
self.type = localhost["type"]
else:
self.type = ""
# Render pam_limits config files
def render_conf_file(self):
env = jinja2.Environment(loader=jinja2.FileSystemLoader('/'), trim_blocks=True)
env.filters['sub'] = sub
try:
template_file = os.path.abspath(PAM_LIMITS_CONF_TEMPLATE)
template = env.get_template(template_file)
pam_limits_conf = template.render(
hwsku=self.hwsku,
type=self.type)
with open(PAM_LIMITS_CONF, 'w') as f:
f.write(pam_limits_conf)
template_file = os.path.abspath(LIMITS_CONF_TEMPLATE)
template = env.get_template(template_file)
limits_conf = template.render(
hwsku=self.hwsku,
type=self.type)
with open(LIMITS_CONF, 'w') as f:
f.write(limits_conf)
except Exception as e:
syslog.syslog(syslog.LOG_ERR,
"modify pam_limits config file failed with exception: {}"
.format(e))
class HostConfigDaemon: class HostConfigDaemon:
def __init__(self): def __init__(self):
# Just a sanity check to verify if the CONFIG_DB has been initialized # Just a sanity check to verify if the CONFIG_DB has been initialized
@ -1007,6 +1069,9 @@ class HostConfigDaemon:
self.hostname_cache="" self.hostname_cache=""
self.aaacfg = AaaCfg() self.aaacfg = AaaCfg()
# Initialize PamLimitsCfg
self.pamLimitsCfg = PamLimitsCfg(self.config_db)
self.pamLimitsCfg.update_config_file()
def load(self): def load(self):
aaa = self.config_db.get_table('AAA') aaa = self.config_db.get_table('AAA')