Hello everyone!

As the title says, I am trying to set up email alerts on my server whenever there is a successful ssh connection (will also setup the same for failed connections with fail2ban later). I already have the email script created and it works (I use it to monitor the directories containing all of these security scripts for changes so that I also get notified if anything critical is modified or deleted in those directories).

I also created a very basic user called test for - you guessed it - testing purposes. This user doesn't have a home directory or anything like that.

Here are the relevant scripts:

$ cat /usr/local/bin/login-alert.sh
#!/bin/bash

# Sends alerts only for real terminals not cron jobs
if [[ -n "$SSH_CONNECTION" ]]; then
	USERNAME=$(whoami)
	IP=$(echo $SSH_CONNECTION | awk '{print $1}')
	HOST=$(hostname)
	DATETIME=$(date)
	/usr/local/bin/semail \
		-s "[CRITICAL] SSH Login to $HOST" \
	 	-b "Login detected:\n\nUser: $USERNAME\nIP: $IP\nTime: $DATETIME\nTTY: $SSH_TTY"
fi

$ cat /usr/local/bin/semail
#!/bin/bash

# Default values
TO="my_email@here.com"
FROM="notifications@my_server.com"
SUBJECT=""
BODY=""
BODY_FILE=""


# Help function
show_help() {
cat <<EOF
Usage: $0 [OPTIONS]

Send a test email using Postfix.

Options:
  -t, --to EMAIL            Recipient email address (default: $TO)
  -s, --subject TEXT        Subject of the email (required)
  -b, --body TEXT           Body text of the email
  -f, --body-file FILE      File to read body text from (overrides --body)
  -h, --help                Show this help message

If no body or body-file is provided, you will be prompted to enter the body interactively.

Examples:
  $0 -s "Test subject" -b "Hello\nThis is a test"
  $0 --subject "Test" --body-file message.txt
EOF
}

# Parse arguments
while [[ "$#" -gt 0 ]]; do
	case "$1" in
		-t|--to)
			TO="$2"
			shift 2
			;;
		-s|--subject)
			SUBJECT="$2"
			shift 2
			;;
		-b|--body)
			BODY="$2"
			shift 2
			;;
		-f|--body-file)
			BODY_FILE="$2"
			shift 2
			;;
		-h|--help)
			show_help
			exit 0
			;;
		*)
			echo "Unknown option: $1"
			show_help
			exit 1
			;;
	esac
done

# Validate required parameters
if [[ -z "$SUBJECT" ]]; then
	echo "Error: --subject is required."
	show_help
	exit 1
fi

# Handle body input
if [[ -n "$BODY_FILE" ]]; then
	if [[ ! -f "$BODY_FILE" ]]; then
		echo "Error: Body file '$BODY_FILE' does not exist."
		exit 1
	fi
	BODY=$(<"$BODY_FILE")
elif [[ -z "$BODY" ]]; then
	echo "Enter the body of the email (end with Ctrl-D):"
	BODY=$(</dev/stdin)
fi

# Send email
{
	echo "From: $FROM"
	echo "To: $TO"
	echo "Subject: $SUBJECT"
	echo "Content-Type: text/plain; charset=UTF-8"
	echo
	printf "%b\n" "$BODY"
} | /usr/sbin/sendmail -t -f "$FROM"

# /usr/sbin/sendmail -f "$FROM" "$TO" <<EOF
# From: $FROM
# To: $TO
# Subject: $SUBJECT

# $BODY
# EOF

echo "Email sent to $TO"

And here is the output I see when I login as the test user:

ssh test@my_server.com
test@my_server.com's password:
dir=/ failed: exit code 2
dir=/ failed: exit code 2
Linux my_server 6.1.0-37-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Aug 15 07:08:24 2025 from my_ip_address
Could not chdir to home directory /home/test: No such file or directory

I don't get email alerts for any user, neither the test user nor my regular admin user.

Here is my /etc/pam.d/sshd relevant lines:

# Standard Un*x session setup and teardown.
account optional pam_exec.so seteuid dir=/ /usr/local/bin/login_notify.sh
@include common-session

I also tried with session instead of account and without the dir=/ part.

Can anyone help me troubleshoot this please?

Thanks in advance. Of course if you need any more info I'll do my best to provide it :)

all 8 comments

sorted by: hot top controversial new old
[–] 3 points 1 year ago* (1 child)

Your PAM line calls /usr/local/bin/login_notify.sh but your script is named login-alert.sh. That alone makes pam_exec fail (ENOENT).

Seems like AI hallucination type of mistake.

  • source
  • hideshow 1 child comment
  • [–] 1 point 1 year ago (1 child)

    It looks like pam_exec has a log file option and a debug option. Those will give you some more info about what's going on.

    There's also one piece that I don't think you mentioned, and that's sshd config settings for pam. See if usePAM is set there.

  • source
  • hideshow 1 child comment
  • [–] 1 point 1 year ago* (last edited 1 year ago) (2 children)

    According to man on my computer, pam_exec doesn't have any "dir" parameter, so you might need to remove that.

    But it has a "log" parameter, so you could add "set +eux" to your script and add a "log=/tmp/loginalert.log" to the pam_exec call and see what the error message is. Or use the "stdout" option to make it output the errors to stdout on login for debugging.

    It seems to me you're requiring a subject and body text, but never setting any.

  • source
  • hideshow 2 child comments
  • [–] [S] 2 points 1 year ago (1 child)

    Ill remove the dir parameter and add the stdout logging and come back with more info.

    As for the subject body, I set those from the -s and -b flags respectively when calling semail

  • source
  • parent
  • hideshow 1 child comment
  • [–] 1 point 1 year ago

    Sure, you're right, I skipped past that. Maybe you need to add a few print statements as well. For example I don't know when SSH_CONNECTION gets set. Maybe that's not yet available if you use "account" instead of "session".

  • source
  • parent
  • [–] 1 point 1 year ago

    Nice idea. Saving this thread to try that on my own server when I have the energy.

    I wouldn't do that with failed logins, though. My server is hammered with myriad login attempts all the time.

  • source