Send droplet security events to Discord - #25
Closed
OpenSource-For-Freedom wants to merge 1 commit into
Closed
Conversation
GitHub runners cannot reach this box: port 22 is open only to DigitalOcean console ranges. So anything that watches sshd, sudo, auditd or fail2ban has to live ON the droplet and push outward. This does that. Reports SSH logins, bursts of failed auth, sudo commands, fail2ban bans, container not-running or restarting, changes to whitelist/ops files that auditd is already watching, and a filling disk. A systemd timer runs it every 15 minutes and it stays completely silent when there is nothing to say, which is almost always. THE CREDENTIAL QUESTION, since this puts a webhook on the server. It lives in /etc/minecraft-alerts.env as root:root 0600, outside the git repo and outside the container. The game runs as uid 1000 with a read-only rootfs and all capabilities dropped, so compromising Minecraft does not reach it. What stealing it buys an attacker: posting messages to a private Discord channel, and spamming it. Annoying, not dangerous. What it does NOT buy is any route back into the droplet, because a webhook is write-only and one-directional; Discord cannot send commands through it. That asymmetry is the entire reason this is acceptable. The alternatives, opening a port or parking an SSH credential in CI so something outside could collect these events, are both strictly worse. The installer prompts for the URL with read -s rather than taking an argument, because an argument lands in shell history and in /proc/<pid>/cmdline where any local user can read it mid-run. The script never echoes the webhook, never passes -v to curl, and the systemd unit carries NoNewPrivileges, PrivateTmp, ProtectHome and friends. The checkpoint is written even when the post fails. Re-reporting the same window forever is worse than missing one alert, because an alert channel that repeats itself stops being read. Installed and exercised on the live droplet before committing: env file verified root:root 600, a quiet run correctly reported nothing, a forced 24-hour window found real SSH logins and posted them, and the webhook appears zero times in the journal.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security monitoring that pushes out from the droplet, because nothing can reach in.
GitHub runners cannot touch this box (port 22 is open only to DigitalOcean console ranges), so anything watching sshd, sudo, auditd or fail2ban has to live on the server itself.
What it reports
SSH logins, bursts of failed auth, sudo commands, fail2ban bans, container not-running or restarting, changes to the whitelist/ops files auditd already watches, and a filling disk.
A systemd timer runs it every 15 minutes. It is silent when there is nothing to say, which is almost always.
The credential question, head on
The webhook now lives on the server. That deserves an argument, not a shrug.
Where:
/etc/minecraft-alerts.env,root:root 0600, outside the git repo and outside the container. Minecraft runs as uid 1000 with a read-only rootfs and all capabilities dropped, so compromising the game does not reach it.What theft buys an attacker: posting to a private Discord channel, and spamming it. Annoying, not dangerous.
What it does not buy: any route back into the droplet. A webhook is write-only and one-directional — Discord cannot send commands through it.
That asymmetry is the whole reason this is acceptable. The alternatives (opening a port, or parking an SSH credential in CI so something external could collect these events) are strictly worse. Alerts should push out.
Rotation is one command: delete the webhook in Discord, re-run the installer. The old URL dies instantly.
Other hardening
read -srather than taking an argument, which would land in shell history and/proc/<pid>/cmdlinewhere any local user could read it mid-run-vto curlNoNewPrivileges,PrivateTmp,ProtectHome,ProtectKernelTunables,RestrictSUIDSGID,LockPersonalityInstalled and exercised on the live droplet before committing
To install after merge:
sudo ./alerts/install_alerts.shon the droplet. It is already running there from this test, so the timer just needs enabling via the installer if you want it permanent.