FYI, attached are my monit systemd units. They are definitely "too hardened" for some users. You can PROBABLY just take everything before the hardening part, and use that as-is.
In particular, I deliberately prevent monit running as root (I want systemd to restart units; I just want monit to check the availability of remote hosts). This required some kludges because (IIRC) monit wants to have write access to its own config (like cupsd, or a crappy PHP webapp). The NRPE replacement's "server" side should be here: https://github.com/cyberitsolutions/die-nrpe-die but I never got around to git filter-repo'ing the publishable parts out, so I've attached a minified version of that as well.
[Unit] Description=Pro-active monitoring utility for unix systems After=network-online.target Documentation=man:monit(1) https://mmonit.com/wiki/Monit/HowTo # NOTE: "monit --interactive" means Run_Foreground and skips daemonize() (Type=simple). # This prevents systemd distinguishing "starting" from "started" (Type=forking). Meh? # This DOES NOT prevent monit doing a weird "if monit is already running, just kill -USR1 it, and exit(0)"! # https://bitbucket.org/tildeslash/monit/src/release-5-32-0/src/monit.c#lines-195:205 # https://bitbucket.org/tildeslash/monit/src/release-5-32-0/src/monit.c#lines-563:584 # Overall, I think Type=forking gives systemd slightly better information. # Type=forking is required if you want to e.g. Before=nginx.service. # # NOTE: "monit reload" is basically "kill -HUP $MAINPID", but # it doesn't require "apt install procps". [Service] Type=forking ExecStartPre=monit --test ExecStart=monit ExecReload=monit reload # FIXME: sometimes on "systemctl start monit" or "systemctl restart monit", # monit simply exits immediately. # This can happen after unattended-upgrades + needrestart restart monit # due to a library getting a security patch! # # # /join irc://irc.cyber.com.au/#cyber # 11:11 <twb> Why is monit down # 11:12 <twb> Something stopped it at 6:25 which is when cron.daily runs # 11:16 <twb> 2022-11-20T06:25:01.984164+11:00 heavy systemd[1]: cron-daily.service: Succeeded. # 11:16 <twb> 2022-11-20T06:25:05.905322+11:00 heavy systemd[1]: Stopping Apt-Cacher NG software download proxy... # 11:16 <twb> 2022-11-20T06:25:05.906880+11:00 heavy systemd[1]: Stopping Statistics collection and monitoring daemon... # 11:16 <twb> 2022-11-20T06:25:05.910253+11:00 heavy systemd[1]: Stopping Pro-active monitoring utility for unix systems... # 11:16 <twb> ...why? # 11:17 <twb> Maybe needrestart did it in response to a package upgrade # 11:17 <twb> 2022-11-20T06:25:08.540726+11:00 heavy systemd[1]: Starting Statistics collection and monitoring daemon... # 11:17 <twb> ...but then # 11:17 <twb> it doesn't actually start properly # 11:17 <twb> So I think there's two things here: # 11:18 <twb> 1) something in morning cron restarted a bunch of services # 11:18 <twb> 2) sometimes monit doesn't restart properly, and instead simply exits # 11:18 <twb> I had seen (2) before not after I stopped messing with it # 11:18 <twb> Same thing again when I run it by hand just now # 11:20 <twb> monit is doing something like exit(0) for no reason # 11:20 <twb> but not deterministically # 11:20 <mike> An ugly workaround might be to set 'restart=always' instead of just on-error # 12:06 <twb> mike: I've gotten nowhere debugging this # 12:07 <twb> mike: I'm going to do what you suggest # # /join ircs://irc.libera.chat/#monit # 11:21 <twb> So I have this problem where SOMETIMES monit when told to start as a daemon, will instead just exit immediately # 11:21 <twb> http://ix.io/4gHg # # twb@heavy:~$ sudo systemctl start monit # [sudo] password for twb: # twb@heavy:~$ sudo systemctl status monit # ● monit.service - Pro-active monitoring utility for unix systems # Loaded: loaded (/etc/systemd/system/monit.service; enabled; vendor preset: enabled) # Active: inactive (dead) since Thu 2022-11-24 11:18:40 AEDT; 3s ago # Docs: man:monit(1) # https://mmonit.com/wiki/Monit/HowTo # Process: 2059508 ExecStartPre=chown -h monit: /etc/monit/monitrc (code=exited, status=0/SUCCESS) # Process: 2059509 ExecStartPre=chmod 0600 /etc/monit/monitrc (code=exited, status=0/SUCCESS) # Process: 2059510 ExecStart=monit (code=exited, status=0/SUCCESS) # Main PID: 2059512 (code=exited, status=0/SUCCESS) # CPU: 272ms # # Nov 24 11:18:40 heavy systemd[1]: Starting Pro-active monitoring utility for unix systems... # Nov 24 11:18:40 heavy monit[2059510]: Starting Monit 5.27.2 daemon with http interface at [*]:2812 # Nov 24 11:18:40 heavy monit[2059510]: Starting Monit 5.27.2 daemon with http interface at [*]:2812 # Nov 24 11:18:40 heavy systemd[1]: Started Pro-active monitoring utility for unix systems. # Nov 24 11:18:40 heavy monit[2059513]: 'heavy' Monit 5.27.2 started # Nov 24 11:18:40 heavy monit[2059513]: Monit daemon with pid [2059513] stopped # Nov 24 11:18:40 heavy monit[2059513]: 'heavy' Monit 5.27.2 stopped # Nov 24 11:18:40 heavy systemd[1]: monit.service: Succeeded. # # 11:21 <twb> This also happens when an apt upgrade of (say) glibc causes monit to get "restarted" overnight, and the net result is monit isn't running at all :/ # 11:22 <twb> Why is monit doing this? I can't see anything obvious in the logs or the config # 11:22 <twb> It's my own systemd unit, but it's a fairly boring one [pastebin of this file] # 11:35 <twb> "Monit daemon with pid [%d] stopped\n" is coming from do_exit() # 11:39 <twb> do_exit is called by do_default if startdelay (which I have) and also state_reboot or Run_Stopped... why would it enter state_reboot or Run_Stopped? # 11:40 <twb> What I suspect is happening is monit is told "stop" and then 1s later a new monit is started, but it's still reading "you've been told to stop" from the previous monit # 11:41 <twb> Where does Run.flags get set # 11:43 <twb> It's only getting Run.flags |= run_stopped *inside* do_exit... # 11:47 <twb> Logically the only place "you should stop" would be saved is in /var/lib/monit/state -- pretty much nothing else is writable # 11:47 <twb> Where is the serialization code for that blob? # 11:49 <twb> state.c # 11:50 <twb> * Data is stored in binary form in the statefile using the following format: <MAGIC><VERSION>{<SERVICE_STATE>}+ # 11:51 <twb> ...i.e. there's nowhere in there storing the Run.flags data at all # 11:51 <twb> So... what the fuck [Service] Restart=always # Hardening # We do not need/want monit to restart broken services. # That is something systemd is capable of doing. # Therefore we can make monit run with few privileges. # # NOTE: DynamicUser=yes requires libnss-systemd, or # 0 == getpwuid_r(geteuid(), ⋯) will fail and monit will crash. # # NOTE: DynamicUser=yes implies NoNewPrivileges=yes. # This prevents this setgid privesc: # -rwxr-sr-x 1 root shadow 38912 2021-08-27 05:11 /sbin/unix_chkpwd # which means you cannot use "allow @coolgroup" (PAM authentication). # Using a generic "allow admin:admin" or a htpasswd file should still work. # This is the exact same class privesc that nginx would need to do PAM auth. # IMO it is reasonable to take the same approach here that we took for gitit KB. [Service] User=%p DynamicUser=yes StateDirectory=%p RuntimeDirectory=%p WorkingDirectory=/run/%p CacheDirectory=%p ConfigurationDirectory=%p # Monit stupidly requires it's entry-point config file # have the same owner as the monit process (usually root), and # MUST NOT be group- or world-readable. # This sort of makes sense when it contains something like # "check radius with username root and password swordfish". # # But for us, using DynamicUser=yes, it is super annoying. # To defeat this, trick monit by giving it a config file # that just says "go read the real config file". # This works because monit does NOT enforce paranoia for #included files. # # PS: monit won't accept this for the same reason (group-readable is not allowed): # # set ssl {pemkey:/etc/ssl/private/ssl-cert-snakeoil.key} # -rw-r----- root:ssl-cert /etc/ssl/private/ssl-cert-snakeoil.key # # UPDATE: with this, "ExecReload=monit reload" is still buggy, AND # "sudo -u monit status" can't work. # So instead resort to the simpler "just make a mess directly in /etc". # The only real downsides are: # # - "git -C /etc log .etckeeper" will churn when monit's UID changes. # - you can't just "cat /etc/monitrc" (meh). # # [Service] # UMask=0077 # ExecStartPre=!sh -c ">/var/lib/monit/monitrc echo include /etc/monit/monitrc.real" # ExecStartPre=!chown -h monit: /var/lib/monit/monitrc # ExecStartPre=!chmod 0600 /var/lib/monit/monitrc # BindReadOnlyPaths=/etc/monit/monitrc:/etc/monit/monitrc.real # BindReadOnlyPaths=/var/lib/monit/monitrc:/etc/monit/monitrc # [Service] ExecStartPre=+chown -h monit: /etc/monit/monitrc ExecStartPre=+chmod 0600 /etc/monit/monitrc # systemd-analyze security hardening. [Service] CapabilityBoundingSet= PrivateDevices=yes ProtectClock=yes NoNewPrivileges=yes ProtectKernelLogs=yes RemoveIPC=yes ProtectControlGroups=yes ProtectKernelModules=yes SystemCallArchitectures=native MemoryDenyWriteExecute=yes RestrictNamespaces=yes RestrictSUIDSGID=yes ProtectHostname=yes LockPersonality=yes ProtectKernelTunables=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 RestrictRealtime=yes ProtectSystem=strict # Leave this unlocked so monit does not complain: # 'heavy' statistic error -- load average data collection failed # system statistic error -- cannot get system uptime: No such file or directory # ProcSubset=pid ProtectHome=yes PrivateUsers=yes PrivateTmp=yes SystemCallFilter=@system-service SystemCallFilter=~@privileged SystemCallFilter=~@resources UMask=0077 # REDACTED.conf works by doing what is *basically* NRPE, but over SSH (with # a forced command), instead of a custom protocol. # To reduce the number of actual SSH connections, # I told ssh_config to use ControlMaster. # When I actually look at monit, I see a lot of this sort of thing: # # # Remote host told me to fuck off? # status failed (255) -- mux_client_request_session: session request failed: Session open refused by peer # kex_exchange_identification: read: Connection reset by peer # Connection reset by 203.39.179.100 port 22 # # # The *control master* told me to fuck off, but when I bypassed it, the remote host worked? # mux_client_request_session: session request failed: Session open refused by peer # ControlSocket /run/monit/ssh-b3e99525bd607c8f801778c1b5f14b684d01f262.socket already exists, disabling multiplexing # TCP OK - 0.000 second response time on localhost port 5432|time=0.000107s;;;0.000000;10.000000 # # One theory is that if there is no CM yet, and then monit stats all 30 ssh checks at once, # # 1. ssh A opens the CM socket as CM server # 2. ssh B opens the CM socket as CM client # 3. ssh B gives up waiting for CM server # 4. ssh A is ready to accept CM clients # # One straightforward way to avoid this is to just ensure the CM # server spins up *BEFORE* monit starts. Let's see if that helps... # # UPDATE: this DID NOT help. # Further, it caused this whinge from systemd: # # systemd[1]: Stopping Pro-active monitoring utility for unix systems... # monit[3488964]: Monit daemon with pid [3488964] stopped # monit[3488964]: 'heavy' Monit 5.27.2 stopped # systemd[1]: monit.service: Succeeded. # systemd[1]: Stopped Pro-active monitoring utility for unix systems. # systemd[1]: monit.service: Consumed 13.846s CPU time. # systemd[1]: Starting Pro-active monitoring utility for unix systems... # ssh[3589446]: OK - load average: 1.68, 2.29, 2.87|load1=1.680;20.000;30.000;0; load5=2.290;15.000;25.000;0; load15=2.870;10.000;20.000;0; # --> systemd[1]: monit.service: Found left-over process 3589446 (ssh) in control group while starting unit. Ignoring. # --> systemd[1]: This usually indicates unclean termination of a previous run, or service implementation deficiencies. # monit[3589447]: Starting Monit 5.27.2 daemon with http interface at [*]:2812 # monit[3589447]: Starting Monit 5.27.2 daemon with http interface at [*]:2812 # systemd[1]: Started Pro-active monitoring utility for unix systems. # monit[3589449]: 'heavy' Monit 5.27.2 started # #ExecStartPre=-/usr/bin/ssh -F /etc/monit/ssh_config REDACTED check_load [Install] WantedBy=multi-user.target
# -*-conf-*- # The upstream default is: # https://bitbucket.org/tildeslash/monit/src/master/monitrc # The (stupid!) Debian changes: # https://sources.debian.org/src/monit/1:5.32.0-1/debian/patches/010_monitrc.patch/ # In Cyber's old Ubuntu 10.04 / Nagios3 config, we had: # interval_length 60 # normal_check_interval 5 # retry_check_interval 1 # notification_interval 0 # # This means: # # * check every 5 minutes when OK; # * check every 1 minute when NOT OK; # * NEVER send a "it's still down" reminder email. # # The upstream monit default cycle is "set daemon 30". # The Debian monit default cycle is "set daemon 120". # I think I will set it to 300, to match "when OK, re-try after 5 minutes". # Monit does not support a faster cycle "when NOT OK". # # UPDATE: I commented out "with start delay 60" because it is # tangentially implicated in "monit sometimes exit(0)'s # immediately on start". See monit.service for details. # --twb, 24 Nov 2022 set daemon 300 # with start delay 60 # Log to the journal. # This is the upstream default. # This is not the Debian default. set log syslog # We run monit as an unprivileged user. # It can write /run/monit/monit.pid. # It cannot write /run/monit.pid (the default); it will exit(1). # You cannot tell monit "pidfiles are stupid; don't make one". set pidfile /run/monit/monit.pid # This is BASICALLY doing the same job as /etc/machine-id, but # it is a different syntax and is based on md5 (yuk). # The default location is ~/.monitid, which # will not work for a DynamicUser=yes in monit.service. set idfile /var/lib/monit/id # This is where monit remembers "was $check failing five minutes ago?" # The default location is ~/.monit.state, which # will not work for a DynamicUser=yes in monit.service. # If you use /var/lib/monit/ it persists across "systemctl restart monit". # If you use /run/monit/ it is lost across "systemctl restart monit". set statefile /var/lib/monit/state # The "event queue" is really an outbound mail spool. # If an alert (email) fails, # # * if there is no event queue (upstream default), # monit just moves on with its life. # # * if there is an event queue (Debian default), # monit will re-try to send the email later. # # In my experience, 99% of the time, all this achieves is # AFTER an outage is resolve, you get a flood of "problem!" and "...fixed" emails. # Often the "fixed" emails appear on time and the "problem!" emails arrive AFTER a retry delay. # So it ends up looking like the problem has come back! # This whole thing is Not Very Interesting, so just skip it. # # set eventqueue # basedir /var/lib/monit/events # slots 100 # We want monit to REPORT errors. # We don't want monit to try to FIX errors. # Consider e.g. monit detects alloc is offline. # Should it restart the firewall, nginx, apache, mysql, uwsgi, or alloc (php)? # It's bound to restart entirely the wrong thing. # And if we REALLY wanted that, we would use systemd # WatchdogSec=60 OnFailure=restart which is better integrated into systemd. # # That being the case, explicitly tell monit "DO NOT try to fix problems". # # UPDATE: I now understand "mode passive" is something you have to add # separately to EVERY CHECK separately, e.g. # # check host example.com # address example.com # mode passive # if failed port 22 protocol ssh then alert # if failed port 443 protocol https then alert # alert ab...@example.com #mode passive # To send emails, talk to smtp://localhost:25/. # It does not require authentication (msmtpd or postfix). set mailserver localhost # Replace "your faithful employee, monit" from message body. # Replace "Host: heavy" with a clickable link. # NOTE: Reply-To: support@ is handled in templates/group-amc. # # https://manpages.debian.org/bullseye-backports/monit/monit.1.en.html#Message_format # # The monit default looks like this: # # https://bitbucket.org/tildeslash/monit/src/release-5-32-0/src/alert.h#lines-32:47 # # The old nagios format looks like this: # # https://github.com/NagiosEnterprises/nagioscore/blob/nagios-3.2.0/sample-config/template-object/commands.cfg.in#L26-L38 # https://github.com/NagiosEnterprises/nagioscore/blob/nagios-4.4.6/sample-config/template-object/commands.cfg.in#L26-L38 set mail-format { from: Monit Availability Monitor <nore...@cyber.com.au> subject: Alert: $EVENT $SERVICE message: $EVENT $SERVICE https://monit.cyber.com.au/$SERVICE Date: $DATE Action: $ACTION Event: $EVENT Check: $SERVICE $DESCRIPTION } set httpd # Listen only on a unix socket; nginx will reverse-proxy it. # FIXME: is this going to work? # normally monit.socket would be created by systemd (privileged) and then handed to monit as fd 3. # But if monit creates the socket (because monit lacks socket activation support), # how can it grant nginx (group www-data) access to it? # If this DOESN'T work, I guess use a localhost-only TCP port (port 2812 allow localhost). # # UPDATE: I saw this, and the /run/monit/monit.socket file did not exist. # # monit[3388281]: HTTP server -- Could not change unix socket gid -- Operation not permitted #unixsocket /run/monit/monit.socket #gid www-data #permission 0660 # Shitty HTTP (no S) instead? port 2812 allow localhost # While monit CAN use PAM authentication ("allow @cyber"), # it does so by calling /sbin/unix_chkpwd, which requires set-gid shadow. # DynamicUser=yes implies NoNewPrivileges=yes, which blocks all sgid privesc. # In other words, PAM auth here requires exactly the same security tradeoffs as PAM auth in nginx. # Since this web UI is only accessible to people on the LAN/VPN, # just have weak "give your username" authentication -- the same as we have for the KB. # The only purpose of this is to (hopefully) log WHO disabled monitoring of check X. # # NOTE: I think you can also enable/disable a check from the CLI, e.g. # # monit --group=amc status # monit unmonitor amc.prisonpc.com # monit monitor amc.prisonpc.com-check_apache # # UPDATE: removed. # Nobody benefits (when clicking "unmonitor", the log doesn't indicate WHO clicked it), and # conz keeps forgetting and typing in his REAL unix password (i.e. monit could learn it). # # allow conz:"" # allow ron:"" # allow twb:"" # allow cjb:"" readonly # allow mattcen:"" # allow mike:"" # # This is for monit to check... itself. # allow guest:"" readonly include /etc/monit/conf.d/* include /etc/monit/conf-enabled/*
# Proxying through nginx really only exists for three reasons: # 1. monit whinges if the TLS private key isn't EXACTLY the permissions it wants. # 2. so you can browse to https://monit.cyber.com.au/ (without a port number). # 3. I forget what my third reason was. server { listen 80; listen [::]:80; server_name monit.cyber.com.au; # Serve ACME http-01 challenges directly. location /.well-known/ { root /var/www/html/; } # Everything else turns into HTTPS. location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name monit.cyber.com.au; ssl_certificate /etc/letsencrypt-uacme/monit.cyber.com.au/cert.pem; ssl_certificate_key /etc/letsencrypt-uacme/private/monit.cyber.com.au/key.pem; # Serve ACME http-01 challenges directly. location /.well-known/ { root /var/www/html/; } # Everything else forwarded to monit. location / { allow ::1; allow 127.0.0.0/8; allow 10.194.71.0/24; allow 203.7.155.0/24; deny all; # Can't use this neatly until monit supports "socket activation". # Without that, an unprivileged monit cannot "chgrp www-data monit.socket". # # proxy_pass http://unix:/run/monit/monit.socket; proxy_pass http://localhost:2812; } }
# -*-conf-*- Host * # Enable ControlMaster (avoid ssh setup/teardown costs for each poll). ControlMaster auto ControlPath /run/monit/ssh-%C.socket ControlPersist 3600 # Probably overall good? Compression yes # Passwords are for chumps. BatchMode yes IdentitiesOnly yes IdentityFile /var/lib/monit/ssh.id_ed25519 UserKnownHostsFile /var/lib/monit/ssh.known_hosts # Save me writing "nagios-remote@" in every check. User nagios-remote
NAME DESCRIPTION EXPOSURE ✗ PrivateNetwork= Service has access to the host's network 0.5 ✓ User=/DynamicUser= Service runs under a transient non-root user identity ✓ CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP) Service cannot change UID/GID identities/capabilities ✓ CapabilityBoundingSet=~CAP_SYS_ADMIN Service has no administrator privileges ✓ CapabilityBoundingSet=~CAP_SYS_PTRACE Service has no ptrace() debugging abilities ✗ RestrictAddressFamilies=~AF_(INET|INET6) Service may allocate Internet sockets 0.3 ✓ RestrictNamespaces=~CLONE_NEWUSER Service cannot create user namespaces ✓ RestrictAddressFamilies=~… Service cannot allocate exotic sockets ✓ CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP) Service cannot change file ownership/access mode/capabilities ✓ CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER) Service cannot override UNIX file/IPC permission checks ✓ CapabilityBoundingSet=~CAP_NET_ADMIN Service has no network configuration privileges ✓ CapabilityBoundingSet=~CAP_SYS_MODULE Service cannot load kernel modules ✓ CapabilityBoundingSet=~CAP_SYS_RAWIO Service has no raw I/O access ✓ CapabilityBoundingSet=~CAP_SYS_TIME Service processes cannot change the system clock ✗ DeviceAllow= Service has a device ACL with some special devices 0.1 ✗ IPAddressDeny= Service does not define an IP address allow list 0.2 ✓ KeyringMode= Service doesn't share key material with other services ✓ NoNewPrivileges= Service processes cannot acquire new privileges ✓ NotifyAccess= Service child processes cannot alter service state ✓ PrivateDevices= Service has no access to hardware devices ✓ PrivateMounts= Service cannot install system mounts ✓ PrivateTmp= Service has no access to other software's temporary files ✓ PrivateUsers= Service does not have access to other users ✓ ProtectClock= Service cannot write to the hardware clock or system clock ✓ ProtectControlGroups= Service cannot modify the control group file system ✓ ProtectHome= Service has no access to home directories ✓ ProtectKernelLogs= Service cannot read from or write to the kernel log ring buffer ✓ ProtectKernelModules= Service cannot load or read kernel modules ✓ ProtectKernelTunables= Service cannot alter kernel tunables (/proc/sys, …) ✗ ProtectProc= Service has full access to process tree (/proc hidepid=) 0.2 ✓ ProtectSystem= Service has strict read-only access to the OS file hierarchy ✓ RestrictAddressFamilies=~AF_PACKET Service cannot allocate packet sockets ✓ RestrictSUIDSGID= SUID/SGID file creation by service is restricted ✓ SystemCallArchitectures= Service may execute system calls only with native ABI ✓ SystemCallFilter=~@clock System call allow list defined for service, and @clock is not included ✓ SystemCallFilter=~@debug System call allow list defined for service, and @debug is not included ✓ SystemCallFilter=~@module System call allow list defined for service, and @module is not included ✓ SystemCallFilter=~@mount System call allow list defined for service, and @mount is not included ✓ SystemCallFilter=~@raw-io System call allow list defined for service, and @raw-io is not included ✓ SystemCallFilter=~@reboot System call allow list defined for service, and @reboot is not included ✓ SystemCallFilter=~@swap System call allow list defined for service, and @swap is not included ✓ SystemCallFilter=~@privileged System call allow list defined for service, and @privileged is not included ✓ SystemCallFilter=~@resources System call allow list defined for service, and @resources is not included ✓ AmbientCapabilities= Service process does not receive ambient capabilities ✓ CapabilityBoundingSet=~CAP_AUDIT_* Service has no audit subsystem access ✓ CapabilityBoundingSet=~CAP_KILL Service cannot send UNIX signals to arbitrary processes ✓ CapabilityBoundingSet=~CAP_MKNOD Service cannot create device nodes ✓ CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW) Service has no elevated networking privileges ✓ CapabilityBoundingSet=~CAP_SYSLOG Service has no access to kernel logging ✓ CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE) Service has no privileges to change resource use parameters ✓ RestrictNamespaces=~CLONE_NEWCGROUP Service cannot create cgroup namespaces ✓ RestrictNamespaces=~CLONE_NEWIPC Service cannot create IPC namespaces ✓ RestrictNamespaces=~CLONE_NEWNET Service cannot create network namespaces ✓ RestrictNamespaces=~CLONE_NEWNS Service cannot create file system namespaces ✓ RestrictNamespaces=~CLONE_NEWPID Service cannot create process namespaces ✓ RestrictRealtime= Service realtime scheduling access is restricted ✓ SystemCallFilter=~@cpu-emulation System call allow list defined for service, and @cpu-emulation is not included ✓ SystemCallFilter=~@obsolete System call allow list defined for service, and @obsolete is not included ✗ RestrictAddressFamilies=~AF_NETLINK Service may allocate netlink sockets 0.1 ✗ RootDirectory=/RootImage= Service runs within the host's root directory 0.1 ✓ SupplementaryGroups= Service has no supplementary groups ✓ CapabilityBoundingSet=~CAP_MAC_* Service cannot adjust SMACK MAC ✓ CapabilityBoundingSet=~CAP_SYS_BOOT Service cannot issue reboot() ✓ Delegate= Service does not maintain its own delegated control group subtree ✓ LockPersonality= Service cannot change ABI personality ✓ MemoryDenyWriteExecute= Service cannot create writable executable memory mappings ✓ RemoveIPC= Service user cannot leave SysV IPC objects around ✓ RestrictNamespaces=~CLONE_NEWUTS Service cannot create hostname namespaces ✓ UMask= Files created by service are accessible only by service's own user by default ✓ CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE Service cannot mark files immutable ✓ CapabilityBoundingSet=~CAP_IPC_LOCK Service cannot lock memory into RAM ✓ CapabilityBoundingSet=~CAP_SYS_CHROOT Service cannot issue chroot() ✓ ProtectHostname= Service cannot change system host/domainname ✓ CapabilityBoundingSet=~CAP_BLOCK_SUSPEND Service cannot establish wake locks ✓ CapabilityBoundingSet=~CAP_LEASE Service cannot create file leases ✓ CapabilityBoundingSet=~CAP_SYS_PACCT Service cannot use acct() ✓ CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG Service cannot issue vhangup() ✓ CapabilityBoundingSet=~CAP_WAKE_ALARM Service cannot program timers that wake up the system ✗ RestrictAddressFamilies=~AF_UNIX Service may allocate local sockets 0.1 ✗ ProcSubset= Service has full access to non-process /proc files (/proc subset=) 0.1 → Overall exposure level for monit.service: 1.3 OK 🙂
#!/usr/bin/python3 import configparser import os import shlex import subprocess import sys import syslog syslog.openlog(ident='nagios-remote', logoption=syslog.LOG_PID) config = configparser.ConfigParser( inline_comment_prefixes='#;', interpolation=None) if os.path.isfile('/etc/nagios-remote.cfg'): with open('/etc/nagios-remote.cfg') as f: config.read_string( source=f.name, string='[local]\n' + f.read()) else: with open('/etc/nagios/nrpe.cfg') as f: config.read_string( source=f.name, string='[DEFAULT]\n' + f.read()) with open('/etc/nagios/nrpe_local.cfg') as f: config.read_string( source=f.name, string='[local]\n' + f.read()) if len(sys.argv) != 1: raise RuntimeError( "Command line arguments are not supported." " Use SSH_ORIGINAL_COMMAND instead") try: nrpe_command = os.environ['SSH_ORIGINAL_COMMAND'] assert nrpe_command.replace('-', '').isidentifier() command = config.get('local', 'command[{}]'.format(nrpe_command)) except KeyError: print("nagios-remote.py: No SSH_ORIGINAL_COMMAND specified") exit(3) except configparser.NoOptionError: print(f"nagios-remote.py: Command {nrpe_command} not recognised") exit(3) except AssertionError: print(f"nagios-remote.py: Unsupported command syntax: {nrpe_command}") exit(3) results = subprocess.run(shlex.split(command)) syslog.syslog( f'Got {results.returncode} when asked to run {nrpe_command}: {command}') exit(results.returncode)