先说句题外话。
我其实个人觉得 Komari 作者做得挺好的,完全没有觉得越权。作者的出发点确实是善意的 —— 有了这个提示,你确实能第一时间发现「某台不该装 Komari 的机器被入侵了」这件事。文案看起来也是深思熟虑过的,包括不给关闭选项,我猜也是为了这个「快速发现入侵」的考虑,而不是那种「教用户做事」的味道。
而且 WebSSH 都开了,Agent 顺手改个 MOTD 提示「你已经开启远程控制」,我个人是觉得无伤大雅的 🙂
但是 —— 看多了还是不舒服。所以只能用一种不怎么优雅的方式解决了 🙂
一、脚本原理
Komari-Agent 每次重启都会重新写入一遍 MOTD 警告块。基于这个行为,脚本的思路是:
- 监测
/etc/motd的变动 —— 一旦变动,立刻把 Komari 添加的那一段删掉 - 每天兜底检测一次 —— 防止某些文件系统上 inotify 看不到变化
这样既不用改 Agent,也不用关掉 WebSSH,面板的远程控制能力完全保留,只是 MOTD 里那几行字会被自动清掉。
关于 set -- --persist 那一行
脚本顶部有一行 set -- --persist,它让脚本默认就走持久模式。
| 模式 | 怎么用 | 效果 |
|---|---|---|
| 持久模式(默认) | 直接跑,或保留 set -- --persist |
清除 + 持续监听 + 每天兜底。Agent 重启后也会被自动清掉 |
| 单次模式 | 删掉 set -- --persist 这一行 |
只清除一次,不监听。Agent 重启后 MOTD 里那一段又会冒出来 |
建议直接在 Komari 主控批量运行 —— 主控有批量执行能力,一次把所有节点都处理掉,省得一台台登录。
二、脚本特性
| 精确删除 | 只删 Komari 那一段。服务商自己的 banner、其它内容一个字都不动;文件的 inode、权限、属主、属组全部保留(原地重写,不重建文件) |
| 自动备份 | 动任何东西之前先写一份带时间戳的备份到 /var/backups/komari-motd/ |
| 边界严格 | 块边界完全对齐 Agent 自己的代码:从 [Komari] ... 那行到 Uninstall Komari Agent: ... 那行,再加它后面空的一行。两个块或残缺块 → 拒绝执行,什么都不改 |
| 附带清理 | 如果 /etc/update-motd.d/ 下有 Komari 留下的钩子(且带 Komari 标记),一并删掉;不是 Komari 管理的则跳过 |
| 不碰 Agent | 全程不会启动、停止、重配或卸载 Komari Agent,面板远程控制能力完好 |
| 可视化结果 | 结束时输出一行机器可读的 [result] host=... status=... rc=...,方便主控批量收集 |
| 兼容性好 | POSIX sh,无 bashism,ASCII 输出。只用 grep / awk / sed / cut / cp / rm / date / basename / head —— Debian (dash + coreutils) 和 Alpine (busybox) 都自带 |
三、脚本源码
#!/bin/sh
# ---------------------------------------------------------------------------
# remove-komari-motd.sh -- strip the Komari Agent security warning from /etc/motd
#
# Why: current Komari Agent builds append a warning block to /etc/motd while the
# agent runs (upstream: cmd/warn_linux.go -> installMOTDWarning):
#
# [Komari] Remote control is enabled on this device
# <panel host> can execute commands and read or modify files on this device as root.
# If you did not set this up, your device may have been accessed without authorization.
# Stop Komari Agent immediately and check your device for signs of compromise.
#
# Uninstall Komari Agent: https://komari-document.pages.dev/en/faq/uninstall
#
# What this script does:
# 1. removes exactly that block from /etc/motd. A provider banner or any other
# content is left untouched, and the file keeps its inode, mode, owner and
# group (content is rewritten in place, not recreated);
# 2. removes a leftover Komari hook under /etc/update-motd.d/ only if that file
# carries the Komari marker;
# 3. writes a timestamped backup before touching anything;
# 4. verifies the result and prints one machine-readable [result] line.
#
# The block boundaries follow the agent's own code: from the "[Komari] ..." line
# through the "Uninstall Komari Agent: ..." line, plus one following empty line
# (the agent inserts one). If what is left is an empty file, the file is removed,
# exactly like the agent does on shutdown. Two blocks or a truncated block =>
# refuse and change nothing (the agent refuses those too).
#
# IMPORTANT: the agent re-appends the warning every time it (re)starts, i.e. on
# reboot, on crash-restart and after its self-update. A one-shot run is therefore
# not permanent. Two supported ways to make it permanent:
# * ./remove-komari-motd.sh --persist
# installs a tiny guard that re-strips automatically. On systemd it is
# event driven: a .path unit watches /etc/motd with inotify and reacts
# within a fraction of a second of the agent's rewrite (a boot + daily
# timer stays as a safety net for filesystems where inotify cannot see it).
# Where systemd is absent: a boot hook plus a 15-minute periodic hook.
# Nothing else is changed, the panel keeps remote control.
# * add --disable-web-ssh to the agent command line / unit ExecStart
# official Komari switch: the agent then removes the warning itself and
# never writes it again -- but the panel also loses remote control
# (web ssh + RCE) for that node.
#
# Compatibility: POSIX sh, no bashisms, ASCII-only output. Uses only grep, awk,
# sed, cut, cp, rm, date, basename, head -- all present on Debian (dash +
# coreutils) and Alpine (busybox). It never starts, stops, reconfigures or
# uninstalls the Komari agent.
#
# Exit codes: 0 = removed or nothing to do, 1 = error, 2 = refused to modify,
# 3 = not root, 4 = usage error
# ---------------------------------------------------------------------------
set -- --persist
set -u
PROG=${0##*/}
SELF=$0
MOTD_PATH=${KOMARI_MOTD_PATH:-/etc/motd}
BACKUP_DIR=${KOMARI_MOTD_BACKUP_DIR:-/var/backups/komari-motd}
GUARD_DIR=/usr/local/lib/komari-motd
GUARD_SH=$GUARD_DIR/guard.sh
UNIT_NAME=komari-motd-guard
SERVICE_FILE=/etc/systemd/system/$UNIT_NAME.service
PATH_FILE=/etc/systemd/system/$UNIT_NAME.path
TIMER_FILE=/etc/systemd/system/$UNIT_NAME.timer
LOCAL_HOOK=/etc/local.d/$UNIT_NAME.start
PERIODIC_HOOK=/etc/periodic/15min/$UNIT_NAME
LEGACY_DIR=/etc/update-motd.d
START_MARKER='[Komari] Remote control is enabled on this device'
END_PREFIX='Uninstall Komari Agent: '
LEGACY_MARKER='# Komari Agent managed MOTD warning'
DRY=0
PERSIST=0
UNPERSIST=0
GUARD=0
VERBOSE=1
info() { printf '[info] %s\n' "$*"; }
ok() { printf '[ ok ] %s\n' "$*"; }
warn() { printf '[warn] %s\n' "$*"; }
fail() { printf '[fail] %s\n' "$*"; }
vex() { if [ "$VERBOSE" = 1 ]; then ok "$*"; fi; return 0; }
vinfo(){ if [ "$VERBOSE" = 1 ]; then info "$*"; fi; return 0; }
vwarn(){ if [ "$VERBOSE" = 1 ]; then warn "$*"; fi; return 0; }
have() { command -v "$1" >/dev/null 2>&1; }
usage() {
cat <<EOF
$PROG -- remove the Komari Agent security-warning block from /etc/motd
Usage: $PROG [options]
Options:
-n, --dry-run show what would be removed, change nothing
-p, --persist strip now and install a guard that keeps /etc/motd clean.
systemd: event driven (.path unit on /etc/motd) + hourly
safety timer. otherwise: boot hook + 15-minute hook.
-u, --unpersist remove the guard installed by --persist
-h, --help this help
--motd PATH operate on PATH instead of /etc/motd (testing; implies no
root requirement and is never combined with --persist)
--guard internal quiet mode used by the installed guard
Environment:
KOMARI_MOTD_PATH same as --motd
KOMARI_MOTD_BACKUP_DIR backup directory, default $BACKUP_DIR
Exit codes: 0 ok / nothing to do, 1 error, 2 refused, 3 not root, 4 usage error
EOF
}
# --------------------------------------------------------------------------
# option parsing
# --------------------------------------------------------------------------
while [ $# -gt 0 ]; do
case $1 in
-n|--dry-run) DRY=1 ;;
-p|--persist) PERSIST=1 ;;
-u|--unpersist) UNPERSIST=1 ;;
--guard) GUARD=1; VERBOSE=0 ;;
--motd) shift; [ $# -gt 0 ] || { fail "--motd needs an argument"; exit 4; }; MOTD_PATH=$1 ;;
--motd=*) MOTD_PATH=${1#--motd=} ;;
-h|--help) usage; exit 0 ;;
*) fail "unknown option: $1"; usage; exit 4 ;;
esac
shift
done
if [ "$MOTD_PATH" = "/etc/motd" ] && [ "$DRY" != 1 ] && [ "$(id -u)" != 0 ]; then
fail "must run as root (use: sudo $PROG)"
exit 3
fi
if [ "$PERSIST" = 1 ] && [ "$MOTD_PATH" != "/etc/motd" ]; then
fail "--persist only works on the real /etc/motd"
exit 4
fi
# --------------------------------------------------------------------------
# environment reporting
# --------------------------------------------------------------------------
os_name() {
if [ -r /etc/os-release ]; then
( . /etc/os-release 2>/dev/null; printf '%s' "${PRETTY_NAME:-${ID:-unknown}}" )
else
printf 'unknown'
fi
}
init_system() {
if [ -d /run/systemd/system ] && have systemctl; then
printf 'systemd'
elif have rc-service || [ -x /sbin/openrc-run ]; then
printf 'openrc'
elif [ -d /etc/periodic ]; then
printf 'cron/periodic'
else
printf 'unknown'
fi
}
agent_state() {
st=not-found
if [ -f /opt/komari/agent ] || [ -f /etc/systemd/system/komari-agent.service ] || [ -f /etc/init.d/komari-agent ]; then
st=installed
fi
if [ "$st" = installed ]; then
if have pgrep && pgrep -f 'komari' >/dev/null 2>&1; then
st="$st,running"
elif ps 2>/dev/null | grep -v grep | grep -q 'komari'; then
st="$st,running"
else
st="$st,not-running"
fi
fi
printf '%s' "$st"
}
agent_disables_web_ssh() {
for f in /etc/systemd/system/komari-agent.service /etc/init.d/komari-agent /opt/komari/komari-agent.service; do
[ -f "$f" ] || continue
if grep -q -- '--disable-web-ssh' "$f" 2>/dev/null; then
return 0
fi
done
return 1
}
# --------------------------------------------------------------------------
# backup helper: backup_file <path> ; prints the backup path or nothing
# --------------------------------------------------------------------------
backup_file() {
_bf_src=$1
_bf_dir=$BACKUP_DIR
if ! mkdir -p "$_bf_dir" 2>/dev/null; then
vwarn "cannot create backup dir $_bf_dir -- continuing without a backup"
return 0
fi
_bf_dst=$_bf_dir/$(basename "$_bf_src").$(date +%Y%m%d-%H%M%S).$$
if cp -p "$_bf_src" "$_bf_dst" 2>/dev/null; then
printf '%s' "$_bf_dst"
else
vwarn "cannot copy $_bf_src to $_bf_dir -- continuing without a backup"
fi
return 0
}
# --------------------------------------------------------------------------
# strip_motd: the actual removal
# returns 0 removed, 10 nothing to do, 2 refused, 1 error
# --------------------------------------------------------------------------
strip_motd() {
sf=$MOTD_PATH
if [ -L "$sf" ]; then
tgt=$(readlink -f "$sf" 2>/dev/null || true)
if [ -n "$tgt" ]; then
vinfo "$sf is a symlink to $tgt"
sf=$tgt
fi
fi
if [ ! -e "$sf" ]; then
vinfo "$sf does not exist -- nothing to strip"
return 10
fi
if [ ! -f "$sf" ]; then
fail "$sf is not a regular file -- refusing to touch it"
return 2
fi
if ! grep -qF "$START_MARKER" "$sf" 2>/dev/null; then
vinfo "no Komari warning block in $sf -- nothing to strip"
return 10
fi
cnt=$(grep -cF "$START_MARKER" "$sf" 2>/dev/null || true)
if [ "${cnt:-0}" -gt 1 ] 2>/dev/null; then
fail "$sf holds $cnt Komari warning blocks -- refusing to modify (the agent refuses this too)"
return 2
fi
start=$(grep -nF "$START_MARKER" "$sf" | head -n 1 | cut -d: -f1)
end=$(awk -v s="$start" -v p="$END_PREFIX" 'NR >= s && index($0, p) == 1 { print NR; exit }' "$sf")
if [ -z "$end" ]; then
fail "$sf holds an incomplete Komari warning (no line starting with '$END_PREFIX') -- refusing to modify"
return 2
fi
removed=$((end - start + 1))
size_before=$(wc -c < "$sf" | tr -d ' ')
if [ "$DRY" = 1 ]; then
vex "dry-run: would remove $removed line(s) (lines $start-$end) from $sf, $size_before bytes now"
return 0
fi
if [ "$GUARD" != 1 ]; then
bk=$(backup_file "$sf")
if [ -n "$bk" ]; then
vex "backup: $bk"
fi
fi
tmp=$sf.komari-strip.$$
if ! awk -v s="$start" -v e="$end" 'NR < s || NR > e { if (NR == e + 1 && $0 == "") next; print }' "$sf" > "$tmp"; then
rm -f "$tmp"
fail "awk failed while rewriting $sf -- nothing changed"
return 1
fi
# rewrite content in place: the inode, mode, owner, group and ACLs survive
if ! cat "$tmp" > "$sf" 2>/dev/null; then
rm -f "$tmp"
fail "cannot write $sf -- nothing changed"
return 1
fi
rm -f "$tmp"
if grep -qF "$START_MARKER" "$sf" 2>/dev/null; then
fail "verification failed: the marker is still in $sf"
return 1
fi
if [ ! -s "$sf" ]; then
vex "removed $removed line(s) (lines $start-$end) from $sf; nothing else was in it, removing the empty file"
rm -f "$sf"
return 0
fi
vex "removed $removed line(s) (lines $start-$end) from $sf; no other content touched, mode/owner kept"
return 0
}
# --------------------------------------------------------------------------
# leftover /etc/update-motd.d hook
# --------------------------------------------------------------------------
strip_legacy_hooks() {
_lh_seen=0
for h in "$LEGACY_DIR"/*komari*; do
[ -f "$h" ] || continue
_lh_seen=1
first=$(head -n 1 "$h" 2>/dev/null)
if [ "$first" != "$LEGACY_MARKER" ]; then
vwarn "$h exists but is not Komari-managed -- leaving it alone"
continue
fi
if [ "$DRY" = 1 ]; then
vex "dry-run: would delete the Komari hook $h"
continue
fi
bk=$(backup_file "$h")
if [ -n "$bk" ]; then
vex "backup: $bk"
fi
if rm -f "$h"; then
vex "removed leftover Komari hook $h"
else
warn "cannot remove $h"
fi
done
[ "$_lh_seen" = 1 ] || vinfo "no Komari hook in $LEGACY_DIR"
return 0
}
# --------------------------------------------------------------------------
# guard: keeps /etc/motd clean without touching the agent
# --------------------------------------------------------------------------
install_guard() {
if [ "$(id -u)" != 0 ]; then
fail "must run as root to install the guard"
return 1
fi
if ! mkdir -p "$GUARD_DIR"; then
fail "cannot create $GUARD_DIR"
return 1
fi
sed -e "s|@@MOTD@@|$MOTD_PATH|g" \
-e "s|@@START@@|$START_MARKER|g" \
-e "s|@@END@@|$END_PREFIX|g" > "$GUARD_SH" <<'GUARD_EOF'
#!/bin/sh
# Generated by remove-komari-motd.sh --persist. Re-strips the Komari Agent
# warning from the MOTD after the agent (re)started. Silent, always exit 0.
M='@@MOTD@@'
S='@@START@@'
E='@@END@@'
[ -f "$M" ] || exit 0
grep -qF "$S" "$M" 2>/dev/null || exit 0
c=$(grep -cF "$S" "$M" 2>/dev/null)
[ "${c:-0}" -gt 1 ] 2>/dev/null && exit 0
n=$(grep -nF "$S" "$M" | head -n 1 | cut -d: -f1)
[ -n "$n" ] || exit 0
e=$(awk -v s="$n" -v p="$E" 'NR >= s && index($0, p) == 1 { print NR; exit }' "$M")
[ -n "$e" ] || exit 0
t="$M.komari-guard.$$"
if awk -v s="$n" -v e="$e" 'NR < s || NR > e { if (NR == e + 1 && $0 == "") next; print }' "$M" > "$t"; then
if cat "$t" > "$M"; then
rm -f "$t"
[ -s "$M" ] || rm -f "$M"
fi
else
rm -f "$t"
fi
exit 0
GUARD_EOF
if [ ! -s "$GUARD_SH" ]; then
fail "cannot write $GUARD_SH"
return 1
fi
chmod 0755 "$GUARD_SH" 2>/dev/null
ok "guard script: $GUARD_SH"
if [ -d /run/systemd/system ] && have systemctl; then
cat > "$SERVICE_FILE" <<EOF
[Unit]
Description=Remove the Komari Agent MOTD security warning
Documentation=file:$GUARD_SH
After=komari-agent.service
[Service]
Type=oneshot
ExecStart=$GUARD_SH
# the guard prints nothing when there is nothing to do; keep systemd's own
# start/finish chatter out of the journal too
LogLevelMax=notice
EOF
cat > "$PATH_FILE" <<EOF
[Unit]
Description=Watch $MOTD_PATH for the Komari Agent security warning
[Path]
PathModified=$MOTD_PATH
Unit=$UNIT_NAME.service
[Install]
WantedBy=paths.target
EOF
cat > "$TIMER_FILE" <<EOF
[Unit]
Description=Safety-net sweep for the Komari Agent MOTD security warning
[Timer]
OnBootSec=1min
OnUnitActiveSec=1d
AccuracySec=1min
Unit=$UNIT_NAME.service
[Install]
WantedBy=timers.target
EOF
if systemctl daemon-reload 2>/dev/null &&
systemctl enable --now "$UNIT_NAME.path" >/dev/null 2>&1 &&
systemctl enable --now "$UNIT_NAME.timer" >/dev/null 2>&1; then
ok "event watch: $UNIT_NAME.path (PathModified=$MOTD_PATH) -- reacts in well under a second, no polling"
ok "safety net: $UNIT_NAME.timer (at boot + daily), for filesystems where inotify sees nothing"
else
warn "wrote the units but could not enable them; run:"
warn " systemctl daemon-reload && systemctl enable --now $UNIT_NAME.path $UNIT_NAME.timer"
fi
return 0
fi
# no systemd: boot hook (Alpine local.d) + periodic hook (busybox crond)
_guard_installed=0
info "no systemd here, so no inotify .path unit: boot hook + 15-minute poll instead"
if [ -d /etc/local.d ]; then
printf '#!/bin/sh\n%s\n' "$GUARD_SH" > "$LOCAL_HOOK" && chmod 0755 "$LOCAL_HOOK" 2>/dev/null && _guard_installed=1
ok "boot hook: $LOCAL_HOOK"
if have rc-update; then
if rc-update show default 2>/dev/null | grep -q '^[[:space:]]*local'; then
ok "openrc service 'local' is already enabled"
else
warn "enable it once with: rc-update add local default"
fi
fi
fi
if [ -d /etc/periodic/15min ]; then
printf '#!/bin/sh\nexec %s\n' "$GUARD_SH" > "$PERIODIC_HOOK" && chmod 0755 "$PERIODIC_HOOK" 2>/dev/null && _guard_installed=1
ok "periodic hook: $PERIODIC_HOOK"
if have rc-service; then
if rc-service crond status >/dev/null 2>&1; then
ok "crond is running, the periodic hook will fire"
else
warn "crond is not running; enable it once with: rc-update add crond default && rc-service crond start"
fi
fi
fi
if [ "$_guard_installed" = 0 ]; then
warn "no systemd and no /etc/local.d or /etc/periodic -- guard not installed"
warn "fallback: put '$GUARD_SH' into whatever cron your init system runs"
return 1
fi
return 0
}
remove_guard() {
_g_removed=0
if [ -f "$TIMER_FILE" ] || [ -f "$SERVICE_FILE" ] || [ -f "$PATH_FILE" ]; then
if have systemctl; then
systemctl disable --now "$UNIT_NAME.timer" "$UNIT_NAME.path" >/dev/null 2>&1
fi
rm -f "$TIMER_FILE" "$SERVICE_FILE" "$PATH_FILE"
have systemctl && systemctl daemon-reload >/dev/null 2>&1
ok "removed systemd guard units (path + timer + service)"
_g_removed=1
fi
for f in "$LOCAL_HOOK" "$PERIODIC_HOOK"; do
if [ -f "$f" ]; then
rm -f "$f" && ok "removed $f"
_g_removed=1
fi
done
if [ -d "$GUARD_DIR" ]; then
rm -rf "$GUARD_DIR" && ok "removed $GUARD_DIR"
_g_removed=1
fi
[ "$_g_removed" = 1 ] || info "no guard installed, nothing to remove"
return 0
}
# --------------------------------------------------------------------------
# main
# --------------------------------------------------------------------------
if [ "$UNPERSIST" = 1 ]; then
remove_guard
_rc=$?
_un_host=$(hostname 2>/dev/null || printf 'unknown')
printf '[result] host=%s status=unpersisted rc=%s\n' "$_un_host" "$_rc"
exit "$_rc"
fi
if [ "$GUARD" = 1 ]; then
# silent periodic run
strip_motd
rc=$?
strip_legacy_hooks
if [ "$rc" = 0 ]; then exit 0; fi
exit 0
fi
HOSTNAME_STR=$(hostname 2>/dev/null || printf 'unknown')
info "host=$HOSTNAME_STR os=$(os_name) init=$(init_system)"
info "komari agent: $(agent_state)"
if [ "$PERSIST" = 1 ] && [ "$(id -u)" != 0 ]; then
fail "must run as root to install the guard"
exit 3
fi
strip_motd
STRIP_RC=$?
strip_legacy_hooks
case $STRIP_RC in
0) STATUS=removed ;;
10) STATUS=clean ;;
2) STATUS=refused ;;
*) STATUS=error ;;
esac
if [ "$STRIP_RC" = 10 ]; then
vex "nothing to do: $MOTD_PATH is already free of the Komari warning"
fi
if [ "$DRY" = 1 ]; then
printf '[result] host=%s status=dry-run rc=%s\n' "$HOSTNAME_STR" "$STRIP_RC"
exit 0
fi
if [ "$STRIP_RC" = 0 ] || [ "$STRIP_RC" = 10 ]; then
if [ "$(agent_state)" = installed,running ] || [ "$(agent_state)" = installed,not-running ]; then
if agent_disables_web_ssh; then
ok "the agent runs with --disable-web-ssh, so it removes the warning itself and will not write it again"
elif [ "$PERSIST" != 1 ]; then
warn "the agent re-adds this warning on every (re)start; run with --persist for a permanent result"
fi
fi
fi
if [ "$PERSIST" = 1 ] && [ "$STRIP_RC" != 2 ]; then
install_guard
ok "guard armed: the warning is removed again automatically"
fi
printf '[result] host=%s status=%s rc=%s\n' "$HOSTNAME_STR" "$STATUS" "$STRIP_RC"
case $STRIP_RC in
0|10) exit 0 ;;
2) exit 2 ;;
*) exit 1 ;;
esac
四、参数说明
| 参数 | 说明 |
|---|---|
-n, --dry-run |
只显示「将会删除什么」,不做任何修改 |
-p, --persist |
立即清除 + 安装守护,保持长期干净。systemd 环境下用 .path 单元事件驱动(inotify 监听,亚秒级响应),另配一个开机 + 每天的定时器兜底;没有 systemd 时退化成正向启动钩子 + 15 分钟轮询 |
-u, --unpersist |
卸载 --persist 装的守护,恢复原状 |
-h, --help |
查看帮助 |
--motd PATH |
对指定文件操作而不是 /etc/motd(测试用,此时不需要 root,且不能和 --persist 一起用) |
--guard |
内部静默模式,供已安装的守护调用 |
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
KOMARI_MOTD_PATH |
/etc/motd |
等同于 --motd |
KOMARI_MOTD_BACKUP_DIR |
/var/backups/komari-motd |
备份目录 |
退出码
| 码 | 含义 |
|---|---|
0 |
已删除 / 无需处理 |
1 |
出错 |
2 |
拒绝修改(检测到异常情况) |
3 |
非 root 运行 |
4 |
参数使用错误 |
五、快速使用
1. 保存脚本
sudo nano /root/remove-komari-motd.sh
# 粘贴上面的脚本内容,保存退出
sudo chmod +x /root/remove-komari-motd.sh
2. 先干跑看看会删什么
sudo /root/remove-komari-motd.sh --dry-run
3. 正式执行(持久模式)
sudo /root/remove-komari-motd.sh
# 脚本顶部已有 set -- --persist,直接跑就是持久模式
4. 验证结果
# MOTD 里应该已经找不到 Komari 字样
grep -i komari /etc/motd
# 守护单元应该都在
systemctl status komari-motd-guard.path
systemctl status komari-motd-guard.timer
# 备份目录
ls -la /var/backups/komari-motd/
5. 想撤销怎么办
# 卸载守护(MOTD 保持当前状态,Agent 下次重启会重新写入)
sudo /root/remove-komari-motd.sh --unpersist
# 如果想彻底恢复,从备份还原
sudo cp -p /var/backups/komari-motd/motd.* /etc/motd
六、官方开关(更优雅,但有代价)
其实 Komari 官方提供了一个开关,比这个脚本优雅得多:
# 在 Agent 的启动命令 / systemd 单元的 ExecStart 里加上:
--disable-web-ssh
效果:Agent 会自己移除那段警告,并且以后再也不写。
代价:该节点的面板远程控制能力也没了(WebSSH + RCE)。
所以这是个取舍:
- 要保留远程控制 → 用本文脚本,MOTD 干净,能力保留
- 不需要远程控制 → 直接用官方
--disable-web-ssh,最干净
七、一点想法
写这个脚本的过程里我想明白了几个事,也顺便记一下:
- 这个提示本身是有价值的。它把一个「静默的、高权限的远程控制通道」变得可见 —— 这是很多监控 Agent 都不做的事,作者愿意做,值得肯定。
- 「看得见」和「每天都要看见」是两件事。第一次知道很重要,第一百次就是噪音了。
- 官方不给关闭选项,我理解但不完全认同。安全设计里「不可关闭」确实能防住「用户随手关掉然后忘了」,但也把「我知道风险、我要自己承担」这类用户逼到了写脚本的路上 —— 比如我 🙂
- 所以本文不是「绕过安全提示」教程,而是一个「我已经知情并接受风险,只是想让终端干净点」的方案。脚本全程不碰 Agent、不降权限、不改任何安全配置。
提醒:如果你不知道为什么 MOTD 里会出现 Komari 提示,那说明这台机器可能被未经授权地装了 Agent —— 请立刻停掉 Agent 并检查机器。这个脚本是给「自己知道、自己装的」人用的,不是给「发现异常后装作没看见」的人用的。
免责声明:本文仅为个人使用体验与技术记录。脚本不修改 Komari Agent 本体,不改变任何权限或安全配置,仅清理 /etc/motd 中的特定文本块。请自行评估风险,遵守相关法律法规。

Comments NOTHING