Mobile wallpaper
1578 字
8 分钟

关于 Docker Gitea 非 rootless 版无法获取SSH log信息解决办法

2025-11-03
浏览量 加载中...
本文主要内容

非rootless版本(也就是默认的gitea/gitea:latest无法获取到SSH登录日志信息,哪怕设置ENABLE_SSH_LOGtrue也无济于事。本文介绍一种解决办法,通过获取Docker容器的日志文件来实现SSH log的记录,从而可以进行fail2ban的配置

Gitea在部署标准版的情况下,无论你如何配置app.ini都无法获取到SSH登录日志信息,主要原因是Gitea容器内并没有sshd服务,所以无法获取到相关日志信息。官网的描述很简单的,只需要改下 app.inilog 部分下的 MODEfile,并开启 ENABLE_SSH_LOG,但是请放心做无用功,哪怕如此修改后,gitea.log 依旧不会有 SSH 相关的log,因为ENABLE_SSH_LOG 仅对 Gitea 内建 SSH 有效,抱歉的是,标准版中的 OpenSSH 不可与内建 SSH 调和。同时在其 github 存在一个 issue ,回复是“如果你没用它自带的 SSH,凭什么指望 Gitea 的日志会提供那些信息?直接设置 fail2ban 去用 sshd 的日志就行了”。 但是,天不绝人之路,通过docker compose logs -f gitea查看可以发现,docker logs内是有SSH相关的爆破信息的,因此解决方法就很简单了,定时将docker logs内的SSH信息提取出来,写入到一个单独的log文件中,然后让fail2ban去监控这个log文件即可。

部署流程#

定时提取SSH log#

  1. 创建脚本文件
    Terminal window
    nano ~/docker_data/gitea/gitea_docker_log.sh && chmod +x ~/docker_data/gitea/gitea_docker_log.sh
    gite_docker_log.sh
    #!/bin/bash
    # --- 配置 ---
    # Gitea 容器的名称
    CONTAINER_NAME="gitea"
    # 日志文件在宿主机上的完整路径
    LOG_FILE="/root/docker_data/gitea/gitea.log"
    # --- 脚本主体 ---
    LOG_DIR=$(dirname "$LOG_FILE")
    # 确保日志目录存在
    mkdir -p "$LOG_DIR"
    echo "脚本已启动,将监控容器 '$CONTAINER_NAME' 并将日志写入 '$LOG_FILE'"
    # 无限循环,确保在容器重启后能自动重新连接
    while true; do
    # 使用 docker inspect 检查容器是否存在且在运行,这比解析 ps 输出更可靠
    if docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "true"; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 已连接到 '$CONTAINER_NAME' 容器日志流..."
    # 使用 docker logs 持续跟踪日志(只取过去15秒的日志),并将标准错误(2)也重定向到标准输出(1)
    # 然后通过 sed 高效地格式化时间戳,最后追加到日志文件
    # sed -u: 使用无缓冲模式,确保日志实时写入
    # s/T/ /: 将日期和时间之间的 'T' 替换为空格
    # s/\..*Z$//: 删除小数点及其后面的所有内容,即毫秒和'Z'
    docker logs --timestamps --follow "$CONTAINER_NAME" 2>&1 | \
    sed -u 's/T/ /;s/\..*Z$//' >> "$LOG_FILE"
    # 如果 docker logs 命令退出 (通常是因为容器停止了),会执行到这里
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 与 '$CONTAINER_NAME' 的日志流断开。正在重试..."
    else
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 容器 '$CONTAINER_NAME' 未运行或不存在,等待 10 秒..."
    fi
    # 短暂休眠,避免在容器不存在时消耗过多 CPU
    sleep 10
    done
  2. 创建systemd服务
    Terminal window
    nano /etc/systemd/system/gitea_docker_log.service
    gitea_docker_log.service
    [Unit]
    Description=Gitea Log Monitor
    After=docker.service
    Requires=docker.service
    [Service]
    ExecStart=/root/docker_data/gitea/gitea_log_monitor.sh
    Restart=always
    User=root
    [Install]
    WantedBy=multi-user.target
  3. 启动服务
    Terminal window
    systemctl daemon-reload
    systemctl enable gitea_docker_log.service
    systemctl start gitea_docker_log.service
  4. 检查服务状态
    Terminal window
    systemctl status gitea_docker_log.service

配置fail2ban监控SSH log#

不要使用官方的过滤规则,根本就是乱写,完全抓不到任何一个爆破行为,下面是我实际使用的过滤规则

  1. 创建fail2ban过滤器
    nano /etc/fail2ban/filter.d/gitea.conf
    filter.d/gitea.conf
    [Definition]
    failregex = ^.*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST> .*
    ^.* Invalid user .* from <HOST> .*
    ^.* <HOST> not allowed because not listed in AllowUsers.*
    ^Disconnected from invalid user .* <HOST> port.*
    ^Connection closed by invalid user .* <HOST> port.*
    ^Timeout before authentication for connection from <HOST> .*
    ignoreregex =
  2. 创建fail2ban jail
    Terminal window
    nano /etc/fail2ban/jail.d/gitea-docker.local
    jail.d/gitea-docker.local
    [gitea-docker]
    # 是否启用此防护规则。true 表示启用。
    enabled = true
    # [关键] 指定 iptables 防火墙规则应该应用在哪条链上。
    # 对于 Docker 容器,所有外部到容器的流量都经过宿主机的 FORWARD 链。
    # 因此,必须设置为 FORWARD 才能正确地封禁访问 Docker 容器的 IP。
    chain = FORWARD
    # 指定用于解析日志文件的过滤器(filter)。
    # "gitea" 对应于 /etc/fail2ban/filter.d/gitea.conf 文件。
    # 这个过滤器定义了如何从日志中识别出失败的登录尝试(例如,通过正则表达式匹配错误信息)。
    filter = gitea
    # [关键] 指定要监控的日志文件的完整路径。
    # 这里指向的是你在宿主机上,通过脚本收集并整合的 Gitea 容器日志。
    # Fail2Ban 将会实时读取这个文件来检测攻击行为。
    logpath = /root/docker_data/gitea/gitea.log
    # [安全策略] 最大重试次数。
    # 在 `findtime` 定义的时间窗口内,如果同一个 IP 地址的失败尝试次数达到 3 次,
    # Fail2Ban 就会触发封禁动作。这是一个比较严格的设置,能快速响应攻击。
    maxretry = 3
    # [安全策略] 观察时间窗口。
    # Fail2Ban 只会计算最近 10 分钟内的失败次数。
    # 例如,一个 IP 在 10:00 失败了 2 次,在 10:11 又失败了 1 次,这不会触发封禁,
    # 因为在任何一个 10 分钟的窗口内,失败次数都没有达到 3 次。可以自行改大
    findtime = 10m
    # [安全策略] 封禁时长。
    # 一旦一个 IP 被封禁,它将在 146 小时(约 6 天)内无法访问任何服务。
    # 这是一个长期封禁,能非常有效地阻止持续的暴力破解或扫描攻击。
    bantime = 146h
    # [安全策略] 封禁动作。
    # 定义当触发封禁时,具体执行什么操作。
    # "iptables-allports" 是一个预定义的动作,会使用 iptables 封禁该 IP 的所有端口。
    # "blocktype=DROP" 是传递给这个动作的参数,指定封禁模式为 DROP。
    # DROP 模式会直接丢弃来自该 IP 的数据包,让攻击者连接超时,比 REJECT (拒绝) 更隐蔽。
    banaction = iptables-allports[blocktype=DROP]
  3. 重启fail2ban服务
    Terminal window
    systemctl restart fail2ban
  4. 检查fail2ban状态
    Terminal window
    fail2ban-client status && fail2ban-client status gitea-docker
  5. 配置logrotate服务
    Terminal window
    nano /etc/logrotate.d/gitea_docker_log
    logrotate.d/gitea-docker
    /root/docker_data/gitea/gitea.log {
    # 每天轮转一次
    daily
    # 保留最近 3 份的归档日志
    rotate 3
    # 归档时使用 gzip 压缩
    compress
    # 推迟到下一次轮转时再压缩,避免服务切换文件句柄时出问题
    delaycompress
    # 如果日志文件不存在,不报错
    missingok
    # 如果日志文件为空,不进行轮转
    notifempty
    # 复制log文件并清空原文件内容
    copytruncate
    }
关于 Docker Gitea 非 rootless 版无法获取SSH log信息解决办法
https://blog.useforall.com/posts/solve-docker-gitea-ssh-log-issue/
最后更新于 2025-11-03,距今已过 13 天

部分内容可能已过时

评论区

目录