ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SSH登录提示配置全解析:Banner、motd与Shell脚本

SSH登录提示配置全解析:Banner、motd与Shell脚本 1. 先搞清楚SSH登录链路上的几个展示节点再动手改配置每次SSH登录服务器真正出现命令提示符之前其实系统已经悄悄做了很多事建立连接、交换密钥、验证身份、初始化Shell环境。在这个过程中存在多个可以“插话”的时机也就是显示自定义信息的位置。很多新手第一次想配这个功能习惯性地直接找“motd”文件改了结果发现有的场景生效、有的场景不生效原因就在这里——不同配置对应的展示节点根本不一样。从OpenSSH的实际工作流程看一次典型的SSH登录会经历四个信息展示窗口连接建立后、认证之前可以显示Banner信息这段内容会在你输入密码或加载密钥之前就出现在屏幕上认证通过之后、Shell正式启动之前可以显示motdMessage of the Day这是大家最熟悉的那张“登录欢迎页”Shell启动过程里还会执行全局profile脚本和用户级rc文件这里也能输出内容如果系统打开了issue文件的终端展示在纯本地tty登录时也会看到额外提示只不过SSH场景下默认不涉及。把这几个节点区分清楚后面所有配置就都能对号入座了。九十月份很多服务器被爆破扫描的日志里Banner就是第一道“门面”motd则是进入系统后最直观的“公告栏”而Shell脚本适合做更灵活的动态展示。所以我的建议是想声明“未经授权禁止登录”优先用Banner想展示系统负载、内存、磁盘这类运维信息优先用motd想做按用户、按来源IP区分的定制提示再考虑Shell脚本。1.1 一次SSH登录信息到底按什么顺序出现我们顺着OpenSSH的实际流程走一遍你就明白每个配置该放在哪了。客户端执行ssh命令后TCP连接先建立服务器端sshd读取sshd_config里的Banner配置把文件内容原样发给客户端。这时你还没输密码所以Banner内容不需要任何身份判断任何人都能看到。这就意味着凡是涉及内网地址、系统版本、应用路径这类敏感信息尽量不要放在Banner里。认证通过之后sshd会调用PAM或自身逻辑输出motd。这里有个关键差异如果sshd_config里UsePAM设为yesmotd的展示由pam_motd模块接管而sshd自己的PrintMotd参数反而会失效如果UsePAM设为no则由PrintMotd控制。很多人在改完motd后抱怨“没反应”一半以上的情况其实是没搞清这种双重控制关系。motd展示完后SSH会为用户启动一个登录Shelllogin shell。Bash作为登录Shell启动时会依次加载/etc/profile、用户目录下的.profile或.bash_profile最后再加载.bashrc。所以如果你把配置写在/etc/profile.d/下它会在motd之后执行如果写在.bashrc里则在用户每次打开新的交互终端时也会执行。这个区别直接决定了你的提示信息是“登录时只出现一次”还是“每次开终端都出现”。1.2 为什么值得花时间配置登录提示抛开“看起来很专业”这种感受层面的优势自定义登录信息至少能解决三个实际问题。第一是安全合规。公司服务器如果被非授权人员连上一个醒目的“本系统仅供授权用户使用所有操作将被记录”提示既是威慑也是审计时的有效声明。我见过不少等保测评要求里明确写了“用户登录系统时应显示警示信息”这种条款而SSH Banner就是满足这条要求最直接的手段。第二是运维效率。登录后立刻看到负载、磁盘占用、内存余量、最近登录的IP不用再手敲uptime、df、free这些命令。在排查故障的紧要关头每次少敲几个命令省下的时间看着不多但胜在“一眼就能进入状态”。第三是团队协作。共享测试服务器上经常出现“谁把环境动了”的争论。如果登陆后自动显示“该环境由A组负责上线操作前请先联络负责人”冲突就会少很多。这些展示内容本质上都是对“默认空白Shell”的一次体验升级。2. motd不止改文件那么简单动态生成才是完整版motd全称Message of the Day是传统Unix系统就有的机制。你把它理解为“服务器入口处的一块小黑板”就行管理员往小黑板写字用户登录后就能看到。这块黑板的实体就是/etc/motd文件几乎在所有的Linux发行版里都默认存在。2.1 直接改/etc/motd为什么有时会被“恢复”最简单的做法当然是vim /etc/motd然后写入自己的内容。在CentOS/RHEL这类系统上改完立即生效下一个SSH登录的用户就能看到新内容。但在Ubuntu以及部分Debian系发行版里情况不太一样系统里装了一套update-motd机制/etc/update-motd.d/目录下放着很多带数字前缀的脚本比如00-header、10-sysinfo、90-footer这些脚本会在特定时机动态生成motd的展示内容。问题就出在这你手动改了/etc/motd但如果update-motd的脚本执行顺序或输出逻辑覆盖了它等到下一次刷新你写的内容就没了。所以我在Ubuntu上处理静态提示的习惯是要么关闭或清理掉update-motd.d下冗余的脚本要么直接在一个较低的脚本文件里echo想说的话让它成为动态生成的一部分。具体用哪种取决于你希望这块“黑板”是固定内容还是随时更新的内容。2.2 做一个动态系统状态脚本动态motd的真正价值在于它能在每次登录时即时采集系统数据并展示。如果你用的是Ubuntu可以新建一个脚本比如/etc/update-motd.d/10-sysinfo#!/bin/sh echo 当前时间$(date %Y-%m-%d %H:%M:%S) echo 系统负载$(uptime | awk -Fload average: {print $2}) echo 根分区使用$(df -h / | awk NR2 {print $3 / $2 ( $5 )}) echo 内存使用$(free -h | awk /Mem:/ {print 已用 $3 总计 $2})写完后顺手给执行权限sudo chmod x /etc/update-motd.d/10-sysinfo这里有个细节update-motd目录下的脚本必须带可执行权限否则会被静默跳过而且不会报任何错误。我自己就吃过这个亏脚本文件写得好好的权限没加上愣是查了半天才发现是chmod漏了。你可以在登录后执行run-parts /etc/update-motd.d/手动验证一下这个命令会模拟登录时的执行流程。动态motd脚本在非交互场景下也可能被执行比如通过SSH执行远程命令时某些系统配置下会连带输出motd这会对自动化脚本造成干扰。后面我会专门讲这个问题怎么规避。3. SSH Banner在认证之前亮出你的提示motd有个天然局限它要等用户完成认证、进入系统后才能看到。如果登录者是未经授权的入侵者他根本不关心进了系统之后有什么提示。所以对安全声明来说最好把信息提前到认证之前——这就是SSH Banner的用途。3.1 一步步配置Banner配置Banner只需要两步。第一步创建提示文件比如/etc/ssh/banner########################################################### # 注意本服务器为授权系统仅限经许可的用户访问。 # # 所有连接行为均会被记录并可能用于安全审计。 # # 如果你不是目标用户请立即断开连接。 # ###########################################################第二步修改/etc/ssh/sshd_config加入一行Banner /etc/ssh/banner然后重启或重载sshd服务sudo systemctl restart sshd这里提醒一下restart和reload是有区别的。restart会断开所有现有SSH连接再重新拉起服务reload只是在不停连接的情况下重新读取配置。生产环境里为了避免造成正在执行的任务被中断我更推荐先执行sshd -t检查配置语法再用systemctl reload sshd。如果你在修改配置时不小心写错了Banner路径ssh连接会直接失败所以在改ssh相关配置前一定养成先跑sshd -t的习惯。3.2 Banner、motd、profile脚本的分工这三个机制的展示时机和适用场景完全不一样我把它们放进一个表里方便对比机制配置位置显示时机可见范围适用场景SSH Banner/etc/ssh/sshd_config的Banner指令连接建立后、认证前所有尝试连接者安全警告、合规声明motd/etc/motd或update-motd脚本认证通过后、Shell启动前已登录用户公告、系统状态、通知profile脚本/etc/profile.d/下*.shShell启动过程中交互式登录用户个性化提示、权限区分在真实环境里我通常会把三种机制组合使用Banner放最精简的安全提醒一到两行就够motd放系统状态和公共通知profile脚本放轮值人员信息和操作注意事项。这样每类信息的生命周期和敏感程度都能得到合理控制。有一个容易被忽略的点Banner文件的内容是纯文本不解析转义符号。你写的\n会原样显示为两个字符不是换行。所以排版时最好直接在文件里分好行用实际的换行符来分行。另外OpenSSH对Banner的默认长度限制很长但过长的Banner在部分SSH客户端上会显示不全或撑满整个屏幕反而不利于阅读。4. Shell启动脚本给不同用户显示不同内容如果你希望登录提示能做到“见人说人话”比如普通用户看到欢迎语管理员看到系统状态或者根据来源IP判断是否来自办公网并给出不同提示那么前面的motd就满足不了需求了。这时候应该用Shell启动脚本。4.1 登录Shell的加载顺序决定脚本写在哪里下面这行命令可以帮助你判断当前Shell是怎么启动的echo $0输出里带“-”前缀或“bash”的比如-bash说明这是一个登录Shell不带前缀的比如bash说明是交互式非登录Shell。SSH登录默认是登录Shell它会读取/etc/profile。而你在终端里手动再开一个子Shell读取的是.bashrc。所以配置全局自定义提示时更稳妥的做法是把脚本放到/etc/profile.d/目录下。因为这个目录下的所有.sh文件都会被/etc/profile循环加载而且不依赖用户个人配置。另外值得注意的一点是/etc/profile.d/对Bash和Zsh等不同的Shell都有兼容性要求如果脚本里用了某一种Shell特有的语法在另一种Shell下可能报错。为了安全我会用最朴素的POSIX风格语法来写这类公共脚本。4.2 一个区分“交互登录”与“远程命令”的实用脚本一个常见的干扰情况是你用scp传输文件或者执行ssh host ls /tmp这类远程命令时系统如果也刷出motd或profile提示会污染自动化脚本的输出。避开这个问题的思路是利用SSH连接相关的环境变量来进行判断。SSH登录后系统会设置几个变量其中最有价值的是SSH_CONNECTION它记录了你客户端的IP和端口。下面这个脚本放在/etc/profile.d/ssh-info.sh里就能做到只有在真正的交互登录时才输出信息#!/bin/bash # 仅保留交互式SSH会话的信息提示 if [ -n $SSH_CONNECTION ] [ -z $SSH_ORIGINAL_COMMAND ]; then client_ip$(echo $SSH_CONNECTION | awk {print $1}) echo 你好本次连接来自: $client_ip echo 系统提醒: 请勿在未确认变更影响前执行高危操作 fi$SSH_ORIGINAL_COMMAND这个变量非常关键。当你远程执行命令时sshd会把它设置成那条命令当你开启交互登录时它是空的。利用这个组合判断可以精确过滤掉scp、rsync以及ssh host command这类非交互场景让它们干干净净地执行不被多余输出干扰。另外如果你希望管理员看到的状态信息比普通用户更多可以在脚本里判断用户身份if [ $(id -u) -eq 0 ]; then echo 管理员登录当前未处理告警: 0 fi这种判断并不是什么高深技术但在多人共用的服务器上确实能提升体验。我自己维护的一台跳板机上普通用户只会看到一行欢迎语管理员登录时会额外显示最近一周的登录失败记录这个功能就是靠几行简单的Shell判断实现的。5. 常见问题排查实录改了不生效、显示乱码、干扰脚本前面讲的都是怎么做现在重点说说我实际踩过的坑。配置登录提示看上去很简单但牵扯到PAM、sshd、Shell启动这几个系统机制稍有不慎就会出现“改了没反应”或“反应太大”的问题。5.1 为什么改完motd就是不显示这是被问得最多的问题。现象是vim /etc/motd写入内容后登录时要么还是旧内容要么什么都没了。排查路径按下面这个顺序来最省时间先看客户端是否跳过了motd如果用户家目录存在~/.hushlogin文件OpenSSH会静默跳过motd显示。这不分发行版很多老手都会忘记这个坑。再看是否是update-motd覆盖Ubuntu系统优先检查/etc/update-motd.d/如果你的系统启用了这类机制手动改/etc/motd基本是无效的。正确做法是在动态脚本里echo你想要的内容。最后检查sshd的PrintMotd和PAM配置执行grep -E PrintMotd|UsePAM /etc/ssh/sshd_config如果UsePAM设置为yes那么motd的最终控制权在pam_motd模块手里你需要检查/etc/pam.d/sshd里有没有pam_motd.so相关行。我遇到过一种很隐蔽的情况客户端是Windows下的老版本PuTTY服务器配置没问题但客户端设置里勾选了“没有motd”导致本地完全不显示。所以排查的时候也别只盯着服务器端换个客户端试试往往能快速定位问题。5.2 中文乱码和颜色控制有不少人喜欢在motd或Banner里加入一些比较醒目的样式比如用figlet生成大号ASCII字或者添加ANSI颜色转义。这时候最常遇到的就是中文乱码问题。服务器端默认的语言环境如果不是UTF-8而你的Banner文件又是用UTF-8保存的SSH连接时终端很可能就会显示出乱码。最简单的解决办法有两个一是文本内容尽量用英文或纯ASCII字符二是在服务器上统一设置语言环境为UTF-8比如在/etc/profile.d目录下加入一行export LANGen_US.UTF-8 export LC_ALLen_US.UTF-8如果你特别想用颜色高亮某行内容可以写成这样echo -e \033[31m注意高危操作需要提前审批\033[0m这里的\033[31m是红色起始标记\033[0m是颜色重置。要注意echo命令需要加上-e参数才能解析转义如果你在脚本里漏了-e那这行就会把原始转义序列整个打印出来效果很丑。5.3 自动化场景下被刷屏的处理SSH远程命令或scp传输时如果也收到motd或profile输出会导致脚本解析出错。例如你的备份脚本执行ssh backup-server tar -czf backup.tar.gz /data结果返回内容前面多了几行系统提示你的tar解压逻辑就可能报错。处理方案我在前面提到过在展示逻辑里判断SSH_ORIGINAL_COMMAND变量。但这是针对profile脚本的对motd的处理方式不太一样。motd是sshd在分配tty时才输出的远程执行命令时默认没有tty所以通常不会刷屏。但如果你远程执行命令时加了-t参数强制分配tty那motd和profile脚本都会执行这就要格外小心。还有一个偏门但实用的技巧远程命令前加上unset MOTD或使用ssh -T有时也能绕开tty相关的动效输出。我的建议是自动化脚本里统一使用ssh -T或明确不带-t同时给scp加上-q参数基本可以避免98%的刷屏问题。问题快速定位方法解决办法motd不显示检查.hushlogin和PAM配置删除.hushlogin检查pam_motdmotd被覆盖检查是否启用update-motd修改/清理/etc/update-motd.d/脚本Banner不显示sshd -t检查语法并reload确认Banner路径和sshd_config生效中文乱码检查LANG和文件编码使用UTF-8或改成纯英文自动化脚本被刷屏ssh -T或scp -q测试设置SSH_ORIGINAL_COMMAND判断5.4 安全提示到底应该写什么最后聊一个看起来是文案问题、实际上影响很大的话题。SSH Banner因为是认证前就展示给所有人的所以内容必须精挑细选。我见过有些公司直接把IP地址、机房名称、系统版本写进Banner这等于提前给攻击者递了情报。一段合适的Banner应该包括三个部分授权声明你是谁才能登录、监控声明操作会被记录、简称或名称便于误连接者识别目标。不要出现主机名、具体IP、Tomcat/Nginx版本、操作系统内核版本等内容。motd里的信息判断标准可以放宽一些因为它只在认证后出现但同样不建议把数据库连接地址、应用端口这类内网信息直接打在欢迎页上——毕竟不是每个登录者都有权限看到这些东西运维最小暴露原则在这里同样适用。配置完这些之后我还强烈建议你每次都做一次“登录演练”用一个新SSH连接进去检查Banner顺序、motd内容、脚本输出是否都符合预期再执行一次ssh host echo ok确认自动化脚本没被多余输出打扰。这套流程跑一遍比任何教程里的“检查清单”都更有效因为它把交互、远程命令、文件传输这三类最常用的SSH场景全部覆盖了。
返回列表