diff --git a/build_debian.sh b/build_debian.sh index 78f40cc20c..24552cfe44 100755 --- a/build_debian.sh +++ b/build_debian.sh @@ -246,9 +246,6 @@ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y do sudo mv $FILESYSTEM_ROOT/grub-pc-bin*.deb $FILESYSTEM_ROOT/$PLATFORM_DIR/x86_64-grub -sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libwrap0_*.deb || \ - sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f - ## Disable kexec supported reboot which was installed by default sudo sed -i 's/LOAD_KEXEC=true/LOAD_KEXEC=false/' $FILESYSTEM_ROOT/etc/default/kexec diff --git a/dockers/docker-snmp-sv2/Dockerfile.j2 b/dockers/docker-snmp-sv2/Dockerfile.j2 index 02d0679f8f..f8deee49d3 100644 --- a/dockers/docker-snmp-sv2/Dockerfile.j2 +++ b/dockers/docker-snmp-sv2/Dockerfile.j2 @@ -58,8 +58,6 @@ RUN rm -rf /debs /python-wheels ~/.cache COPY ["start.sh", "/usr/bin/"] COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] COPY ["*.j2", "/usr/share/sonic/templates/"] -COPY ["snmpd-config-updater", "/usr/bin/snmpd-config-updater"] -RUN chmod +x /usr/bin/snmpd-config-updater # Although exposing ports is not needed for host net mode, keep it for possible bridge mode EXPOSE 161/udp 162/udp diff --git a/dockers/docker-snmp-sv2/snmpd-config-updater b/dockers/docker-snmp-sv2/snmpd-config-updater deleted file mode 100755 index 1970245149..0000000000 --- a/dockers/docker-snmp-sv2/snmpd-config-updater +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python - -# Daemon that listens to updates from ConfigDB about the source IP prefixes from which -# SNMP connections are allowed. In case of change, it will update the SNMP configuration -# file accordingly. After a change, it will notify snmpd to re-read its config file -# via SIGHUP. -# -# This daemon is meant to be run on Arista platforms only. Service ACLs on all other -# platforms will be managed by caclmgrd. -# - -import os -import re -import signal -import subprocess -import sys -import syslog -import time -from swsssdk import ConfigDBConnector - -VERSION = "1.0" - -SYSLOG_IDENTIFIER = "snmpd-config-updater" - - -# ============================== Classes ============================== - -class Process(object): - def __init__(self, pid): - self.pid = pid - self.path = '/proc/%d/status' % pid - self.status = None - - def read_proc_status(self): - data = {} - with open(self.path) as f: - for line in f.readlines(): - key, value = line.split(':', 1) - data[ key ] = value.strip() - self.status = data - - def get_proc_signals(self): - assert self.status - sigBlk = int(self.status[ 'SigBlk' ], 16) - sigIgn = int(self.status[ 'SigIgn' ], 16) - sigCgt = int(self.status[ 'SigCgt' ], 16) - return (sigBlk, sigIgn, sigCgt) - - def handle_signal(self, sig): - sigBlk, sigIgn, sigCgt = self.get_proc_signals() - mask = 1 << ( sig - 1 ) - if mask & sigBlk: - return True - if mask & sigIgn: - return True - if mask & sigCgt: - return True - return False - - def send_signal(self, sig): - log_info('Sending signal %s to %d' % (sig, self.pid)) - os.kill(self.pid, sig) - - def safe_send_signal(self, sig): - self.read_proc_status() - if not self.handle_signal(sig): - return False - self.send_signal(sig) - return True - - def wait_send_signal(self, sig, interval=0.1): - while not self.safe_send_signal(sig): - log_info('Process %s has not yet registered %s' % (self.pid, sig)) - time.sleep(interval) - - @staticmethod - def by_name(name): - try: - pid = subprocess.check_output([ 'pidof', '-s', name ]) - except subprocess.CalledProcessError: - return None - return Process(int(pid.rstrip())) - -class ConfigUpdater(object): - SERVICE = "snmpd" - CONFIG_FILE_PATH = "/etc/snmp" - - ACL_TABLE = "ACL_TABLE" - ACL_RULE = "ACL_RULE" - - ACL_TABLE_TYPE_CTRLPLANE = "CTRLPLANE" - - ACL_SERVICE_SNMP = "SNMP" - - def get_src_ip_allow_list(self): - src_ip_allow_list = [] - - # Get current ACL tables and rules from Config DB - tables_db_info = self.config_db.get_table(self.ACL_TABLE) - rules_db_info = self.config_db.get_table(self.ACL_RULE) - - # Walk the ACL tables - for (table_name, table_data) in tables_db_info.iteritems(): - # Ignore non-control-plane ACL tables - if table_data["type"] != self.ACL_TABLE_TYPE_CTRLPLANE: - continue - - # Ignore non-SNMP service ACLs - if self.ACL_SERVICE_SNMP not in table_data["services"]: - continue - - acl_rules = {} - - for ((rule_table_name, rule_id), rule_props) in rules_db_info.iteritems(): - if rule_table_name == table_name: - acl_rules[rule_props["PRIORITY"]] = rule_props - - # For each ACL rule in this table (in descending order of priority) - for priority in sorted(acl_rules.iterkeys(), reverse=True): - rule_props = acl_rules[priority] - - if "PACKET_ACTION" not in rule_props: - log_error("ACL rule does not contain PACKET_ACTION property") - continue - - # We're only interested in ACCEPT rules - if rule_props["PACKET_ACTION"] != "ACCEPT": - continue - - if "SRC_IP" in rule_props and rule_props["SRC_IP"]: - src_ip_allow_list.append(rule_props["SRC_IP"]) - - return src_ip_allow_list - - # To update the configuration file - # - # Example config file for reference: - # root@sonic:/# cat /etc/snmp/snmpd.conf - # <...some snmp config, like udp port to use etc...> - # rocommunity public 172.20.61.0/24 - # rocommunity public 172.20.60.0/24 - # rocommunity public 127.00.00.0/8 - # <...some more snmp config...> - # root@sonic:/# - # - # snmpd.conf supports include file, like so: - # includeFile /etc/snmp/community.conf - # includeDir /etc/snmp/config.d - # which could make file massaging simpler, but even then we still deal with lines - # that have shared "masters", since some other entity controls the community strings - # part of that line. - # If other database attributes need to be written to the snmp config file, then - # it should be done by this daemon as well (sure, we could inotify on the file - # and correct it back, but that's glitchy). - # - # src_ip_allow_list may contain individual IP addresses or blocks of - # IP addresses using CIDR notation. - def write_configuration_file(self, src_ip_allow_list): - filename = "%s/%s.conf" % (self.CONFIG_FILE_PATH, self.SERVICE) - filename_tmp = filename + ".tmp" - - f = open(filename, "r") - snmpd_config = f.read() - f.close() - - f = open(filename_tmp, "w") - this_community = "not_a_community" - for line in snmpd_config.split('\n'): - m = re.match("^(..)community (\S+)", line) - if not m: - f.write(line) - f.write("\n") - else: - if not line.startswith(this_community): # already handled community (each community is duplicated per allow entry) - this_community = "%scommunity %s" % (m.group(1), m.group(2)) - if len(src_ip_allow_list): - for value in src_ip_allow_list: - f.write("%s %s\n" % (this_community, value)) - else: - f.write("%s\n" % this_community) - f.close() - - os.rename(filename_tmp, filename) - - # Force snmpd process to reload its configuration if it is running - proc = Process.by_name(self.SERVICE) - if proc: - proc.wait_send_signal(signal.SIGHUP) - - def notification_handler(self, key, data): - log_info("ACL configuration changed. Updating {} config accordingly...".format(self.SERVICE)) - self.write_configuration_file(self.get_src_ip_allow_list()) - - def run(self): - # Open a handle to the Config database - self.config_db = ConfigDBConnector() - self.config_db.connect() - - # Write initial configuration - self.write_configuration_file(self.get_src_ip_allow_list()) - - # Subscribe to notifications when ACL tables or rules change - self.config_db.subscribe(self.ACL_TABLE, - lambda table, key, data: self.notification_handler(key, data)) - self.config_db.subscribe(self.ACL_RULE, - lambda table, key, data: self.notification_handler(key, data)) - - # Indefinitely listen for Config DB notifications - self.config_db.listen() - - -# ========================== Syslog wrappers ========================== - -def log_info(msg): - syslog.openlog(SYSLOG_IDENTIFIER) - syslog.syslog(syslog.LOG_INFO, msg) - syslog.closelog() - - -def log_warning(msg): - syslog.openlog(SYSLOG_IDENTIFIER) - syslog.syslog(syslog.LOG_WARNING, msg) - syslog.closelog() - - -def log_error(msg): - syslog.openlog(SYSLOG_IDENTIFIER) - syslog.syslog(syslog.LOG_ERR, msg) - syslog.closelog() - - -# Determine whether we are running on an Arista platform -def is_platform_arista(): - proc = subprocess.Popen(["sonic-cfggen", "-H", "-v", "DEVICE_METADATA.localhost.platform"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - (stdout, stderr) = proc.communicate() - - if proc.returncode != 0: - log_error("Failed to retrieve platform string") - return False - - return "arista" in stdout - - -def main(): - log_info("Starting up...") - - if not os.geteuid() == 0: - log_error("Must be root to run this daemon") - print "Error: Must be root to run this daemon" - sys.exit(1) - - if not is_platform_arista(): - log_info("Platform is not an Arista platform. Exiting...") - sys.exit(0) - - # Instantiate a ConfigUpdater object - config_updater = ConfigUpdater() - config_updater.run() - -if __name__ == "__main__": - main() diff --git a/dockers/docker-snmp-sv2/start.sh b/dockers/docker-snmp-sv2/start.sh index 5ac31c5b43..6ec3379df5 100755 --- a/dockers/docker-snmp-sv2/start.sh +++ b/dockers/docker-snmp-sv2/start.sh @@ -12,6 +12,5 @@ echo "# Config files managed by sonic-config-engine" > /var/sonic/config_status rm -f /var/run/rsyslogd.pid supervisorctl start rsyslogd -supervisorctl start snmpd-config-updater supervisorctl start snmpd supervisorctl start snmp-subagent diff --git a/dockers/docker-snmp-sv2/supervisord.conf b/dockers/docker-snmp-sv2/supervisord.conf index b3db3be95a..d805795061 100644 --- a/dockers/docker-snmp-sv2/supervisord.conf +++ b/dockers/docker-snmp-sv2/supervisord.conf @@ -19,18 +19,9 @@ autorestart=false stdout_logfile=syslog stderr_logfile=syslog -[program:snmpd-config-updater] -command=/usr/bin/snmpd-config-updater -priority=3 -autostart=false -autorestart=unexpected -startsecs=0 -stdout_logfile=syslog -stderr_logfile=syslog - [program:snmpd] command=/usr/sbin/snmpd -f -LS4d -u Debian-snmp -g Debian-snmp -I -smux,mteTrigger,mteTriggerConf,ifTable,ifXTable,inetCidrRouteTable,ipCidrRouteTable,ip,disk_hw -p /run/snmpd.pid -priority=4 +priority=3 autostart=false autorestart=false stdout_logfile=syslog @@ -38,7 +29,7 @@ stderr_logfile=syslog [program:snmp-subagent] command=/usr/bin/env python3.6 -m sonic_ax_impl -priority=5 +priority=4 autostart=false autorestart=false stdout_logfile=syslog diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index e815e136fc..f04a214ad7 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -206,7 +206,6 @@ sudo cp $IMAGE_CONFIGS/sudoers/sudoers.lecture $FILESYSTEM_ROOT/etc/ # Copy control plane ACL management daemon files sudo cp $IMAGE_CONFIGS/caclmgrd/caclmgrd.service $FILESYSTEM_ROOT/etc/systemd/system/ sudo LANG=C chroot $FILESYSTEM_ROOT systemctl enable caclmgrd.service -sudo cp $IMAGE_CONFIGS/caclmgrd/caclmgrd-start.sh $FILESYSTEM_ROOT/usr/bin/ sudo cp $IMAGE_CONFIGS/caclmgrd/caclmgrd $FILESYSTEM_ROOT/usr/bin/ ## Install package without starting service @@ -259,21 +258,6 @@ if [ "$image_type" = "aboot" ]; then sudo sed -i 's/udevadm settle/udevadm settle -E \/sys\/class\/net\/eth0/' $FILESYSTEM_ROOT/etc/init.d/networking fi -# Service to update the sshd config file based on database changes for Arista devices -sudo cp $IMAGE_CONFIGS/ssh/sshd-config-updater.service $FILESYSTEM_ROOT/etc/systemd/system -sudo mkdir -p $FILESYSTEM_ROOT/etc/systemd/system/multi-user.target.wants -cd $FILESYSTEM_ROOT/etc/systemd/system/multi-user.target.wants/ -sudo ln -s ../sshd-config-updater.service sshd-config-updater.service -cd - -sudo cp $IMAGE_CONFIGS/ssh/sshd-config-updater $FILESYSTEM_ROOT/usr/bin/ -sudo chmod +x $FILESYSTEM_ROOT/usr/bin/sshd-config-updater -sudo cp $IMAGE_CONFIGS/ssh/sshd-clear-denied-sessions $FILESYSTEM_ROOT/usr/bin -sudo chmod +x $FILESYSTEM_ROOT/usr/bin/sshd-clear-denied-sessions -sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libwrap0_*_amd64.deb || \ - sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f -sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/tcpd_*_amd64.deb || \ - sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f - ## copy platform rc.local sudo cp $IMAGE_CONFIGS/platform/rc.local $FILESYSTEM_ROOT/etc/ diff --git a/files/image_config/caclmgrd/caclmgrd-start.sh b/files/image_config/caclmgrd/caclmgrd-start.sh deleted file mode 100755 index 6bca9a8126..0000000000 --- a/files/image_config/caclmgrd/caclmgrd-start.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -# Only start control plance ACL manager daemon if not an Arista platform. -# Arista devices will use their own service ACL manager daemon(s) instead. -if [ "$(sonic-cfggen -H -v "DEVICE_METADATA.localhost.platform" | grep -c "arista")" -gt 0 ]; then - echo "Not starting caclmgrd - unsupported platform" - exit 0 -fi - -exec /usr/bin/caclmgrd diff --git a/files/image_config/caclmgrd/caclmgrd.service b/files/image_config/caclmgrd/caclmgrd.service index c63f1f7c5c..f385384375 100644 --- a/files/image_config/caclmgrd/caclmgrd.service +++ b/files/image_config/caclmgrd/caclmgrd.service @@ -5,7 +5,7 @@ After=updategraph.service [Service] Type=simple -ExecStart=/usr/bin/caclmgrd-start.sh +ExecStart=/usr/bin/caclmgrd [Install] WantedBy=multi-user.target diff --git a/files/image_config/ssh/sshd-clear-denied-sessions b/files/image_config/ssh/sshd-clear-denied-sessions deleted file mode 100755 index d0ca655023..0000000000 --- a/files/image_config/ssh/sshd-clear-denied-sessions +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python - -""" -This utility will find the ip addresses of all hosts that have connected to -this device via ssh, then validate they are still in the list of allowed prefixes, -and if not kill the ssh session with a SIGHUP. -""" - -import os -import re -import subprocess - -# Run utmpdump, capture and return its output -def run_utmpdump(_utmpFilename): - devnull = file("/dev/null", "w" ) - p = subprocess.Popen(args=["utmpdump", _utmpFilename], stdout=subprocess.PIPE, stderr=devnull) - (stdout, stderr) = p.communicate() - rc = p.returncode - assert rc is not None # because p.communicate() should wait. - out = (stdout or '') + (stderr or '') - if rc: - e = SystemCommandError("%r: error code %d" % (" ".join(argv), rc)) - e.error = rc - e.output = out - raise e - return stdout - -# Run utmpdump and parse its output into a list of dicts and return that -def get_utmp_data(utmpFileName=None): - """Reads the specified utmp file. - Returns a list of dictionaries, one for each utmp entry. - All dictionary keys and values are strings - Values are right padded with spaces and may contain all - spaces if that utmp field is empty. - Dictionary keys: - "type": See UTMP_TYPE_* above - "pid": Process ID as a string - "tty": TTY (line) name - device name of tty w/o "/dev/" - "tty4": 4 char abbreivated TTY (line) name - "user": User ID - "host": Hostname for remote login, - kernel release for Run Level and Boot Time - "ipAddr": IP Address - "time": Time and date entry was made - See linux docs on utmp and utmpdemp for more info. - Example output from utmpdump: - pid tty4 user tty host ipAddr time - [7] [22953] [/238] [myname ] [pts/238 ] [example.com] [253.122.98.159 ] [Mon Dec 18 21:08:09 2017 PST] - """ - if not utmpFileName: - utmpFileName = os.environ.get( "DEFAULT_UTMP_FILE", "/var/run/utmp" ) - if not os.path.exists(utmpFileName): - return [] - output = run_utmpdump(utmpFileName) - lines = re.split("\n", output) - regExp = re.compile( - r"\[(?P" r"[^\]]*?)\s*\] \[(?P" r"[^\]]*?)\s*\] " \ - r"\[(?P" r"[^\]]*?)\s*\] \[(?P" r"[^\]]*?)\s*\] " \ - r"\[(?P" r"[^\]]*?)\s*\] \[(?P" r"[^\]]*?)\s*\] " \ - r"\[(?P" r"[^\]]*?)\s*\] \[(?P