如何审计 ssh 会话操作
目前从命令行终端操作方面, 主要可以通过以下两种方式记录 ssh 会话的操作, 不过这些方式都存在固有的缺点, 难以达到审计的目的. 如下所示:
1. 终端工具日志
这种方式主要依赖终端工具(比如 securecrt, iterm2) 的日志功能, 其可以记录会话的内容并保存到本地机器的文件中. 不过这种方式强依赖使用者的自觉性, 比较严重的情况下, 一些使用者可能删掉本地日志来掩盖自身失误的操作记录.
2. snoopy 记录
snoopy 通过封装 exec, execve 系统调用来记录所有的历史操作, 很方便操作方面的问题排查. 不过其没有会话一致性的功能, 如果 ssh 登录多台机器就很难将单人的操作串起来, 如果大部分操作都是 root 用户, 要跟踪个人的操作, 就需要做很多日志文件的对比筛查工作.
基于上面的两个缺点, 我们可以在登录的跳板机中增加 ovh-ttyrec 工具, 记录个人所有的会话操作(包括多级联的 ssh 登录). 目前可以采用云厂商 ovh 发布的 ttyrec 分支版本, 其增加了会话管理功能, 因为有其它产品依赖该工具, 所以相对活跃, 存在一些问题的话, 也能相对好处理.
不过上述的三种方式, 都可能多少记录了敏感信息(系统, db 密码等), 处理日志文件的时候一定要注意数据安全. 下面简单介绍如何使用 ovh-ttyrec 工具.
如何部署
rpm 安装
ttyrec 工具目录仅支持 Linux/Unix 系统, 可以参考下载官方的 ovh-ttyrec_download 进行安装, 其可适用于大部分平台.
创建共享目录
需要在跳板机中以 root 用户创建一个目录, 并赋予 777 权限, 方便所有的用户集中将 ttyrec 文件写到一起. 如下所示:
mkdir /data/tty_record
chmod 777 /data/tty_record
修改 sshd 配置, 强制执行 ttyrec 命令
在 /etc/ssh/sshd_config 文件的 Match User 部分增加一下配置, 强制用户登录时执行 tty_record 脚本, 以下配置忽略了 root 用户:
Match User *,!root
ForceCommand /usr/local/bin/tty_record
tty_record 脚本见以下示例, 目录 /export/tty_record
需要提前创建好, 并赋予 777 权限, 让所有用户可写:
#!/bin/bash
WHO="$(whoami)"
WHO="{$WHO:-"none"}"
[[ $WHO != "root" ]] && {
if tty -s; then
ttyrec -t 3600 -k 7200 -z $WHO -Z -d /export/tty_record
fi
}
上述配置中, 忽略了 root 用户, 另外几个选项说明如下:
-t 3600 当前会话超过 1 小时空闲, 则加锁该会话, 加锁的会话可以通过 USR2 信号解锁;
-k 7200 当前会话超过 2 小时空闲, 则 kill 当前会话;
-z string 生成的文件默认加上当前用户名后缀, 建议开启, 跳板机的磁盘都比较小;
-Z 生成的文件启用 zstd 压缩, ttyplay 也会自动识别压缩;
-d string 生成的文件都放在指定的目录中, 默认为用户 home 目录;
重启 sshd
配置完成后, 需要重启 sshd 服务以便生效.
如何使用及查看会话日志
如何解锁
以上述配置为例, 会话空闲超过 1 小时则被锁, 超过 2 小时才会被 kill, 这即说明在 kill 之前, 我们可以执行解锁会话的操作. 解锁会话需要单独再登录一个会话, 给被锁的会话发送 SIGUSR2 信号, 如下所示:
pkill -USR2 ttyrec # 给所有匹配 ttyrec 信息的进程发送 USR2 信号, 未锁的会话不受影响.
日志文件
在设置完 ssh 的 ForceCommand 后, 用户登录的时候, 会在指定的目录中生成 ttyrec 结果的日志文件, 如果开启了 zstd 压缩, 会生成 zst 结果的文件. 如下所示:
$ ls -hl /export/tty_record
......
-rw-rw-r-- 1 arstercz arstercz 147 Jan 10 02:57 2022-01-10.02-57-19.705167.cz.ttyrec.zst
-rw-rw-r-- 1 arstercz arstercz 1.9K Jan 10 03:28 2022-01-10.02-58-29.293751.cz.ttyrec.zst
查看日志会话记录
可以通过 ttyplay 工具, 以指定倍速(-s 选项)重新播放 ttyrec 文件. 在播放的过程中也可以通过 +/- 符号加速减速播放.
如何导出日志
可以通过 ttyplay -n 的方式导出 ttyrec 文件的内容到指定文本文件:
ttyplay -n -Z 2022-01-10.02-57-19.705167.cz.ttyrec.zst > /tmp/cz.session.log
备注: ttyrec 文件中本身包含了时间信息, 文本高亮(如果 ssh 会话包含颜色显示), 导出的文件会包含一些特舒服和颜色字符. 不过通过 cat 等方式也可以正常显示. 一些命令的操作也可以通过 grep 进行处理. 另外也可以通过工具 sys-rmcolor 进行处理.
统计日志
上述的配置中, 统一包含了时间, 用户名等信息, 在统计的时候, 也可以通过 ttytime 额外查看 ttyrec 文件包含了多长时间的内容:
$ ttytime 2022-01-10.02-23-52.068179.cz.ttyrec
391 2022-01-10.02-23-52.068179.cz.ttyrec # 共计 391 秒时长
日志文件的大小
从原理来看, ttyrec 记录的文件大小和时间长短没有绝对的关系. 目前文件大小主要受以下因素制约:
1. 会话输出多, 比如用户执行了查看文件的操作, 或者脚本 debug 打印了很多日志;
2. 交互会话多, 比如长时间的 top, db, redis 等命令操作;
3. 执行了 rz/sz 等操作, 文件越大, 对应的日志就越大;
基于这些因素, ttyrec 建议开启 -Z 压缩减少对磁盘的占用.
ttyplay 中断播放
一些会话可能包含 rz/sz
的命令, 这些命令在通过 ttyplay
播放的时候会出现交互页面, 比如播放 rz 的时候, 如果你的终端(securecrt, iterm 等)支持 Zmodem
, 则会弹出窗口让你选择文件列表. 这点像是 ttyplay 没有正确处理 Zmodem 协议. 如果终端禁止 Zmodem 协议, sz/rz 也会等待直到命令超时才会播放下一步骤.
目前没有好的方法, 可以通过 ttyplay 的 -n
选项, 先导出 ttyrec 文件的内存, 再通过 less 等命令查看. 也可以找找开源的 web 端播放工具, 可能解决了此类问题.
lshell 下如何使用
lshell 类似特定的 shell, 如果用户的 shell 环境被修改为 lshell
, 在一开始登录的时候就会进入 lshell 会话, 再去执行 ttyrec 之类的命令, 但是 ttyrec 命令本身需要在当前 shell 中继续加载 shell 会话, 如下所示, ttyrec 需要继续调用 lshell,
ut1:~$ ttyrec
/usr/bin/lshell: Permission denied
ttyrec: aborting!
从 lshell 的实现来看, 底层的很多关联(比如 exec, bash) 等都是严格限制, 工具或者脚本依赖的一些命令或调用都不一定可以通过, 所以只能从 先执行 ttyrec 启动新会话, 再执行 lshell 限制会话
的方式来解决, 如下所示, 用户还是 bash shell
, 在 sshd_config
中执行以下命令:
ttyrec -t 3600 -k 7200 -Z -d /export/tty_record -- sh -c 'lshell'
ttyrec 的缺点
通过 ssh 的 ForceCommand 方式可以让使用者无感的登录跳板机. 不过 ttyrec 本质上还是在 ssh 登录后又起了一个子进程, 所以以下操作不会成功:
1. 从其它机器 scp 文件到跳板机, 会出现 lost connection 的情况;
2. 从其他机器 rsync(以 ssh 方式) 文件到跳板机, 会出现 lost connection 的情况;
如果一定要传文件, 需要提前做好上传文件的通路, 比如在跳板机启动 rsync 服务, 或者提供统一的上传下载服务.
另外, ttyrec 目前仅适用于 Linux/Unix 系统, 如果需要连接 windows 等机器, 则无能为力, 只能借助其他工具, 比如 Apache 项目 Guacamole 提供了通用的远程管理协议, 通过它可以统一管理基于 VNC, RDP(windows 系统等), 和 SSH 协议的会话. 如果需要统一所有系统的登录管理, 可以在 Guacamole 项目的基础上做更多的定制. 现成的项目可以参考 next-terminal.
其他替换工具
另一个工具 log-user-session, 提供了和 ttyrec
类似的功能, 但不支持录屏, 仅以文本文件的方式存储会话内存, 等同 ttyplay -n xxxx
后的结果, 不过从易用性来看不如 ttyrec 方便, 而且不好和 lshell 结合, 但是该工具不会启动子会话, scp, rsync 等命令不受影响. 主要的几个配置选项说明如下:
LogFile = /export/user_session/%h-%u-%y%m%d-%H%M%S-%c-%p.log # 格式见文档说明
LogRemoteCommandData = 1 # remote command 对应 SSH_ORIGINAL_COMMAND 环境变量, 比如 'ssh host1 uptime', uptime 即为 SSH_ORIGINAL_COMMAND 的值
LogNonInteractiveData = 1 # 是否记录非交互会话, 比如 'ssh host1 uptime' 即为非交互方式执行命令
NonInteractiveCommandWhitelist = rsync,ps,uptime # 非交互会话命令白名单, 在名单里的只记录命令, 不记录输出结果
上述三个容易混淆的选项, 可以从源代码中详细区分, 只有返回 1 才会记录命令的输出:
544 int should_log_data(int interactive, const char *original_command) {
545 if (!interactive && !opt_log_non_interactive_data) return 0;
546 if (!interactive && *whitelist_size && is_command_whitelisted(original_command)) return 0;
547 if (original_command && !opt_log_remote_command_data) return 0;
548 return 1;
549 }
更多说明见: log-user-session_doc.
如何去除文本中的颜色和控制字符
参见工具 sys-rmcolor.