TOC

使用 ntfy 做 Ubuntu 认证通知

/etc/profile.d/loginnotify.sh

#!/bin/sh

###############################################################################
# loginnotify.sh
#
# SSH login/logout notification via ntfy
#
# Install:
#   /etc/profile.d/loginnotify.sh
###############################################################################

###############################################################################
# Configuration
###############################################################################

# Prevent multiple sourcing
if [ -n "${LOGINNOTIFY_LOADED:-}" ]; then
    return 0
fi
readonly LOGINNOTIFY_LOADED=1

readonly NTFY_BIN="/usr/bin/ntfy"
readonly IP_BIN="$HOME/Projects/Mine/WLB/tools/ip2region/ip2region"
readonly TMP_DIR="/tmp"
readonly LOG_FILE="${TMP_DIR}/loginnotify.log"
readonly NTFY_TOPIC="loginnotify"
readonly TITLE_LOGIN="SSH Login"
readonly TITLE_LOGOUT="SSH Logout"
readonly HOST="${HOSTNAME:-$(hostname)}"
readonly JQ_BIN="$(command -v jq 2>/dev/null || true)"

# Only handle SSH sessions.
[ -n "$SSH_CONNECTION" ] || return 0

###############################################################################
# Helper Functions
###############################################################################

_get_tty() {
    basename "${SSH_TTY:-$(tty 2>/dev/null)}" | tr '/' '-'
}

_get_client_ip() {
    local ip
    ip="${SSH_CONNECTION%% *}"
    printf '%s\n' "${ip:-localhost}"
}

_get_location() {
    local ip="$1"
    local result
    local region

    case "$ip" in
        ""|localhost)
            printf '%s\n' "localhost"
            return
            ;;
    esac

    if [ ! -x "$IP_BIN" ] || [ ! -x "$JQ_BIN" ]; then
        printf '%s\n' "unknown"
        return
    fi

    if ! result=$("$IP_BIN" "$ip" 2>/dev/null); then
        printf '%s\n' "unknown"
        return
    fi

    region=$(
        "$JQ_BIN" -r '.region // empty' <<<"$result" 2>/dev/null
    )

    if [[ "$region" == Reserved* ]]; then
        printf '%s\n' "private"
    elif [ -z "$region" ]; then
        printf '%s\n' "unknown"
    else
        printf '%s\n' "$region"
    fi
}

_collect_info() {
    local client_ip location tty_dev

    client_ip=$(_get_client_ip)
    location=$(_get_location "$client_ip")
    tty_dev=$(_get_tty)

    printf '%s\n' \
"Client:   $client_ip
Location: $location
TTY:      $tty_dev"
}


_get_timefile() {
    printf '%s/loginnotify_%s_%s\n' \
        "$TMP_DIR" \
        "$USER" \
        "$(_get_tty)"
}


_format_duration() {
    local seconds="$1"

    local days
    local hours
    local mins

    days=$((seconds / 86400))
    hours=$(((seconds % 86400) / 3600))
    mins=$(((seconds % 3600) / 60))


    if (( days > 0 )); then
        printf "%dd %dh %dm" "$days" "$hours" "$mins"
    elif (( hours > 0 )); then
        printf "%dh %dm" "$hours" "$mins"
    else
        printf "%dm" "$mins"
    fi
}

_send_ntfy_async() {
    local title="$1"
    local message="$2"
    [ -x "$NTFY_BIN" ] || return
    nohup "$NTFY_BIN" publish --title "$title $USER@$HOST" "$NTFY_TOPIC" "$message" >>"$LOG_FILE" 2>&1 </dev/null &
    disown
}

###############################################################################
# Business Logic
###############################################################################

loginnotify_login() {
    date +%s > "$(_get_timefile)"
    _send_ntfy_async \
        "$TITLE_LOGIN" \
        "$(_collect_info)"
}

loginnotify_logout() {
    local timefile
    local duration_str="unknown"
    local login_time
    local logout_time
    local duration

    timefile=$(_get_timefile)
    if [ -f "$timefile" ]; then
        read -r login_time < "$timefile"
        logout_time=$(date +%s)
        duration=$((logout_time - login_time))
        duration_str=$(_format_duration "$duration")
        rm -f "$timefile"
    fi

    _send_ntfy_async \
        "$TITLE_LOGOUT" \
"$(_collect_info)
Duration: $duration_str"
}

###############################################################################
# Main (bash only)
###############################################################################

if [ -n "$BASH_VERSION" ]; then
    if [ -n "$SSH_CONNECTION" ]; then
        trap loginnotify_logout EXIT
        loginnotify_login
    fi
fi

/etc/zsh/zlogin

# /etc/zsh/zlogin: system-wide .zlogin file for zsh(1).
#
# This file is sourced only for login shells. It
# should contain commands that should be executed only
# in login shells. It should be used to set the terminal
# type and run a series of external commands (fortune,
# msgs, from, etc.)
#
# Global Order: zshenv, zprofile, zshrc, zlogin

if [ -r /etc/profile.d/loginnotify.sh ]; then
    source /etc/profile.d/loginnotify.sh
fi
if [[ -n "$SSH_CONNECTION" ]]; then
    loginnotify_login
fi

/etc/zsh/zlogout

# /etc/zsh/zlogout: system-wide .zlogout file for zsh(1).

if [ -r /etc/profile.d/loginnotify.sh ]; then
    source /etc/profile.d/loginnotify.sh
fi
if [[ -n "$SSH_CONNECTION" ]]; then
    loginnotify_logout
fi
如果你有魔法,你可以看到一个评论框~