When managing multiple users on an SSH server, you might want to greet each user with a customized message upon login. The standard /etc/motd approach displays the same message for everyone, which doesn't work when you need user-specific messages like:
Welcome back, developer1! Your last login was: 2023-11-15 14:30 from 192.168.1.100
versus
ALERT for admin1: Scheduled maintenance tonight at 2AM UTC
SSH displays messages in this order during login:
/etc/issue.net(pre-login banner)/etc/motd(post-login message)- PAM modules (like
pam_motd)
The traditional approach modifies /etc/motd, but we need to intercept the process before the final display.
This method provides the most flexibility. First, create a script:
#!/bin/bash
# /usr/local/bin/user_motd.sh
USER=$1
MOTD_DIR="/etc/ssh/motd.d"
if [ -f "${MOTD_DIR}/${USER}" ]; then
cat "${MOTD_DIR}/${USER}"
elif [ -f "${MOTD_DIR}/default" ]; then
cat "${MOTD_DIR}/default"
fi
Then configure PAM by editing /etc/pam.d/sshd:
session optional pam_exec.so /usr/local/bin/user_motd.sh
Create per-user MOTD files in /etc/ssh/motd.d/:
$ sudo mkdir -p /etc/ssh/motd.d $ echo "Welcome back developer!" | sudo tee /etc/ssh/motd.d/developer1 $ echo "System alerts for admin:" | sudo tee /etc/ssh/motd.d/admin1
For simpler cases, modify the user's ~/.bash_profile:
#!/bin/bash
# ~/.bash_profile
if [ -f ~/.motd ]; then
cat ~/.motd
fi
Then create user-specific ~/.motd files:
$ echo "Your personal workspace" > ~/.motd
Combine both approaches for dynamic messages:
#!/bin/bash
# /usr/local/bin/dynamic_motd.sh
USER=$1
LAST_LOGIN=$(last -n 1 "$USER" | head -n 1 | awk '{print $3" "$4" "$5" "$6" from "$2}')
cat << EOF
Welcome $USER!
Last login: $LAST_LOGIN
System load: $(uptime | awk -F'[a-z]:' '{print $2}')
EOF
- Set proper permissions:
chmod 755 /usr/local/bin/user_motd.sh - Make MOTD directory read-only:
chmod -R 755 /etc/ssh/motd.d - Regularly audit custom scripts for vulnerabilities
If messages don't appear:
$ ssh -vvv user@host # Check debug output $ grep pam_exec /var/log/auth.log $ ls -la /etc/ssh/motd.d/ # Verify file permissions
When administering multi-user Linux systems, you might want to display personalized welcome messages when users SSH into the server. While the default /etc/motd provides a global message, we need per-user customization.
We'll leverage the bash or zsh shell initialization files combined with SSH's ForceCommand capability to achieve per-user MOTD functionality.
1. Create MOTD Template Files
First, create a directory to store user-specific MOTD files:
sudo mkdir /etc/motd.d
sudo chmod 755 /etc/motd.d
Then create template files for each user:
sudo nano /etc/motd.d/john
# Content for John's MOTD
Welcome back, John!
Your last login was: $(last -n 1 $USER | head -n 1 | awk '{print $4,$5,$6,$7}')
2. Modify SSH Configuration
Edit /etc/ssh/sshd_config:
Match User *
ForceCommand /usr/local/bin/custom_motd.sh
3. Create the MOTD Script
Create /usr/local/bin/custom_motd.sh:
#!/bin/bash
# Check if MOTD file exists for user
MOTD_FILE="/etc/motd.d/${USER}"
if [ -f "$MOTD_FILE" ]; then
# Process with parameter expansion
eval "echo \"$(cat "$MOTD_FILE")\""
echo ""
fi
# Execute user's shell
exec "$SHELL" "$@"
Make it executable:
sudo chmod +x /usr/local/bin/custom_motd.sh
For dynamic content, you can add variables to your MOTD templates:
sudo nano /etc/motd.d/alice
Hello Alice!
System load: $(uptime | awk -F'load average: ' '{print $2}')
Disk space:
$(df -h | grep -v tmpfs)
1. Set proper permissions on /etc/motd.d to prevent users from modifying others' MOTD files
2. Audit your MOTD templates for potential command injection vulnerabilities
3. Consider SELinux/AppArmor policies if enabled on your system
For simpler setups, you can add this to individual user's ~/.bashrc:
if [ -n "$SSH_CONNECTION" ]; then
echo "===================================="
echo "Welcome back, $USER!"
echo "Your home directory is using $(du -sh $HOME | awk '{print $1}')"
echo "===================================="
fi
- Check
/var/log/auth.logfor SSH session issues - Test with
ssh -vfor verbose connection output - Verify script permissions and SELinux contexts if commands fail