良许Linux教程网 干货合集 通过使用 Shell 轻松搞定 Linux 命令审计

通过使用 Shell 轻松搞定 Linux 命令审计

利用定制 Bash 源增加日志审计功能,并将用户操作发给 rsyslog 聚合,最后在 elasticsearch 做日志存储和查询。

使用 Shell 轻松搞定 Linux 命令审计使用 Shell 轻松搞定 Linux 命令审计

Pixelated word Linux made from cubes, mosaic pattern

首先,当谈到 Linux 的操作审计需求时,大多数我们希望的是还原线上服务器被人为(误)操作时执行的命令行,以及它关联的上下文。这个需求场景其实跟通用的业务日志采集一致,简单一点可以直接通过 history 将内容发给 syslog,复杂一点的采用 auditd 或 ebpf 在内核层面上捕获行为。

不过本文不打算对上述的方案做原理解释,仅仅站在一个运维小白的角度来完成日常 80%(80%的数据来源?我也不知道,大概是二八原则)的操作审计 。既然文章标题是用 Shell 来完成, 由此可见今天的主题跟 Bash 脱不了关系了。

一句话概括今天的主题:利用定制 Bash 源增加日志审计功能,并将用户操作发给 rsyslog 聚合,最后在 elasticsearch 做日志存储和查询。

