more tools

This commit is contained in:
2025-08-29 09:40:20 +03:00
parent 4346b838ed
commit 4448764bca
15 changed files with 513 additions and 24 deletions

View File

@@ -1,27 +1,5 @@
# server-toolset
## login-mailer.sh
A collection of self-contained programs and scripts to manage different scenarios surrounding homelab, development and production server management.
Get an email when someone logs in on your server. Depends on `rsyslog` and `/var/log/auth.log`.
Requires you to host/use an email proxy. See [git.fybx.dev/fyb/mail-proxy](https://git.fybx.dev/fyb/mail-proxy) for a simple Express.js and nodemailer powered alternative. Fork and deploy to Vercel, it's set!
Change `EMAIL` and `ENDPOINT` environment variables in `login-mailer.service.template` and copy to `login-mailer.service`. Run `install.sh`.
`install.sh` and `uninstall.sh` provided for ease-of-use, places systemctl service and timer, and enables it.
## credits
Feel free to contact me for collaboration on anything!
Yiğid BALABAN, <[fyb@fybx.dev][llmail]>
[My Website][llwebsite] • [My Bento][llbento] • [X][llx] • [LinkedIn][lllinkedin]
2024
[llmail]: mailto:fyb@fybx.dev
[llwebsite]: https://fybx.dev
[llbento]: https://bento.me/balaban
[llx]: https://x.com/fybalaban
[lllinkedin]: https://linkedin.com/in/fybx
Built upon my personal experience of self hosting.

5
derpcheck-ssh/README.md Normal file
View File

@@ -0,0 +1,5 @@
# server-toolset
## derpcheck-ssh
See connection details while SSH'ing to a remote with Tailscale.

12
derpcheck-ssh/ssh.fish Normal file
View File

@@ -0,0 +1,12 @@
function ssh --wraps ssh
if test (count $argv) -eq 1
set -l target_host $argv[1]
set -l ts_status (tailscale status | grep -i -- "$target_host")
if test -n "$ts_status"
echo "$ts_status"
echo ""
end
end
command ssh $argv
end

30
login-mailer/README.md Normal file
View File

@@ -0,0 +1,30 @@
# server-toolset
## login-mailer.sh \[ARCHIVED\]
> [!WARNING]
> Please do not use this.
> Use ssh-notify instead.
Get an email when someone logs in on your server. Depends on `rsyslog` and `/var/log/auth.log`.
Requires you to host/use an email proxy. See [git.fybx.dev/fyb/mail-proxy](https://git.fybx.dev/fyb/mail-proxy) for a simple Express.js and nodemailer powered alternative. Fork and deploy to Vercel, it's set!
Change `EMAIL` and `ENDPOINT` environment variables in `login-mailer.service.template` and copy to `login-mailer.service`. Run `install.sh`.
`install.sh` and `uninstall.sh` provided for ease-of-use, places systemctl service and timer, and enables it.
## credits
Feel free to contact me for collaboration on anything!
Yiğid BALABAN, <[fyb@fybx.dev][llmail]>
[My Website][llwebsite] • [X][llx] • [LinkedIn][lllinkedin]
2024
[llmail]: mailto:fyb@fybx.dev
[llwebsite]: https://fybx.dev
[llx]: https://x.com/fybalaban
[lllinkedin]: https://linkedin.com/in/fybx

0
login-mailer.sh → login-mailer/login-mailer.sh Executable file → Normal file
View File

66
ssh-notify/README.md Normal file
View File

@@ -0,0 +1,66 @@
# server-toolset
## ssh-notify
Get notified through emails or Telegram messages when a user logs in to your server. Uses PAM modules, and a single Bash script!
### Why use this?
- It's plug-and-play: use emails with []() or Telegram, or both.
- Distro agnostic: uses PAM and Bash with the least amount of surface.
## Installation
Run `install.sh` to automate the steps below.
### 1. The script
```sh
cp ssh-notify.sh /usr/local/sbin/ssh-notify.sh
chmod 700 /usr/local/sbin/ssh-notify.sh
```
### 2. Configuration
```sh
mkdir /etc/ssh-notify
cp example.conf /etc/ssh-notify/config.conf
vim /etc/ssh-notify/config.conf # edit in place
chmod 600 /etc/ssh-notify/config.conf
```
### 3. PAM configuration
```sh
vim /etc/pam.d/sshd # edit in place and add the following lines:
```
```conf
# Send notification upon successful login (added by ssh-notify install.sh)
session optional pam_exec.so seteuid /usr/local/sbin/ssh-notify.sh
```
### 4. Logging
Optional, but suggested:
```sh
cp ssh-notify.logrotate /etc/logrotate.d/ssh-notify
```
The `ssh-notify.logrotate` tells `logrotate` to rotate `/var/log/sshnotify.log` once a day, keep seven old copies, compress older archives (but delay compressing the newest one), skip rotation if the file is empty or missing, and after each rotation immediately create a fresh log with permissions `600` owned by `root:root`; the `sharedscripts` directive ensures any `postrotate` commands (none are defined here) would only run once even if multiple files were listed.
## Credits
Feel free to contact me for collaboration on anything!
Yiğid BALABAN, <[fyb@fybx.dev][llmail]>
[My Website][llwebsite] • [X][llx] • [LinkedIn][lllinkedin]
2024
[llmail]: mailto:fyb@fybx.dev
[llwebsite]: https://fybx.dev
[llx]: https://x.com/fybalaban
[lllinkedin]: https://linkedin.com/in/fybx

10
ssh-notify/example.conf Normal file
View File

@@ -0,0 +1,10 @@
# Email Notification Settings
EMAIL_RECIPIENT=""
EMAIL_API_ENDPOINT="https://mail-proxy.example.org/api/mail"
# Telegram Notification Settings
TELEGRAM_BOT_TOKEN=""
TELEGRAM_CHAT_ID=""
# Log file for the notifier script
LOG_FILE="/var/log/ssh-notify.log"

97
ssh-notify/install.sh Normal file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# install.sh
# part of ssh-notify from server-toolset
# 2025 © Yigid BALABAN <fyb@fybx.dev>
# This script must be run as root. It installs the SSH login notification script,
# example config, updates PAM, and installs a logrotate config in a transactional manner.
set -euo pipefail
# Ensure running as root
if [[ "$(id -u)" -ne 0 ]]; then
echo "Error: This script must be run as root." >&2
exit 1
fi
# Create temporary directory for backups
BACKUP_DIR="$(mktemp -d)"
# Rollback function on error
rollback() {
echo "Error encountered. Rolling back changes..." >&2
[[ -f "$BACKUP_DIR/sshd.bak" ]] && mv "$BACKUP_DIR/sshd.bak" /etc/pam.d/sshd
[[ -f "$BACKUP_DIR/ssh-notify.sh.bak" ]] && mv "$BACKUP_DIR/ssh-notify.sh.bak" /usr/local/sbin/ssh-notify.sh
[[ -f "$BACKUP_DIR/config.conf.bak" ]] && mv "$BACKUP_DIR/config.conf.bak" /etc/ssh-notify/config.conf
[[ -f "$BACKUP_DIR/ssh-notify.logrotate.bak" ]] && mv "$BACKUP_DIR/ssh-notify.logrotate.bak" /etc/logrotate.d/ssh-notify
rm -rf "$BACKUP_DIR"
exit 1
}
trap rollback ERR
trap 'rm -rf "$BACKUP_DIR"' EXIT
# Determine script and config sources (relative to this install.sh)
_project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_SRC="${_project_root}/ssh-notify.sh"
CONF_SRC="${_project_root}/example.conf"
LOGROTATE_SRC="${_project_root}/ssh-notify.logrotate"
# Destinations
SCRIPT_DEST="/usr/local/sbin/ssh-notify.sh"
CONF_DIR="/etc/ssh-notify"
CONF_DEST="${CONF_DIR}/config.conf"
PAM_FILE="/etc/pam.d/sshd"
LOGROTATE_FILE="/etc/logrotate.d/ssh-notify"
# Step 1: Install the main script
if [[ ! -f "$SCRIPT_SRC" ]]; then
echo "Error: Source script not found at $SCRIPT_SRC" >&2
exit 1
fi
if [[ -f "$SCRIPT_DEST" ]]; then
cp "$SCRIPT_DEST" "$BACKUP_DIR/ssh-notify.sh.bak"
fi
install -o root -g root -m 700 "$SCRIPT_SRC" "$SCRIPT_DEST"
echo "Installed script to $SCRIPT_DEST"
# Step 2: Install the example configuration
if [[ ! -f "$CONF_SRC" ]]; then
echo "Error: Example config not found at $CONF_SRC" >&2
exit 1
fi
mkdir -p "$CONF_DIR" && chmod 700 "$CONF_DIR"
if [[ -f "$CONF_DEST" ]]; then
cp "$CONF_DEST" "$BACKUP_DIR/config.conf.bak"
fi
install -o root -g root -m 600 "$CONF_SRC" "$CONF_DEST"
echo "Installed config to $CONF_DEST"
# Step 3: Update PAM configuration idempotently
if [[ ! -f "$PAM_FILE" ]]; then
echo "Error: PAM config file not found at $PAM_FILE" >&2
exit 1
fi
cp "$PAM_FILE" "$BACKUP_DIR/sshd.bak"
if ! grep -q "ssh-notify.sh" "$PAM_FILE"; then
cat << 'EOF' >> "$PAM_FILE"
# Send notification upon successful login (added by ssh-notify install.sh)
session optional pam_exec.so seteuid /usr/local/sbin/ssh-notify.sh
EOF
echo "Appended PAM exec to $PAM_FILE"
else
echo "PAM already configured for ssh-notify"
fi
# Step 4: Install logrotate configuration
if [[ -f $LOGROTATE_FILE ]]; then
cp $LOGROTATE_FILE $BACKUP_DIR/ssh-notify.logrotate.bak
fi
install -o root -g root -m 644 $LOGROTATE_SRC $LOGROTATE_FILE
echo Copied logrotate config to $LOGROTATE_FILE
# Success message
trap - ERR
echo "Installation completed successfully."
echo "Please edit the config file at $CONF_DEST and restart sshd service."

View File

@@ -0,0 +1,12 @@
/var/log/ssh-notify.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 600 root root
sharedscripts
postrotate
endscript
}

177
ssh-notify/ssh-notify.sh Normal file
View File

@@ -0,0 +1,177 @@
#!/bin/bash
# ssh-notify.sh
# part of ssh-notify from server-toolset
# 2025 © Yigid BALABAN <fyb@fybx.dev>
# --- Configuration ---
CONFIG_FILE="/etc/ssh-notify/config.conf"
HOSTNAME=$(hostname -f)
# --- Script Variables ---
LOGIN_USER=""
REMOTE_HOST=""
MODE="PAM"
TEST_TYPE="both"
# --- Helper Functions ---
usage() {
echo
echo "Usage:"
echo " (As PAM module): Called automatically by PAM on SSH session start"
echo " (For Testing): sudo $0 -t[=email|telegram] <test_user> <test_remote_host>"
echo " -t, --test : Test both email and Telegram"
echo " -t email, --test=email : Test email only"
echo " -t telegram, --test=telegram : Test Telegram only"
echo " Example: sudo $0 -t testuser 192.168.1.100"
echo " Example: sudo $0 -t email testuser 192.168.1.100"
echo " Example: sudo $0 --test=telegram testuser 192.168.1.100"
exit 1
}
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$1] $2" >> "$LOG_FILE"
}
# --- Argument Parsing & Mode Detection ---
case "$1" in
-t|--test)
MODE="TEST"
TEST_TYPE="both"
shift
if [[ "$1" == "email" ]]; then
TEST_TYPE="email"
shift
elif [[ "$1" == "telegram" ]]; then
TEST_TYPE="telegram"
shift
fi
;;
-t=email|--test=email)
MODE="TEST"
TEST_TYPE="email"
shift
;;
-t=telegram|--test=telegram)
MODE="TEST"
TEST_TYPE="telegram"
shift
;;
*)
MODE="PAM"
;;
esac
if [[ "$MODE" == "TEST" ]]; then
if [[ $# -lt 2 ]]; then
echo "Error: Test mode requires <test_user> and <test_remote_host> arguments."
usage
fi
LOGIN_USER="$1"
REMOTE_HOST="$2"
echo "--- Running in TEST mode ($TEST_TYPE) ---"
echo "User: $LOGIN_USER"
echo "Host: $REMOTE_HOST"
else
# --- PAM Mode Logic ---
if [[ "$PAM_TYPE" != "open_session" || -z "$PAM_USER" || -z "$PAM_RHOST" ]]; then
exit 0
fi
LOGIN_USER="$PAM_USER"
REMOTE_HOST="$PAM_RHOST"
fi
# --- Load Configuration (needed for both modes) ---
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE"
else
ERR_MSG="ssh-notify Error: Configuration file $CONFIG_FILE not found."
echo "$ERR_MSG" | systemd-cat -p err -t 'ssh-notify'
echo "$ERR_MSG" >&2
exit 1
fi
# Ensure required config variables are set
if [[ -z "$EMAIL_RECIPIENT" || -z "$EMAIL_API_ENDPOINT" || -z "$TELEGRAM_BOT_TOKEN" || -z "$TELEGRAM_CHAT_ID" || -z "$LOG_FILE" ]]; then
ERR_MSG="ssh-notify Error: One or more required variables are missing in $CONFIG_FILE."
echo "$ERR_MSG" | systemd-cat -p err -t 'ssh-notify'
echo "$ERR_MSG" >&2
exit 1
fi
# Prepare log directory and file
LOG_DIR=$(dirname "$LOG_FILE")
mkdir -p "$LOG_DIR"
if [[ ! -f "$LOG_FILE" ]]; then
touch "$LOG_FILE"
# Set permissions only if we create it
chown root:root "$LOG_FILE" # Or another appropriate user/group if needed
chmod 640 "$LOG_FILE" # Restrict write access
fi
# Log the mode of operation
log_message INFO "Script triggered. Mode: $MODE. User: '$LOGIN_USER'. Host: '$REMOTE_HOST'."
# --- Prepare Notification Details ---
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S %Z')
SUBJECT="[${MODE}] SSH Login on $HOSTNAME: $LOGIN_USER" # Add mode to subject
MESSAGE_TEXT="[${MODE}] SSH login detected on $HOSTNAME: User '$LOGIN_USER' from '$REMOTE_HOST' at $TIMESTAMP"
MESSAGE_HTML="<b>[${MODE}] SSH Login Alert</b>%0AServer: $HOSTNAME%0AUser: $LOGIN_USER%0AFrom: $REMOTE_HOST%0ATime: $TIMESTAMP"
# --- Notification Functions ---
send_email() {
log_message INFO "Attempting to send email to $EMAIL_RECIPIENT for user $LOGIN_USER from $REMOTE_HOST"
JSON_PAYLOAD=$(jq -n \
--arg subject "$SUBJECT" \
--arg text "$MESSAGE_TEXT" \
--arg recipient "$EMAIL_RECIPIENT" \
'{subject: $subject, text: $text, recipient: $recipient}')
RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" \
-L "$EMAIL_API_ENDPOINT")
if [[ "$RESPONSE_CODE" -eq 200 ]]; then
log_message INFO "Email alert sent successfully. Response code: $RESPONSE_CODE"
else
log_message ERROR "Failure sending email alert. Response code: $RESPONSE_CODE"
fi
}
send_telegram() {
log_message INFO "Attempting to send Telegram message to chat $TELEGRAM_CHAT_ID for user $LOGIN_USER from $REMOTE_HOST"
TELEGRAM_API_URL="https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"
RESPONSE=$(curl -s -X POST "$TELEGRAM_API_URL" \
-d chat_id="$TELEGRAM_CHAT_ID" \
-d text="$MESSAGE_HTML" \
-d parse_mode="HTML")
if echo "$RESPONSE" | grep -q '"ok":true'; then
log_message INFO "Telegram alert sent successfully."
else
ERROR_DESC=$(echo "$RESPONSE" | jq -r .description)
log_message ERROR "Failure sending Telegram alert. Response: $ERROR_DESC"
fi
}
# --- Execute Notifications in Background ---
(
if [[ "$MODE" == "TEST" ]]; then
[[ "$TEST_TYPE" == "both" || "$TEST_TYPE" == "email" ]] && send_email
[[ "$TEST_TYPE" == "both" || "$TEST_TYPE" == "telegram" ]] && send_telegram
else
send_email
send_telegram
fi
) &
log_message INFO "Notification process forked for user $LOGIN_USER from $REMOTE_HOST"
if [[ "$MODE" == "TEST" ]]; then
echo "--- Test notifications triggered in background ---"
echo "Check $LOG_FILE for details."
fi
exit 0

102
ssh-notify/uninstall.sh Normal file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/env bash
# uninstall.sh
# part of ssh-notify from server-toolset
# 2025 © Yigid BALABAN <fyb@fybx.dev>
# This script must be run as root. It uninstalls the SSH login notification script,
# example config, updates PAM, and uninstalls the logrotate config in a transactional manner.
set -euo pipefail
# Ensure running as root
if [[ "$(id -u)" -ne 0 ]]; then
echo "Error: This script must be run as root." >&2
exit 1
fi
# Create temporary directory for backups
TMPDIR="$(mktemp -d)"
BACKUP_DIR="${TMPDIR}/backup"
mkdir -p "$BACKUP_DIR"
# Rollback function on error
rollback() {
echo "Error encountered. Rolling back changes..." >&2
[[ -f "$BACKUP_DIR/sshd.bak" ]] && mv "$BACKUP_DIR/sshd.bak" /etc/pam.d/sshd
[[ -f "$BACKUP_DIR/ssh-notify.sh.bak" ]] && mv "$BACKUP_DIR/ssh-notify.sh.bak" /usr/local/sbin/ssh-notify.sh
[[ -f "$BACKUP_DIR/config.conf.bak" ]] && mv "$BACKUP_DIR/config.conf.bak" /etc/ssh-notify/config.conf
[[ -f "$BACKUP_DIR/ssh-notify.logrotate.bak" ]] && mv "$BACKUP_DIR/ssh-notify.logrotate.bak" /etc/logrotate.d/ssh-notify
rm -rf "$TMPDIR"
exit 1
}
trap rollback ERR
# Determine project root and destinations
_project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_DEST="/usr/local/sbin/ssh-notify.sh"
CONF_DIR="/etc/ssh-notify"
CONF_DEST="${CONF_DIR}/config.conf"
PAM_FILE="/etc/pam.d/sshd"
LOGROTATE_FILE="/etc/logrotate.d/ssh-notify"
# Step 1: Remove PAM exec entry and associated comment
if [[ -f "$PAM_FILE" ]]; then
cp "$PAM_FILE" "$BACKUP_DIR/sshd.bak"
# Filter out sshnotify lines and trim any trailing blank lines
awk '
# skip the install.sh comment
$0 ~ /^# Send notification upon successful login \(added by sshnotify install\.sh\)/ { next }
# skip the pam_exec line
$0 ~ /^session[[:space:]]+optional[[:space:]]+pam_exec\.so.*sshnotify\.sh/ { next }
# collect other lines
{ buf[++n] = $0 }
END {
# drop trailing empty lines
while (n > 0 && buf[n] == "") n--
for (i = 1; i <= n; i++) print buf[i]
}
' "$PAM_FILE" > "$PAM_FILE.tmp"
if ! cmp -s "$PAM_FILE" "$PAM_FILE.tmp"; then
mv "$PAM_FILE.tmp" "$PAM_FILE"
echo "Removed PAM exec entry for ssh-notify from $PAM_FILE"
else
rm -f "$PAM_FILE.tmp"
echo "PAM exec entry for ssh-notify not found in $PAM_FILE (no changes made)"
fi
else
echo "PAM file $PAM_FILE not found."
fi
# Step 2: Remove main script
if [[ -f "$SCRIPT_DEST" ]]; then
cp "$SCRIPT_DEST" "$BACKUP_DIR/ssh-notify.sh.bak"
rm "$SCRIPT_DEST"
echo "Removed script $SCRIPT_DEST"
fi
# Step 3: Remove configuration
depart_dir_config() {
if [[ -f "$CONF_DEST" ]]; then
cp "$CONF_DEST" "$BACKUP_DIR/config.conf.bak"
rm "$CONF_DEST"
echo "Removed config $CONF_DEST"
fi
if [[ -d "$CONF_DIR" ]]; then
rmdir "$CONF_DIR" 2>/dev/null || true
echo "Removed directory $CONF_DIR"
fi
}
depart_dir_config
# Step 4: Remove logrotate configuration
if [[ -f "$LOGROTATE_FILE" ]]; then
cp "$LOGROTATE_FILE" "$BACKUP_DIR/ssh-notify.logrotate.bak"
rm "$LOGROTATE_FILE"
echo "Removed logrotate file $LOGROTATE_FILE"
fi
trap - ERR
echo "Uninstallation completed successfully."
echo "Backup files are located in $BACKUP_DIR. You may delete this directory manually."