login alerts

This commit is contained in:
Alex 2025-01-07 02:52:19 +03:00
parent 3eb358d618
commit 0e177210f6
10 changed files with 85 additions and 5 deletions

View file

@ -13,8 +13,17 @@ DISCLAIMER: This repository does not have anything to do with the LEGO Group. "l
* Copy `config.example.json` to `config.json`, edit as necessary
* Run `alerting/login.py` once to login into Matrix
### Setting up login alerts
* Copy `lego-login-alert` to your `/etc/sudoers.d`
* Add this to your `/etc/ssh/sshd_config`:
```
# login alerts
ForceCommand /opt/lego-monitoring/wrappers/login_wrapper.sh
```
## Running
* `prettyprint.py` -- check and print all sensors
* `service.py` -- launch service
* `lego-monitoring.service` is a systemd unit that starts `service.py`
* `assets/lego-monitoring.service` is a systemd unit that starts `service.py`

View file

@ -1,13 +1,10 @@
import json
from dataclasses import dataclass
from typing import Optional
import aiofiles
import nio
from alerting.enum import AlertType, Severity
from misc import cvars
from misc.common import CONFIG_FILE
from misc.config import get_config

View file

@ -8,7 +8,7 @@ class AlertType(StrEnum):
CPU = "CPU"
TEMP = "TEMP"
VULN = "VULN"
LOGIN = "LOGIN" # TODO
LOGIN = "LOGIN"
SMART = "SMART" # TODO
RAID = "RAID"
DISKS = "DISKS"

2
assets/lego-login-alert Normal file
View file

@ -0,0 +1,2 @@
Defaults env_keep += "SSH_CLIENT"
ALL ALL=(ALL:ALL) NOPASSWD: /opt/lego-monitoring/wrappers/send_login_alert.sh

View file

@ -22,6 +22,9 @@
"severity": "CRITICAL"
}
]
},
"login": {
"hostname": "example.com"
}
}
}

View file

@ -38,11 +38,17 @@ class CheckWearoutConfig(NestedDeserializableDataclass):
disks: list[CheckWearoutDiskConfig]
@dataclass
class CheckLoginConfig:
hostname: str
@dataclass
class ChecksConfig(NestedDeserializableDataclass):
docker_registry: CheckDockerRegistryConfig
raid: CheckRaidConfig
wearout: CheckWearoutConfig
login: CheckLoginConfig
@dataclass

47
send_login_alert.py Normal file
View file

@ -0,0 +1,47 @@
import asyncio
import os
import socket
import sys
from alerting import alerts
from alerting.enum import AlertType, Severity
from misc.config import get_config
async def main():
check_config = get_config().checks.login
try:
from_where = os.environ["SSH_CLIENT"].split()[0]
except:
from_where = os.ttyname(sys.stdout.fileno())
is_local = True
else:
is_local = False
try:
actual_user = os.environ["SUDO_USER"]
except Exception as exc:
await alerts.send_alert(
alerts.Alert(
alert_type=AlertType.ERROR,
message=f"Failed to determine username for login from {from_where}, see logs",
severity=Severity.CRITICAL,
)
)
return
if not is_local:
rdns_result = socket.getnameinfo((from_where, 0), 0)[0]
message = f"Login from {from_where} as {actual_user} on `{check_config.hostname}`"
html_message = f"Login from <code>{from_where}</code> ({rdns_result}) as {actual_user} on <code>{check_config.hostname}</code>"
else:
message = f"Login from {from_where} as {actual_user} on {check_config.hostname}"
html_message = f"Login from {from_where} as {actual_user} on <code>{check_config.hostname}</code>"
alert = alerts.Alert(alert_type=AlertType.LOGIN, message=message, severity=Severity.INFO, html_message=html_message)
await alerts.send_alert(alert)
if __name__ == "__main__":
asyncio.run(main())

12
wrappers/login_wrapper.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/bash
mydir=$(dirname "$0")
sudo "$mydir/send_login_alert.sh"
shell=$(getent passwd $LOGNAME | cut -d: -f7)
if [[ -n $SSH_ORIGINAL_COMMAND ]] # command given, so run it
then
exec "$shell" -c "$SSH_ORIGINAL_COMMAND"
else # no command, so interactive login shell
exec "$shell" -il
fi

4
wrappers/send_login_alert.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
mydir=$(dirname "$0")
"$mydir/../.venv/bin/python" "$mydir/../send_login_alert.py"