Linux 部分

  1. 准备一些必要的工具

    • rsyslog: 一个Linux上自带并兼容 syslog 语法的日志处理服务
    • jq: 一个在 shell 下处理 json 数据的小工具
    • logger: 一个可以往 syslog 输入日志的工具
  2. 这些小工具除 jq 外,大多操作系统发行版都自带,如果没有的话也可以直接用操作系统内置的包管理工具安装。

  3. ash.audit.sh,并将其拷贝到 /etc/profile.d/ 目录下

  4. if [ "${SHELL##*/}" != "bash" ]; then
     return
    fi
    if [ "${AUDIT_READY}" = "yes" ]; then
       return
    fi
    declare -rx HISTFILE="$HOME/.bash_history
    declare -rx HISTSIZE=500000
    declare -rx HISTFILESIZE=500000
    declare -rx HISTCONTROL=""
    declare -rx HISTIGNORE=""
    declare -rx HISTCMD
    declare -rx AUDIT_READY="yes"
    shopt -s histappend
    shopt -s cmdhist
    shopt -s histverify
    if shopt -q login_shell && [ -t 0 ]; then
     stty -ixon
    fi
    if groups | grep -q root; then
     declare -x TMOUT=86400
     # chattr +a "$HISTFILE"
    fi
    declare -a LOGIN_INFO=( $(who -mu | awk '{print $1,$2,$6}') )
    declare -rx AUDIT_LOGINUSER="${LOGIN_INFO[0]}"
    declare -rx AUDIT_LOGINPID="${LOGIN_INFO[2]}"
    declare -rx AUDIT_USER="$USER"
    declare -rx AUDIT_PID="$$"
    declare -rx AUDIT_TTY="${LOGIN_INFO[1]}"
    declare -rx AUDIT_SSH="$([ -n "$SSH_CONNECTION" ] && echo "$SSH_CONNECTION" | awk '{print $1":"$2"->"$3":"$4}')"
    declare -rx AUDIT_STR="$AUDIT_LOGINUSER  $AUDIT_LOGINPID  $AUDIT_TTY  $AUDIT_SSH"
    declare -rx AUDIT_TAG=$(echo -n $AUDIT_STR | sha1sum |cut -c1-12)
    declare -x AUDIT_LASTHISTLINE=""
    set +o functrace
    shopt -s extglob
    function AUDIT_DEBUG() {
     if [ -z "$AUDIT_LASTHISTLINE" ]; then
       local AUDIT_CMD="$(fc -l -1 -1)"
       AUDIT_LASTHISTLINE="${AUDIT_CMD%%+([^ 0-9])*}"
     else
       AUDIT_LASTHISTLINE="$AUDIT_HISTLINE"
     fi
     local AUDIT_CMD="$(history 1)"
     AUDIT_HISTLINE="${AUDIT_CMD%%+([^ 0-9])*}"
     if [ "${AUDIT_HISTLINE:-0}" -ne "${AUDIT_LASTHISTLINE:-0}" ] || [ "${AUDIT_HISTLINE:-0}" -eq "1" ]; then
       MESSAGE=$(jq -c -n \
        --arg pwd "$PWD" \
        --arg cmd "${AUDIT_CMD##*( )?(+([0-9])?(\*)+( ))}" \
        --arg user "$AUDIT_LOGINUSER" \
        --arg become "$AUDIT_USER" \
        --arg pid "$AUDIT_PID" \
        --arg info "${AUDIT_STR}" \
        '{cmd: $cmd, user: $user, become: $become, pid: $pid, pwd: $pwd, info: $info}')
       logger -p local6.info -t "$AUDIT_TAG" "@cee: $MESSAGE"
     fi
    }
    function AUDIT_EXIT() {
     local AUDIT_STATUS="$?"
     if [ -n "$AUDIT_TTY" ]; then
       MESSAGE_CLOSED=$(jq -c -n \
           --arg action "session closed" \
           --arg user "$AUDIT_LOGINUSER" \
           --arg become "$AUDIT_USER" \
           --arg pid "$AUDIT_PID" \
           --arg info "${AUDIT_STR}" \
           '{user: $user, become: $become, pid: $pid, action: $action, info: $info}')
       logger -p local6.info -t "$AUDIT_TAG" "@cee: $MESSAGE_CLOSED"
     fi
     exit "$AUDIT_STATUS"
    }
    declare -frx +t AUDIT_DEBUG
    declare -frx +t AUDIT_EXIT
    if [ -n "$AUDIT_TTY" ]; then
     MESSAGE_OPENED=$(jq -c -n \
         --arg action "session opened" \
         --arg user "$AUDIT_LOGINUSER" \
         --arg become "$AUDIT_USER" \
         --arg pid "$AUDIT_PID" \
         --arg info "${AUDIT_STR}" \
         '{user: $user, become: $become, pid: $pid, action: $action, info: $info}')
     logger -p local6.info -t "$AUDIT_TAG" "@cee: $MESSAGE_OPENED"
    fi
    declare -rx PROMPT_COMMAND="[ -n "$AUDIT_DONE" ] && echo ''; AUDIT_DONE=; trap 'AUDIT_DEBUG && AUDIT_DONE=1; trap DEBUG' DEBUG
    declare -rx BASH_COMMAND
    declare -rx SHELLOPT
    trap AUDIT_EXIT EXIT
    
  5. 简单说明下这个脚本,大致就是定义了 shell 的历史条目、登录超时时间、以及审计日志的格式和发送。

  6. 配置rsyslog 客户端,本地创建一个 /etc/rsyslog.d/40-audit.conf 文件,用于将本地local6级别的系统日志发送远端的rsyslog服务集中处理

  7. $RepeatedMsgReduction off
    local6.info @:514
    & stop
    
  8. 配置完成后,别忘了重启下 rsyslog 服务!

  9. 数据部分

  10. 数据部分顾名思义,用于接收并处理客户端发来的操作系统日志。这里我们用到了 rsyslog 和 elasticsearch 两个服务了。

  11. 准备rsyslog-elasticsearch

  12. 要让 rsyslog 将日志发送给 elastichsearch,我们就必须安装它的 es 模块

  13. # Ubuntu  
    apt-get install -y rsyslog-elasticsearch rsyslog-mmjsonparse  
    #CentOS
    yum install rsyslog-elasticsearch rsyslog-mmjsonparse
    
  14. 准备 ElasticSearch 服务

  15. 为了简单部署,本文直接用 docker 快速拉起一个 ES 服务

  16. docker run -d --name elasticsearch  -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.3.1
    
  17. 配置 rsyslog 服务端,创建一个文件 /etc/rsyslog.d/40-audit-server.conf,用于定义日志的写入策略。

  18. $RepeatedMsgReduction off
    $ModLoad imudp
    $UDPServerRun 514
    module(load="mmjsonparse")          # for parsing CEE-enhanced syslog messages
    module(load="omelasticsearch")      # for outputting to Elasticsearch
    #try to parse a structured log
    # this is for index names to be like: rsyslog-YYYY.MM.DD
    template(name="rsyslog-index" type="string" string="bashaudit-%$YEAR%.%$MONTH%.%$DAY%")
    # this is for formatting our syslog in JSON with @timestamp
    template(name="json-syslog" type="list") {
       constant(value="{")
         constant(value="\"@timestamp\":\"")     property(name="timegenerated" dateFormat="rfc3339" date.inUTC="on")
         constant(value="\",\"host\":\"")        property(name="fromhost-ip")
         constant(value="\",\"severity\":\"")    property(name="syslogseverity-text")
         constant(value="\",\"facility\":\"")    property(name="syslogfacility-text")
         constant(value="\",\"program\":\"")     property(name="programname")
         constant(value="\",\"tag\":\"")         property(name="syslogtag" format="json")
         constant(value="\",")                   property(name="$!all-json" position.from="2")
       # closing brace is in all-json
    }
    if ($syslogfacility-text == 'local6' and $syslogseverity-text == 'info'then {
    action(type="mmjsonparse")
    action(type="omelasticsearch" template="json-syslog" searchIndex="rsyslog-index" dynSearchIndex="on" server="" serverport="")
           # action(type="omfile" file="/var/log/bashaudit.log")
           stop
    }
    
  19. 这里采用了 rsyslog 的两个 module 来处理收集的日志

  20. mmjsonparse用于 json 格式化日志

  21. omelasticsearch用于配置 ElastichSearch

  22. 配置完成重启 rsyslog 服务

  23. 查询部分

  24. 审计日志的查询我们可以使用 Kibana 或者自己根据 ElasticSearch API 进行二次开发。这里我们以 Kibana 举例。

  25. cat  ./kibana.yml
    server.port: 15601
    elasticsearch.hosts: ["http://:"]
    i18n.locale: "zh-CN"
    EOF
    docker run -d --ulimit nofile=1000000:1000000 --net host --name elasticsearch-audit -v ./kibana.yml:/usr/share/kibana/config/kibana.yml  --restart always docker.elastic.co/kibana/kibana-oss:7.3.1
    
  26. 本地访问http://localhost:15601进入 kibana 配置创建一个名为 bashaudit 的索引模式

  27. 使用 Shell 轻松搞定 Linux 命令审计使用 Shell 轻松搞定 Linux 命令审计
  28. 之后,我们就能进入 Discover 中查询审计日志了,包含了基本Shell执行时间、来源用户、执行目录等数据。

  29. 使用 Shell 轻松搞定 Linux 命令审计使用 Shell 轻松搞定 Linux 命令审计
  30. 再进一步,我们也可以通过调用 API 的方式对审计日志做一些额外的二次开发,例如:

  31. 对线上服务器热点用户统计

  32. 对线上服务器做热点操作统计

  33. 对线上危险Shell 操作做告警

  34. 使用 Shell 轻松搞定 Linux 命令审计使用 Shell 轻松搞定 Linux 命令审计
    使用 Shell 轻松搞定 Linux 命令审计使用 Shell 轻松搞定 Linux 命令审计
  35. 总结

  36. 本文讲述了采用定制 Bash 的方式,在用户登录初始化 Shell 的方式将其后续的命令行操作发送给 rsyslog 服务进行处理,并将格式化后的日志存储在 ElasticSearch 中方便辅助系统管理者在线上故障定位时使用,也可以依此对 Linux命令行审计做可视化的二次开发。

    不过本文基于定制 Bash 的方式仍然具备很多局限性,例如:

    不能审计 ShellScript 内的执行逻辑;

    存在用其他 shell 绕过审计,如 zsh 等;

    可以看到要想审计到更详细的内容,光在 Bash(表面功夫)上实现并不能满足,读者可以尝试使用snoopy 对 Shell 脚本内部做跟踪审计。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部