我们遇到了这样一种情况,将日志目录的写访问权限限制为用户进程的特定子集是有利的。这些特定进程(例如,telnet 等)已被我们修改,以便在发生重大用户操作(如远程连接等)时生成日志记录。我们不希望用户通过复制和编辑现有日志记录来手动创建这些记录。
syslog
接近但仍然允许用户生成虚假记录,SELinux
似乎合理但作为难以管理的野兽而臭名昭著。
任何见解都值得赞赏。
我们遇到了这样一种情况,将日志目录的写访问权限限制为用户进程的特定子集是有利的。这些特定进程(例如,telnet 等)已被我们修改,以便在发生重大用户操作(如远程连接等)时生成日志记录。我们不希望用户通过复制和编辑现有日志记录来手动创建这些记录。
syslog
接近但仍然允许用户生成虚假记录,SELinux
似乎合理但作为难以管理的野兽而臭名昭著。
任何见解都值得赞赏。
以 root 身份运行本地日志记录守护程序。让它监听一个 Unix 域套接字(通常/var/run/my-logger.socket
或类似的)。
编写一个简单的日志库,其中事件消息通过 Unix 域套接字发送到本地运行的守护进程。对于每个事件,还通过辅助消息发送流程凭据。有关详细信息,请参阅man 7 unix。
当本地日志守护进程收到一条消息时,它会检查辅助消息,如果没有,则丢弃该消息。凭据的uid
和gid
准确地告诉谁在运行发送日志请求的进程;这些是由内核本身验证的,所以它们不能被欺骗(除非你有 root 权限)。
聪明的地方来了:守护进程还检查PID
凭据中的 ,并根据其值,/proc/PID/exe
. 它是发送消息的进程正在执行的实际进程二进制文件的符号链接,这是用户无法伪造的。为了能够伪造消息,他们必须用自己的二进制文件覆盖实际的二进制文件,这需要 root 权限。
(有一个可能的竞争条件:用户可以制作一个执行相同操作的特殊程序,并立即exec()
生成他们知道允许的二进制文件。为了避免这种竞争,您可能需要让守护程序在检查凭据后做出响应,并且日志客户端发送另一条消息(带有凭据),因此守护进程可以验证凭据仍然相同,并且/proc/PID/exe
符号链接没有更改。我个人会使用它来检查消息的真实性(通过记录器要求确认事件,使用随机 cookie,并让请求者用校验和和 cookie 来响应事件校验和是否正确。包括随机 cookie 应该使之前不可能在套接字队列中填充确认exec()
。)
使用 ,pid
您还可以进行进一步检查。例如,您可以通过跟踪父进程来跟踪进程父级以查看人类用户如何连接,直到您通过 ssh 或控制台检测到登录。这有点乏味,因为您需要解析/proc/PID/stat
或/proc/PID/status
文件,并且不可移植。OSX 和 BSD 有一个 sysctl 调用,可用于查找父进程 ID,因此您可以parent_process_of(pid_t pid)
通过编写特定于平台的函数使其可移植。
这种方法将确保您的日志守护进程确切知道 1) 日志请求来自哪个可执行文件,以及 2) 哪个用户(以及如何连接,如果您进行进程跟踪)运行了该命令。
由于本地日志守护程序以 root 身份运行,它可以将事件记录到仅根目录中的文件,和/或将消息转发到远程机器。
显然,这并不完全是轻量级的,但假设您每秒处理的事件少于十二个,那么日志记录开销应该完全可以忽略不计。
通常有两种方法可以做到这一点。一,运行这些进程root
并写保护目录(主要出于历史目的提及)。然后除了root之外没有人可以在那里写。第二个也是更安全的方法是将它们作为另一个用户(不是root
)运行并授予该用户(但没有其他人)对日志目录的写入权限。
我们采用的方法是使用 setuid 二进制文件来允许对日志目录进行写访问,该二进制文件可由所有用户执行,但仅当父进程路径由 /proc/$PPID 定义时才允许写入日志记录/exe 匹配我们放置在系统上的修改后的二进制路径的子集。