☰
SSH登录后显示自定义信息:motd、Banner与动态脚本配置指南
2026/10/1 10:55:57 网站建设 项目流程

干了好几年 Linux 运维,我到现在还记得第一次在客户机器上看到登录后出现大段提示信息时的那种感觉。明明只是一个再普通不过的 SSH 会话,却因为那一屏系统状态、项目名称和告警提示,整台机器的专业感一下子就上来了。后来我自己维护的服务器多了,才意识到这个需求背后其实不止是好看:它能让团队里的人一眼明白自己登录的是哪台机器,能提醒操作规范和联系窗口,还能在不额外部署监控的情况下,把负载、磁盘、在线用户这些信息直接送到每个登录者的眼前。如何在 SSH 登录后显示自定义信息,这个话题听起来小,但涉及 sshd 配置、PAM、Shell 启动脚本、动态生成脚本好几个层次,不排清楚很容易改了半天不生效,或者生效之后把 scp 和 sftp 的输出搞得一团糟。这篇文章就按我实际折腾的顺序来聊,给出静态 motd、动态 Shell 脚本、sshd Banner 三种常用做法,再加上一份排查清单和几个进阶玩法,适合给 Linux 服务器做初始化、或者想优化团队登录体验的朋友参考。

1. 动手之前,先搞清楚登录时是谁在打印这些信息

很多人在网上搜“SSH 登录后显示自定义信息”,随便找到一条命令就贴到服务器上,改完之后发现有时生效有时不生效,换了发行版又失效。其实这是因为 SSH 登录后那几行内容的输出位置并不只有一个。整个链路里,至少有三个环节可以往你的终端上写东西:sshd 服务本身、PAM 会话模块、以及登录 Shell 的启动脚本。它们执行顺序不同,权限范围不同,适合承载的内容也不同。

1.1 SSH 会话从建立到提示符出现,中间经过哪几个输出点

先说顺序。你用 ssh 客户端连上服务器的 22 端口之后,sshd 会先读取配置文件,如果配了 Banner,会在你输入账号密码之前就把它打印出来,目的是让你在认证之前就看到提示。接着完成密码或密钥认证,进入会话建立阶段,这时 PAM 会触发 pam_motd,把系统的 /etc/motd 内容显示出来,或者执行 update-motd.d 下面的动态脚本。再接着才轮到 Shell 环境,系统会加载 /etc/profile、/etc/profile.d/ 下面的脚本,然后加载用户自己的 ~/.profile 和 ~/.bashrc。

换句话说,Banner 最靠前,motd 在中间,Shell 脚本最后。如果你想显示“欢迎登录某某系统”这种静态信息,用 motd 就够了;想显示 CPU 负载、磁盘剩余、在线用户这些每次登录都变化的数值,就得在 Shell 启动脚本里跑一小段脚本;想在用户还没输密码之前就提示“本系统仅有授权人员可登录”,请配置 sshd Banner。

1.2 你想显示的内容,到底适合放在哪一层

