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.log
for SSH session issues - Test with
ssh -v
for verbose connection output - Verify script permissions and SELinux contexts if commands fail