位置显示时机生效范围适合内容
sshd Banner认证前所有 SSH 连接法律声明、安全警告、访问限制
/etc/motd交互式登录后所有登录用户静态公告、项目说明、联系人
update-motd.d 脚本交互式登录后所有登录用户系统状态、补丁提示、动态消息
/etc/profile.d/*.shShell 启动时所有登录用户动态信息、团队快捷指令
~/.bashrc、~/.zshrcShell 启动时当前用户个人环境信息、个性化提示

上面这张表是我每次初始化服务器都会参考的。有一个常见误区是:以为只要改了 /etc/motd,任何 SSH 方式登录都会看到。其实很多系统里 motd 走的是 PAM 会话模块,如果你用 scp 传文件、用 sftp 登录、或者直接执行 ssh host uptime 这类非交互命令,都可能不会触发它。这也解释了为什么要分清楚“交互式登录”和“非交互式执行命令”两个场景。新手阶段,最好把目标定清楚:我的提示信息是要给交互式登录的人看,还是要给所有 SSH 会话都展示?想清楚之后再动手,后面就不会被各种特例折腾到怀疑人生。

2. 第一招:改动 /etc/motd,给所有登录者一份静态提示

2.1 motd 的显示机制,以及在 Ubuntu、CentOS 上的差异

“motd”的全称是 Message of the Day,最早源于 Unix 系统用来发通知的机制。到了 Linux 上,它被 PAM 的 pam_motd 模块继承了下来。在 Ubuntu 这种发行版里,/etc/motd 文件本身可能不存在,或者里面是一句默认提示,真正生成内容的是 /etc/update-motd.d/ 目录下面一堆可执行脚本,它们会拼出一个动态的 motd。CentOS 7 的做法稍有不同,sshd_config 里的 PrintMotd yes 会直接打印 /etc/motd,如果 PAM 那边也配了 pam_motd,就有可能出现一段内容重复显示的情况。

所以当你看到网上有人说“直接改 /etc/motd”时,不要直接照抄,先确认一下系统发行版和 PAM 配置,否则一样不生效或者重复。我见过最典型的情况是 Ubuntu 用户改了 /etc/motd 发现没变化,其实系统显示的是 /run/motd.dynamic,由 update-motd.d 里的脚本动态生成,静态 motd 反而被“降级”成了兜底文件。

2.2 写入自定义内容的正确姿势与文本格式细节

改文件之前先把原文件备份,这是服务器操作的基本修养。具体可以这样写:

sudo cp /etc/motd /etc/motd.bak 2>/dev/null || true sudo tee /etc/motd >/dev/null <<'EOF' ____ __ ______________ __ _______ / __ \/ / / / ____/ _/ |/ |/ / ___/ / / / / / / / /_ / // /|_/ / /\__ \ / /_/ / /_/ / __/ _/ // / / / /___/ / \____/\____/_/ /___/_/ /_/_______/ 欢迎访问 Kubernetes 测试环境 联系方式:ops@example.com 本机运维入口:http://ops.example.local EOF

我习惯用 tee 而不是直接 cat >,因为 tee 在普通用户配合 sudo 时更直观,也不容易因为重定向权限问题写出一个空文件。注意 EOF 要用单引号包住,这样变量、反斜杠都不会被 Shell 提前展开;比如你写 $HOSTNAME 就是字面意思,不会在写入时变成具体主机名。文件里的内容默认就是纯文本,终端能识别 ANSI 颜色转义,但我不建议在 motd 里放大量颜色码,因为有的 SSH 客户端和日志系统会原样记录 motd,纯文本比满屏转义符更容易阅读。改完这个文件不需要重启 sshd,用户下一次登录就能看到新内容。

2.3 权限和内容红线:motd 是给所有人看的公告栏

motd 的权限默认是 644,所有用户都能读,这代表它是“公告栏”性质的。不要把内网 IP、数据库地址、内部拓扑写进去,尤其当你的服务器有公网 SSH 端口暴露时,这些信息等于变相泄露。一般写清楚设备用途、所属项目、环境名称、维护联系人就够了。

还有一点:如果团队里有同事习惯用 sftp 传文件,他可能看不到 motd,所以那些“必须让所有人看到”的重要通知,不要只依赖 motd,最好再配合登录后的 Shell 脚本或者 sshd Banner 一起做。比如停机通知,我会在 motd 里写一句话,同时在运维群里说一声,双保险才不会被漏掉。

3. 第二招:利用 Shell 启动脚本显示动态信息,负载、磁盘、在线用户都能上

3.1 动态信息为什么不能只写在 motd 里

motd 的内容在用户看到之前就已经由系统生成好了,虽然 Ubuntu 的 update-motd.d 也能跑脚本,但它更偏系统级,而且更新时间有缓存策略,不是每次登录都会实时刷新。想做到每次 SSH 登录都看到最新负载、内存、在线用户,最简单可靠的方法是借用 Shell 启动脚本。

把一段 echo 或 printf 脚本放到 /etc/profile.d/ 下面,对登录用户全局生效;放到某个用户自己的 .bashrc 里,则只对该用户生效。这里要先理解一个区别:SSH 从远程执行命令,比如 ssh host date,它不一定加载完整的 profile,甚至不分配终端;只有在分配了交互式终端的情况下,启动脚本才会把输出呈现给用户。我见过很多同事把 neofetch 直接塞进 bashrc 后,发现 scp 上传的 tar 包解压出来多了系统信息开头,这就是没做交互式判断的后果。

3.2 一段不干扰 scp、sftp 和远程命令的输出脚本

我自己在服务器上常用的模板长这样,放在 /etc/profile.d/ops-info.sh:

if [ -n "$SSH_CONNECTION" ] && [ -t 0 ]; then echo "==== $(date '+%F %T') ====" echo "登录用户: $(whoami)" echo "来源地址: ${SSH_CONNECTION%% *}" echo "系统负载: $(uptime | sed 's/.*load average://')" echo "根分区: $(df -h / | awk 'NR==2{print $3 "/" $2 " (" $5 ")"}')" fi

第一行的两个条件很关键:SSH_CONNECTION 非空,说明确实是从 SSH 进来的;[ -t 0 ] 判断标准输入是不是终端,只有交互式会话才满足。两个条件同时为真,才输出信息,否则什么都不打印。我以前在这上面栽过跟头,没加判断时每次用自动化工具执行远程命令,返回结果都会混进一段欢迎信息,轻则解析日志出错,重则把 JSON 结构直接弄坏。放到 /etc/profile.d/ 之后,先执行 bash -n /etc/profile.d/ops-info.sh 做语法检查,再开一个新 SSH 窗口看效果。

3.3 不想手写状态命令,可以用现成的信息面板工具

不想自己拼信息,可以用现成的打印工具。Debian/Ubuntu 上最常见的是 neofetch、screenfetch、landscape-sysinfo,新一点还有 fastfetch。以 neofetch 为例,安装后这样接进登录流程:

sudo apt install neofetch -y

然后编辑 /etc/profile.d/ops-info.sh:

if [ -n "$SSH_CONNECTION" ] && [ -t 0 ]; then neofetch fi

landscape-sysinfo 是 Ubuntu 自带的,基本零依赖,输出的是内存、负载、进程数、软件包更新数量这些运维关心的东西。CentOS/RHEL 系列装 screenfetch 也比较省事:

sudo yum install -y epel-release sudo yum install -y screenfetch

注意这类工具版本差异很大,有的老系统不支持新参数,有的在字符终端下会显示错位,所以最好先在测试环境跑一遍再分发到生产。我个人的偏好是:如果只是给运维看,用 landscape-sysinfo 或自己写脚本更稳;如果想让团队觉得登录界面有点意思,再上 neofetch。

3.4 不同用户登录显示不同内容,怎么优雅区分

要按用户区分,就用 case 匹配 $USER。比如管理员登录多显示系统负载,普通开发人员登录只看项目状态。实际脚本可以这样写:

case "$USER" in root) echo "管理员会话,请谨慎操作" uptime ;; dev*) echo "开发环境,当前分支状态请使用 git status 查看" ;; *) echo "欢迎,请遵守操作规范" ;; esac

注意 case 里每一段末尾要有两个分号,少写了整个脚本会报语法错误。这个示例适合放在用户自己的 .bashrc 里,不要放全局 profile.d,毕竟每个人的职责不一样,放全局容易让登录输出变得不可预测。如果公司有统一的 sudo 管理平台,也可以根据用户组来判断信息内容,原理都一样。

4. 第三招:配置 sshd Banner,让用户在输入密码前就看到安全提示

4.1 Banner 与 motd 的显示时机区别

Banner 和 motd 最大的区别在于显示时机。sshd 的 Banner 会在服务器要求输入密码之前就输出到你的 SSH 客户端,注意是在认证之前,所以即使对方没有正确的账号密码,他也能看到你设置的提示。motd 则要等认证成功、会话建立之后才会出现。

因此 Banner 天然适合放法律声明、安全警告、访问限制这类“先亮规矩再看门”的内容。很多等保和合规要求里,正好需要这种登录前提示。我在给客户做服务器初始化时,只要机器有对外暴露的端口,都会顺手把 Banner 配上,成本极低,效果却很明显。

4.2 三步配置一个登录前警告横幅

第一步,创建 Banner 文件。这个文件的位置可以自定义,一般放在 /etc/ssh/banner,权限建议 600,避免普通用户读取。示例内容:

sudo tee /etc/ssh/banner >/dev/null <<'EOF' *********************************************************************** * WARNING: Authorized access only! * * This system is for authorized maintenance use only. * * All activities may be monitored and recorded. * * If you are not authorized, disconnect immediately. * *********************************************************************** EOF

第二步,修改 sshd_config。打开 /etc/ssh/sshd_config,找到或新增一行:

Banner /etc/ssh/banner

然后重载 SSH 服务。

sudo sshd -t sudo systemctl reload sshd

如果你的系统没有 systemd,也可以用 sudo service ssh reload。reload 比 restart 温和,不会影响已经建立的连接,但新增配置一定会生效。修改后不要关掉当前 SSH 窗口,重新开一个窗口测试 Banner 是否出现,一旦配置有误,还能保留一个逃生通道回服务器改回来。

4.3 Banner 内容别踩的雷

因为 Banner 在认证前就会发送,外部扫描工具也能看到它,所以不要写主机名、真实 IP、操作系统版本、软件版本。写“仅授权用户可访问”这种通用声明即可。如果你同时开了 motd 和 Banner,内容上别重复,Banner 管安全声明,motd 管内部操作提示,分工以后整个登录界面会清爽很多。

另外,Banner 文件不要用 Windows 记事本创建的带 \r\n 换行的文件,服务器上最好用 vi 或 tee 生成,否则有些终端会显示出一个奇怪的 ^M 符号。这个问题看着小,但第一次遇到时会让人怀疑是 sshd 配置错了。

5. 改完不生效、乱码、登录变慢?这里有一份排查实录

5.1 改了 motd 但登录后没变化,先从这三个地方查

第一,确认你连接的方式是交互式登录。如果你执行的是 ssh root@host "df -h",这叫远程命令,不分配终端,很多 motd 不会显示,拿它验证 motd 自然是白搭。第二,确认系统用的是 PAM 还是 sshd 自带的 PrintMotd。在 sshd_config 里 PrintMotd yes 会让 sshd 在认证后打印 motd;同时 PAM 的 pam_motd 模块也可能打印一次,二者共享同一个文件,但输出逻辑不同。如果配置了更新式 motd,Ubuntu 还会优先显示 /run/motd.dynamic,这个动态文件会覆盖静态 motd 的显示。

第三,检查 /etc/update-motd.d 里的脚本是否还在运行,有些系统初始化脚本会定期覆盖 motd,你手写的内容第二天就被冲掉了。最简单的办法是:先备份你要改的文件,再修改,马上开新登录验证,并观察两天确认没被覆盖。如果真有这种覆盖脚本,要么把它停掉,要么把自定义信息直接写进脚本输出的文件里。

5.2 动态脚本输出乱码、错位、颜色混乱怎么办

乱码基本逃不出三个原因:编码、终端宽度、转义符。SSH 客户端和服务器字符集不一致时,中文会变成问号,建议脚本里统一用 UTF-8;如果必须显示中文,先确认客户端也设置了 UTF-8。错位通常是因为终端宽度小于脚本里固定字符串的长度,给脚本里的英文对齐空格少一点,测试时把窗口拖窄到 80 列看看效果。

颜色码尽量用 tput 而不是硬编码 ESC 字符,比如 tput setaf 2 表示绿色,tput sgr0 恢复默认,脚本可读性好,也不会因为转义序列写错导致整个提示符都乱了。还有一个小坑:不要在脚本末尾输出多余的换行符,有些终端会因此出现一个无用的空行。这个空行看着无所谓,但如果你把提示输出重定向到日志文件,经常会产生大量空行。

5.3 SSH 登录变慢,如何快速定位是不是提示脚本的锅

如果你发现输完密码之后、出现提示符之前要好几秒,把时间拆开看。先测不带交互的延迟:time ssh host true。如果这条命令也慢,那是 TCP、DNS、密钥认证这些环节的问题,和登录提示脚本无关。如果它很快,但交互式登录明显卡顿,问题多半出在 profile 或 bashrc 脚本里,可能是脚本里执行了网络请求、source 了很重的文件、或者有某个命令在等待输入。

进一步定位就跑 bash -x ~/.bashrc,看每一条语句的执行情况,卡在哪一步一目了然。除此之外,把登录提示里的复杂命令加入 timeout 包裹,比如 timeout 2 curl -s http://内网健康检查地址,能避免脚本因为网络超时拖垮整个登录过程。这个习惯在服务器网络隔离比较严格的时候尤其重要,别问我为什么知道。

5.4 自定义信息位置对照速查表

位置显示时机生效范围常用场景典型坑
sshd Banner认证前所有 SSH 连接安全警告、合规声明会暴露给未授权用户
/etc/motd交互式登录后所有登录用户公告、静态提示可能被动态脚本覆盖
/etc/profile.d/*.shShell 启动时所有登录用户动态系统状态需要判断是否为交互终端
~/.bashrc / ~/.zshrcShell 启动时单用户个人快捷信息远程执行命令时受污染
update-motd.d交互式登录后所有登录用户系统状态、补丁数量缓存策略可能导致延迟

这张表我建议保存下来。它解决的最大问题是“定位”:当你看到一段提示信息时,能反推它是从哪个环节出来的,从而知道该改哪个文件。只要能定位,排查就完成了一半。

6. 进阶玩法:把登录提示变成运维入口和自动化小助手

6.1 写一个能读取多个状态源的统一登录面板

熟悉了 profile.d 脚本的写法之后,可以慢慢把它升级成一个小型运维面板。我自己的做法是维护一个 ops-info.sh,放在所有服务器的 /etc/profile.d 下面,内容也不复杂:显示当前时间、负载、内存、磁盘、在线用户。关键不是命令有多花哨,而是把团队真正关心的几个指标固定下来。示例:

if [ -n "$SSH_CONNECTION" ] && [ -t 0 ]; then echo "========== $(hostname) ==========" echo "时间: $(date '+%F %T')" echo "负载: $(uptime | sed 's/.*load average://')" echo "内存: $(free -h | awk '/Mem:/{print $3 "/" $2}')" echo "磁盘: $(df -h / | awk 'NR==2{print $3 "/" $2 " (" $5 ")"}')" echo "在线: $(who | wc -l) 人" fi

这里坚持了最开始的交互式判断,所以不会污染自动化脚本的返回。你也可以再加一个 if [ -f /etc/motd-custom ],让每台机器拥有定制信息,又不改公共脚本。比如某些高负载机器想额外显示 GPU 使用率,某些机器想显示某条业务日志的最新一行,都可以通过环境变量或额外文件来扩展。

6.2 给同事做一个只读风格的登录后菜单

登录后菜单适合团队自维护场景:同事连上服务器,直接看到“1 看最近日志,2 查看服务状态,3 跳转到部署目录”这类选项,输入数字就能执行预设命令。这个想法很诱人,但我要先说一句:千万别在菜单里放 rm、mkfs、iptables -F 这类危险命令,也别让普通用户能通过菜单获得 root 权限。入门级实现可以只输出菜单文字,不真的交互,例如:

if [ -n "$SSH_CONNECTION" ] && [ -t 0 ]; then echo "本机常用命令:" echo " systemctl status nginx # 查看服务状态" echo " tail -f /var/log/app/error.log # 跟进错误日志" echo " cd /srv/app && git status # 查看发布目录" fi

真正的交互式菜单需要 read、case、函数,我建议单独做成一个脚本文件,在 profile.d 里只负责 source 它,方便后续维护。交互菜单的权限控制要做到足够细,最好关联 sudo 规则和审计,否则表面上看起来方便,实际上是一个巨大的安全隐患。所以我更推荐先只做展示,等团队规模稳定了再考虑交互。

6.3 批量管理多台机器:用统一模板让登录提示不再各写各的

如果你手上有几十台同类型服务器,一台台改 motd 和 profile.d 脚本既不现实也容易漏。此时可以把提示脚本放进一个统一的运维仓库,用 Ansible 的 copy 或 template 模块分发。例如这样的一段 playbook,就把 motd 和登录脚本批量复制到整批机器:

- hosts: all become: true tasks: - name: 下发 motd 静态提示 ansible.builtin.copy: src: files/motd dest: /etc/motd owner: root group: root mode: '0644' - name: 下发登录动态信息脚本 ansible.builtin.copy: src: files/ops-info.sh dest: /etc/profile.d/ops-info.sh owner: root group: root mode: '0755'

Ansible 的 copy 默认会保留源文件权限,但最好还是显式指定 mode;motd 文件是 644,脚本为了方便调试可以给 755。模板里还可以套用 ansible_hostname、environment 等变量,让每台机器显示各自的环境名和用途,真正做到定制化和自动化兼得。每次更新登录提示时,先在一台测试机上跑通,再批量下发,这个顺序千万别颠倒。

写到最后,说一个我自己这几年养成的习惯:每次初始化完一台服务器,我都会先用一个普通用户身份重新登录一遍,确认提示信息不会把自己锁在外面,也不会把远程命令输出弄乱,然后再给同事开放权限。SSH 登录后显示自定义信息这件事,看起来只是几行文字,背后却是登录链路、脚本执行时机、自动化兼容性这些实打实的细节。折腾一次之后,后面再遇到“为什么我的提示不生效”“为什么 scp 文件内容变了”这类问题,心里就有数了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询