Samba AD 架設
BUBU 因專案上接到 Samba AD 升級的需求,因此將整個安裝流程與相關設定記錄下來。Samba AD 可以視為 Linux 版的 Active Directory,能提供與 Windows Server 網域控制站相同的網域服務。
運行環境
環境皆架設於「Proxmox VE」虛擬系統,預設以「LXC」模式為主,除非有特殊狀況才會改用「VM」模式。
- 系統環境:Ubuntu 20.04、24.04
- 網域名稱(Realm):abc.local
- NetBIOS 名稱(Domain):abc
- 第一台 DC:dc1、192.168.0.254
- 第二台 DC:dc2、192.168.0.253
- 防毒服務:ClamAV(套件庫版本,20.04 為 0.103.x、24.04 為 1.x,此版本差異會直接影響病毒碼能否更新,詳見「安裝防毒」章節)
前置需求
- 網域名稱與 NetBIOS 名稱請先規劃確定,建置完成後要變更的代價極高。
- 每台 DC 都必須使用固定 IP,且 DNS 改由 Samba 自行提供,不可再交由 DHCP 派發。
- 主機名稱需事先確定,provision 完成後不建議再更動。
- 各台 DC 之間的系統時間必須能正常同步,時間誤差過大會導致 Kerberos 驗證失敗。
管理者密碼必須符合複雜度要求:至少 8 個字元,且需包含大寫字母與數字。不符合時 provision 會直接失敗,這是初次架設最常見的卡關點。
安裝過程
環境設定
- 設定本機名稱
sudo hostnamectl set-hostname dc1
- 設定 Hosts
vim /etc/hosts
192.168.0.254 dc1.abc.local dc1
安裝 Samba AD 服務
- 安裝 Samba AD 相關套件
sudo apt-get install -y acl attr samba samba-dsdb-modules samba-vfs-modules winbind libpam-winbind libnss-winbind libpam-krb5 krb5-config krb5-user dnsutils chrony net-tools
- 安裝過程中系統會跳出三個設定視窗要求填入資訊,依序如下。
# 預設的 Kerberos 第五版領域(Realm):
abc.local
# 您的領域所使用的 Kerberos 伺服器:
dc1.abc.local
# 您的領域所使用的管理伺服器(Administrative server):
dc1.abc.local
-
填入畫面如下
- 設定 Kerberos 時系統會自動帶出您的網域名稱,不需修改,直接按 OK。
- 輸入 Samba AD 主機名稱,例:dc1.abc.local
- 一樣輸入 Samba AD 主機名稱,例:dc1.abc.local
-
備份預設設定檔
sudo mv /etc/samba/smb.conf /etc/samba/smb.conf.orig
sudo mv /etc/krb5.conf /etc/krb5.conf.orig
- 設定 Samba AD 服務
sudo samba-tool domain provision --use-rfc2307 --interactive
- 設定 AD 相關參數。執行上述指令後,系統會自動帶出剛才填入的資訊,方括號內為預設值,直接按 Enter 即可沿用。
- Realm [abc.local]:直接按 Enter
- Domain [abc]:直接按 Enter
- Server Role (dc, member, standalone) [dc]:直接按 Enter
- DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE) [SAMBA_INTERNAL]:直接按 Enter
- DNS forwarder IP address (write 'none' to disable forwarding) [127.0.0.53]:輸入 168.95.1.1
- Administrator password:填入管理者的密碼
- Retype password:再次填入管理者的密碼
DNS forwarder 只接受一組 IP 位址,不可填入多組。若需要多組上游 DNS,請於 provision 完成後在 smb.conf 的 [global] 區段以 dns forwarder 參數調整。
- 最後會看到所設定的主機相關資訊
- 複製設定檔
sudo cp /var/lib/samba/private/krb5.conf /etc/krb5.conf
- 停止並停用 systemd-resolved 服務
sudo systemctl disable --now systemd-resolved
- 移除符號連結檔案 /etc/resolv.conf
sudo unlink /etc/resolv.conf
- 建立一組新的 DNS 相關資訊
vim /etc/resolv.conf
# Samba 伺服器的 IP 位址
nameserver 192.168.0.254
# fallback resolver
nameserver 8.8.8.8
nameserver 1.1.1.1
# Samba 的主要網域
search abc.local
- 鎖定 DNS 設定檔,避免被其他服務覆寫(chattr 說明:改變檔案的屬性(Change Attribute),
+i代表 immutable,設定後連 root 都無法直接修改或刪除)
sudo chattr +i /etc/resolv.conf
- 如要異動 DNS 設定檔,要先把屬性解除才能修改
sudo chattr -i /etc/resolv.conf
- 確認 Samba NTP 簽章位置
samba -b | grep 'NTP' | awk '{print $NF}'
samba -b 讀取的是編譯時的組態參數,不需要先啟動 Samba 服務即可查詢;ntp_signd 目錄在 provision 階段就已建立。
- 執行後會出現 NTP 簽章位置
- 設定
chrony服務所需的目錄權限,供時間同步使用
sudo chown root:_chrony /var/lib/samba/ntp_signd/
sudo chmod 750 /var/lib/samba/ntp_signd/
- 編輯
chrony設定檔vim /etc/chrony/chrony.conf,於設定檔最後新增以下三個參數
# 將 NTP 服務綁定到 Samba AD 的 IP 位址
bindaddress 192.168.0.254
# 允許網路內的用戶端連線到 Chrony NTP 伺服器
allow 192.168.0.0/24
# 指定 Samba AD 的 ntpsigndsocket 目錄位置,供 MS-SNTP 用戶端進行簽章驗證
ntpsigndsocket /var/lib/samba/ntp_signd
此處要用的是 bindaddress 而非 bindcmdaddress。bindaddress 綁定的是 NTP 服務本身,bindcmdaddress 綁定的則是 chronyc 使用的監控命令通道,預設僅監聽本機,不建議對外開放。
- 重啟
chrony服務
sudo systemctl restart chrony
在 Ubuntu 與 Debian 上服務名稱為 chrony,RHEL 系(Rocky、AlmaLinux 等)才是 chronyd,兩者不可混用。
- 將 Samba 設定為以 systemd 服務執行。先停用並遮蔽(mask)smbd、nmbd、winbind,再解除遮蔽(unmask)並啟用 samba-ad-dc。
sudo systemctl stop smbd nmbd winbind
sudo systemctl disable smbd nmbd winbind
sudo systemctl mask smbd nmbd winbind
sudo systemctl unmask samba-ad-dc
sudo systemctl enable --now samba-ad-dc
在 AD DC 上,samba 服務會自動以子行程的方式啟動所需的 smbd 與 winbindd,切勿再另外手動啟動這三個服務,否則 Samba AD DC 將無法正常運作。
- 重啟系統
sudo reboot
- 確認 Samba AD 狀態
sudo systemctl status samba-ad-dc
- 確認網域伺服器是否正常運行,可以透過 kinit 進行測試
kinit Administrator
- 輸入密碼後,畫面會顯示該密碼將於幾天後到期
安裝第二台以上 Samba AD 服務
第二台以後的 DC 是「加入」既有網域,而不是重新建立網域,因此不執行 provision,改為執行 domain join。以下先列出與第一台相同、可直接沿用的步驟,接著只詳述三處差異。
-
與第一台相同的步驟,請直接參照上方章節操作,指令完全一致:
- 環境設定(主機名稱改為
dc2,Hosts 改為192.168.0.253 dc2.abc.local dc2) - 安裝 Samba AD 相關套件(含安裝過程中的三個 Kerberos 設定視窗)
- 備份預設設定檔
- 停止並停用 systemd-resolved 服務、移除
/etc/resolv.conf符號連結
- 環境設定(主機名稱改為
-
以下為可一次貼上執行的共通步驟
# 設定主機名稱
sudo hostnamectl set-hostname dc2
# 安裝相關套件
sudo apt-get install -y acl attr samba samba-dsdb-modules samba-vfs-modules winbind libpam-winbind libnss-winbind libpam-krb5 krb5-config krb5-user dnsutils chrony net-tools
# 備份預設設定檔
sudo mv /etc/samba/smb.conf /etc/samba/smb.conf.orig
sudo mv /etc/krb5.conf /etc/krb5.conf.orig
# 停用 systemd-resolved 並移除符號連結
sudo systemctl disable --now systemd-resolved
sudo unlink /etc/resolv.conf
- 差異一:DNS 必須先指向第一台 DC。 加入網域時需要透過 DNS 找到既有網域,因此
vim /etc/resolv.conf的 nameserver 要填第一台 DC 的位址,不可填自己。
# 第一台 Samba AD 伺服器的 IP 位址
nameserver 192.168.0.254
# fallback resolver
nameserver 8.8.8.8
nameserver 1.1.1.1
# Samba 的主要網域
search abc.local
- 完成後同樣鎖定設定檔
sudo chattr +i /etc/resolv.conf
- 差異二:以 domain join 加入網域,不執行 provision。
-W指定的是 NetBIOS 網域名稱,必須與第一台 provision 時的 Domain 值一致(本文為abc)。
sudo samba-tool domain join abc.local DC -W abc -U Administrator
- 複製設定檔
sudo cp /var/lib/samba/private/krb5.conf /etc/krb5.conf
- 接著的 chrony 與 systemd 服務設定同第一台,僅
bindaddress改為本機 IP
# 確認 NTP 簽章位置
samba -b | grep 'NTP' | awk '{print $NF}'
# 設定 ntp_signd 目錄權限
sudo chown root:_chrony /var/lib/samba/ntp_signd/
sudo chmod 750 /var/lib/samba/ntp_signd/
- 編輯
vim /etc/chrony/chrony.conf,於設定檔最後新增以下三個參數
# 將 NTP 服務綁定到本台 DC 的 IP 位址
bindaddress 192.168.0.253
# 允許網路內的用戶端連線到 Chrony NTP 伺服器
allow 192.168.0.0/24
# 指定 Samba AD 的 ntpsigndsocket 目錄位置
ntpsigndsocket /var/lib/samba/ntp_signd
- 重啟
chrony,並將 Samba 設定為以 systemd 服務執行
sudo systemctl restart chrony
sudo systemctl stop smbd nmbd winbind
sudo systemctl disable smbd nmbd winbind
sudo systemctl mask smbd nmbd winbind
sudo systemctl unmask samba-ad-dc
sudo systemctl enable --now samba-ad-dc
sudo reboot
- 重開機後確認服務狀態與網域驗證
sudo systemctl status samba-ad-dc
kinit Administrator
- 差異三:檢查目錄資料庫正確性。 每次加入或移除 DC 後,建議執行以下指令確認複寫資料無誤。
samba-tool dbcheck --reindex
samba-tool dbcheck --fix
samba-tool dbcheck --cross-ncs --fix
憑證設定
- 建立憑證存放位置。路徑可自行調整,但以下三處(存放目錄、權限設定、smb.conf 參數)必須完全一致。
sudo mkdir -p /root/ssl
- 將
privkey.pem、cert.pem、chain.pem放入該目錄後,再設定權限
sudo chown root:root /root/ssl/*.pem
sudo chmod 600 /root/ssl/privkey.pem
sudo chmod 644 /root/ssl/cert.pem
sudo chmod 644 /root/ssl/chain.pem
- 憑證設定
vim /etc/samba/smb.conf
tls enabled = yes
tls keyfile = /root/ssl/privkey.pem
tls certfile = /root/ssl/cert.pem
tls cafile = /root/ssl/chain.pem
- 重啟服務
sudo systemctl restart samba-ad-dc
稽核記錄
- Samba AD 啟用稽核記錄
vim /etc/samba/smb.conf
[global]
# Samba AD DC 稽核記錄設定:
# auth_json_audit:5 驗證成功/失敗、Kerberos、NTLM、來源 IP、授權身分
# dsdb_json_audit:5 AD 物件及屬性的建立、修改與刪除
# dsdb_password_json_audit:5 密碼變更與管理員重設密碼
# dsdb_group_json_audit:5 群組成員新增與移除
#
# 註:auth 由 3 提升為 5,等級 3 僅有 Authentication,5 才含 Authorization
# 註:已移除 dsdb_transaction_json_audit,三台複寫下量體暴增且訊噪比極差
# 注意:任一 class 名稱拼錯會導致整串 log level 被拒絕,稽核完全無輸出
# 修改後務必執行 smbcontrol all debuglevel 確認實際載入結果
log level = 1 auth_json_audit:5@/var/log/samba/auth_json_audit.log dsdb_json_audit:5@/var/log/samba/dsdb_json_audit.log dsdb_password_json_audit:5@/var/log/samba/dsdb_password_json_audit.log dsdb_group_json_audit:5@/var/log/samba/dsdb_group_json_audit.log
# 主記錄檔路徑,固定檔名,勿用 %m(依用戶端分檔會產生大量小檔)
log file = /var/log/samba/log.samba
# 停用 Samba 內建輪替,交由 logrotate 管理
# 預設 5000(KB)只保留一份 .old,舊記錄會被直接覆蓋而產生稽核缺口
max log size = 0
# 時間戳記置於同一行前綴,每筆訊息為單行,省去 multiline 解析
debug prefix timestamp = yes
# 微秒級精度,跨三台 DC 事件排序時需要秒以下解析度
debug hires timestamp = yes
# 務必保持 no,設 yes 會將 SMB/LDAP 工作階段金鑰寫入記錄檔
debug encryption = no
# 保持 Samba 原生格式,避免與 rsyslog 前綴重複造成 JSON 解析失敗
# 註:舊版無此參數,testparm 回報 Unknown parameter 時直接移除本行
debug syslog format = no
- 輪替保存
vim /etc/logrotate.d/samba-audit
# Samba AD DC 稽核記錄輪替設定:
# 本檔即為 180 天保存政策的技術實作,修改等同變更保存期限
# 異動會被 auditd 記錄(key: samba_rotate_change)
/var/log/samba/*_json_audit.log {
# 每日輪替,與 maxsize 併用時單日可能輪替多次
daily
# 檔名改用日期後綴,取代預設的數字遞增(.1 .2 .3)
dateext
# 結尾 -%s(epoch 秒)不可省略:
# 單日多次輪替時若檔名只到日期會發生碰撞,
# logrotate 將直接跳過輪替,當日記錄持續累積直到寫爆磁碟
dateformat -%Y%m%d-%s
# 保留份數上限,純防呆用,非保存期限
# 因 maxsize 存在,份數不等於天數,設太小會導致提前刪除
rotate 900
# 真正的保留期限:刪除 mtime 超過 190 天者
# 目標 180 天,多給 10 天緩衝以涵蓋調閱作業與排程延遲
# 唯一直接對應法遵要求的參數,變更前需經核准
maxage 190
# 單檔超過即提前輪替,避免密碼噴灑等異常產生單一巨大檔案
maxsize 500M
# 壓縮輪替檔,JSON 重複欄位多,壓縮效益極高
compress
compresscmd /usr/bin/zstd
uncompresscmd /usr/bin/unzstd
compressext .zst
# -19 壓縮等級(保存期長,空間優先於耗時)
# --rm 壓縮後刪除原始檔,-T2 限制執行緒避免排擠 samba
# 註:未安裝 zstd 時將上述 compress* 四行整組移除即可退回 gzip
compressoptions -19 --rm -T2
# 延後一輪才壓縮,避免 samba 緩衝內容尚未落地就被壓縮而遺失
delaycompress
# 檔案不存在時不報錯(無對應事件時該 class 檔案不會被建立)
missingok
# 空檔案不輪替,避免產生大量 0 byte 歷史檔
notifempty
# 輪替後立即建立新檔,0640 僅 root 可寫、adm 可讀
# 稽核記錄含帳號名稱與來源 IP,不可設為 0644
create 0640 root adm
# 目錄權限已收斂為 750 root:adm,未指定會因權限不足略過整組規則
su root root
# 比對到多個檔案時 postrotate 只執行一次
sharedscripts
postrotate
# 通知 samba 重新開啟 file descriptor
# 缺少此步驟,samba 會繼續寫入已改名的舊 inode,新檔案永遠是空的
# 此為稽核記錄靜默失效最常見的原因
/usr/bin/smbcontrol all reload-config > /dev/null 2>&1 || true
endscript
}
安裝防毒
本案的 Samba AD 只承擔 LDAP 帳號驗證功能,並未提供檔案共享等其他服務。若讓 ClamAV 以常駐模式(clamd)長期掛在記憶體中,反而會排擠網域驗證的效能,因此這裡改用「定期排程掃描」的作法:平時不留任何常駐程序,只在排定的時間更新病毒碼並掃描一次。
Ubuntu 20.04 請先確認病毒碼能否更新。ClamAV 官方已封鎖 0.105 及以下所有版本(含全部修訂版)的病毒碼下載,而 20.04 套件庫提供的是 0.103.x,執行 freshclam 會直接收到 403 Blocked by CDN 而取不到病毒碼。若必須在 20.04 上部署,需自行升級 ClamAV 或改指向內部鏡像,否則本章節的排程掃描等於空轉。
- 安裝 ClamAV 服務
sudo apt update && sudo apt install -y clamav clamav-freshclam clamav-testfiles
- 安裝後系統會自動啟動常駐服務,立即停用
sudo systemctl disable --now clamav-freshclam
sudo systemctl disable --now clamav-daemon 2>/dev/null || true
本案只安裝 clamav 與 clamav-freshclam,並未安裝 clamav-daemon 套件,因此停用指令的第二行刻意加上錯誤重導向與 || true,讓該服務不存在時不會中斷後續步驟。
- 確認兩個服務都已停止
sudo systemctl is-active clamav-daemon clamav-freshclam
- 備份 ClamAV 的預設設定檔
sudo cp /etc/clamav/freshclam.conf /etc/clamav/freshclam.conf.bak
- 將原本的預設內容全部清除後,貼上以下參數
sudo vim /etc/clamav/freshclam.conf。此檔案屬 clamav 使用者,vim 可能提示唯讀,存檔時請以:w!強制寫入
# ClamAV 病毒碼更新設定 — Samba AD DC(純 LDAP 驗證用途)
# 架構:不使用常駐 daemon,由 systemd timer 於掃描前觸發單次更新
# 服務已停用:systemctl disable --now clamav-freshclam
# 最後修改:2026-07-26 / 負責人:XXX
# 更新程序執行身分,套件安裝時已建立此使用者
DatabaseOwner clamav
# 病毒碼儲存位置,約需 500 MB(更新期間會有暫存副本)
# 務必確認此路徑不在 /var/log 這個 LV 上,避免侵蝕 180 天稽核記錄的空間
DatabaseDirectory /var/lib/clamav
# 更新記錄檔路徑
UpdateLogFile /var/log/clamav/freshclam.log
# 每行加上時間戳記,便於判斷病毒碼年齡與更新失敗時間點
LogTime true
# 只記錄必要訊息,設 true 會產生大量除錯輸出
LogVerbose false
# 不寫入 syslog,避免與 Samba 稽核記錄混雜
LogSyslog false
# 停用內建輪替,交由 /etc/logrotate.d/clamav-custom 統一管理
# 與 smb.conf 的 max log size = 0 同一原則:避免兩套輪替機制互相打架
# 註:官方文件載明 LogRotate 在 LogFileMaxSize 啟用時會被強制開啟,
# 因此兩者必須成對設定,只設其中一項無法真正停用內建輪替
LogFileMaxSize 0
LogRotate false
# 更新來源。三台若同時對外更新,官方 CDN 節流機制可能暫時封鎖來源 IP
# 已由 timer 的 RandomizedDelaySec 錯開;若日後架設內部鏡像,改指向內部位址
DatabaseMirror database.clamav.net
# 版本查詢用的 DNS TXT 記錄,可在無需下載的情況下判斷是否有新版
DNSDatabaseInfo current.cvd.clamav.net
# 使用增量更新(.cdiff),大幅減少每次下載量與對外流量
# 副作用:daily 檔案會由 daily.cvd 變成 daily.cld,監控腳本兩者都要判斷
ScriptedUpdates yes
# 下載完成後驗證資料庫完整性再套用,避免半殘的病毒碼被載入
TestDatabases yes
# 一併下載 bytecode.cvd,內含針對較複雜惡意程式的偵測邏輯
# 註:在 freshclam.conf 中此參數控制的是「要不要下載」,預設即為 yes;
# 真正決定執不執行 bytecode 簽章的是 clamd.conf,兩者不同層
Bytecode true
# 連線逾時。DC 對外連線通常會經過 proxy 或防火牆,故將連線逾時放寬一倍
# 註:ConnectTimeout 官方預設 30 秒,此處 60 秒為實際放寬;
# ReceiveTimeout 60 與 MaxAttempts 3 皆等同官方預設值,明列僅為日後便於調整
# 若對外線路不穩,官方範例值為 ReceiveTimeout 300、MaxAttempts 5
ConnectTimeout 60
ReceiveTimeout 60
MaxAttempts 3
# ── 以下為刻意停用的項目 ──────────────────────────────────────
# 通知 clamd 重新載入病毒碼
# 此參數接受的是 clamd.conf 的「路徑」,不是 true/false
# 寫成 NotifyClamd false 會讓 freshclam 把 false 當成檔名而報錯
# 本環境未執行 clamd,整行註解停用
#NotifyClamd /etc/clamav/clamd.conf
# 每日檢查次數,僅在 daemon 模式(freshclam -d)有效
# 本環境由 systemd timer 觸發單次執行,不需要此參數
#Checks 12
# PID 檔案,僅 daemon 模式需要
#PidFile /run/clamav/freshclam.pid
# 若 DC 需經 proxy 對外,取消註解並填入實際位址
#HTTPProxyServer proxy.aaa2.com
#HTTPProxyPort 3128
# 不要自訂 HTTPUserAgent,官方 CDN 會封鎖非標準 User-Agent
#HTTPUserAgent
- 手動執行一次病毒碼更新
sudo freshclam
- 確認病毒碼已下載
ls -lh /var/lib/clamav/*.c[vl]d
啟用 ScriptedUpdates 之後,daily 病毒碼的副檔名會由 .cvd 變成 .cld,因此上面刻意用 .c[vl]d 一次比對兩種副檔名,否則會誤判成沒有下載成功。
- 用測試檔確認掃描器真的抓得到。前面安裝的
clamav-testfiles會提供一組帶有測試特徵碼的檔案,掃描結果的Infected files應為非 0;若回報 0,代表病毒碼或掃描設定有問題,不要等排程上線後才發現
clamscan -r /usr/share/clamav-testfiles
- 建立掃描腳本
sudo vim /usr/local/sbin/samba-dc-avscan.sh
#!/bin/bash
# Samba AD DC 防毒掃描:
# 每週執行一次,僅告警不處置,偵測結果由 LibreNMS 讀取狀態檔
# 刻意不使用 --remove / --move,誤刪 sam.ldb 或 GPO 的後果遠大於病毒本身
set -uo pipefail
# 註:不使用 -e,掃描發現病毒時 exit code 為 1,需自行判讀而非中止
LOG=/var/log/clamav/scan.log
STATE=/var/lib/clamav/last-scan.state # 供監控讀取的狀態檔
SCAN_TARGETS="/var/lib/samba/sysvol /root /tmp /home /usr/local"
# 本次掃描的暫存輸出。告警內容只取本次結果,
# 若直接對 scan.log 過濾,會把前幾次掃描的舊紀錄一併寄出
TMPLOG=$(mktemp)
trap 'rm -f "$TMPLOG"' EXIT
# ── 掃描前更新病毒碼 ───────────────────────────────────────────
# 單次執行,完成即結束,不留常駐程序
freshclam --quiet || logger -t dc-avscan "WARN: freshclam update failed"
# ── 執行掃描 ───────────────────────────────────────────────────
# -r 遞迴子目錄
# --infected 只輸出感染項目,大幅減少記錄檔體積
# --stdout 錯誤訊息一併導向標準輸出,便於統一收集
# --cross-fs=no 不跨檔案系統(預設為 yes),避免誤掃到網路掛載或其他 LV
START=$(date +%s)
{
echo "===== $(date '+%F %T') scan start ====="
clamscan -r --infected --stdout --cross-fs=no $SCAN_TARGETS
RC=$?
echo "===== exit code: $RC ====="
} > "$TMPLOG" 2>&1
ELAPSED=$(( $(date +%s) - START ))
# 本次結果併入主記錄檔保存
cat "$TMPLOG" >> "$LOG"
# ── 記錄結果供監控使用 ─────────────────────────────────────────
# exit code:0=乾淨、1=發現病毒、2=掃描錯誤
echo "timestamp=$(date +%s) rc=${RC} elapsed=${ELAPSED}" > "$STATE"
case "$RC" in
0) logger -t dc-avscan "scan clean (${ELAPSED}s)" ;;
1) logger -t dc-avscan "ALERT: malware detected, see $LOG (manual review required)"
# 僅告警。發現後由人工判斷,不自動處置
# 只取本次掃描的 FOUND 行,避免混入歷史紀錄
grep FOUND "$TMPLOG" | tail -20 | mail -s "[$(hostname -s)] ClamAV 偵測到威脅" [email protected] 2>/dev/null || true
;;
*) logger -t dc-avscan "ERROR: scan failed with code ${RC}" ;;
esac
exit 0
# 註:一律回傳 0,避免 systemd 將發現病毒視為服務失敗
- 設定掃描腳本權限,並建立記錄檔目錄
sudo chmod 750 /usr/local/sbin/samba-dc-avscan.sh
sudo mkdir -p /var/log/clamav
sudo chown clamav:adm /var/log/clamav
sudo chmod 750 /var/log/clamav
腳本中的告警信件依賴 mail 指令,Ubuntu 最小安裝並未內建,需另外安裝 mailutils 或 bsd-mailx 才會實際寄出。未安裝時腳本不會失敗,但信件會靜默地寄不出去,只剩 logger 寫入 syslog 的那一筆紀錄。
- 建立掃毒的服務單元
sudo vim /etc/systemd/system/samba-dc-avscan.service
[Unit]
Description=Samba AD DC ClamAV scan
# 以下兩行必須成對出現。依 systemd 官方說明,network-online.target
# 屬於「由使用它的單元自行拉起」的類型,只寫 After= 並不會讓它被啟用
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/samba-dc-avscan.sh
# 資源限制:確保掃描作業永遠不會排擠 samba
# clamscan 載入完整病毒碼約需 1.5 GB,設 2G 保留裕度
MemoryMax=2G
MemorySwapMax=0
# 限制 CPU 使用率,避免掃描期間影響 LDAP 驗證回應時間
CPUQuota=40%
Nice=15
# I/O 排程設為 idle,只在系統閒置時使用磁碟
IOSchedulingClass=idle
# 記憶體吃緊時優先終止本程序,而非 samba
OOMScoreAdjust=800
- 建立執行排程
sudo vim /etc/systemd/system/samba-dc-avscan.timer
[Unit]
Description=Weekly Samba AD DC ClamAV scan
[Timer]
# 每週日 04:00 執行,避開稽核記錄的 logrotate(00:00 前後)與歸檔作業(03:00)
OnCalendar=Sun *-*-* 04:00:00
# 隨機延遲最多 1 小時,避免三台同時對外更新病毒碼觸發 CDN 節流
RandomizedDelaySec=3600
# 主機關機而錯過排程時,開機後補執行
Persistent=true
[Install]
WantedBy=timers.target
- 降低 samba 被 OOM Killer 終止的機率
sudo systemctl edit samba-ad-dc
[Service]
# 記憶體不足時,讓核心優先終止其他程序而非 samba
# 數值越低越不容易被 OOM Killer 選中
OOMScoreAdjust=-500
systemctl edit 建立的是 drop-in 覆寫檔,存放於 /etc/systemd/system/samba-ad-dc.service.d/override.conf,不會改動套件安裝的原始單元檔,因此套件升級時這項設定不會被覆蓋。此處與掃描服務的 OOMScoreAdjust=800 是一組的設計:一邊調高、一邊調低,才能確保記憶體吃緊時被犧牲的是掃描作業而不是網域服務。
- 啟用服務
sudo systemctl daemon-reload
sudo systemctl enable --now samba-dc-avscan.timer
sudo systemctl list-timers samba-dc-avscan.timer
- 設定記錄檔輪替
sudo vim /etc/logrotate.d/clamav-custom
# ClamAV 記錄檔輪替 — 保留 180 天,與 Samba 稽核記錄的保存政策一致
# /var/log 為稽核記錄專用 LV,ClamAV 記錄不得無限成長侵蝕其空間
# 前提:freshclam.conf 已設 LogRotate false + LogFileMaxSize 0,避免雙重輪替
/var/log/clamav/*.log {
# 每週輪替。ClamAV 記錄量極小,不需要每日切檔
weekly
# 檔名改用日期後綴,便於稽核調閱時直接依日期定位
dateext
# 結尾 -%s(epoch 秒)不可省略:
# maxsize 會造成單週多次輪替,檔名只到日期會發生碰撞,
# logrotate 將直接跳過輪替而使記錄持續累積
dateformat -%Y%m%d-%s
# 保留份數上限,純防呆用,非保存期限
# 週輪替 26 份即滿半年,設 60 提供 maxsize 額外輪替的裕度
rotate 60
# 真正的保留期限:刪除 mtime 超過 190 天者
# 目標 180 天,多給 10 天緩衝以涵蓋調閱作業與排程延遲
maxage 190
# 單檔超過即提前輪替,防止病毒碼更新反覆失敗等異常造成暴增
maxsize 20M
# 壓縮輪替檔
compress
compresscmd /usr/bin/zstd
uncompresscmd /usr/bin/unzstd
compressext .zst
compressoptions -19 --rm -T2
# 延後一輪才壓縮,避免正在寫入的內容被壓縮而遺失
delaycompress
# 檔案不存在時不報錯(scan.log 在首次掃描前不存在)
missingok
# 空檔案不輪替
notifempty
# 輪替後立即建立新檔,擁有者與 freshclam 的 DatabaseOwner 一致
create 0640 clamav adm
# 目錄擁有者為 clamav 而非 root,未指定 su 會因權限檢查而略過整組規則
su root root
}
- 設定完成後,以測試模式確認輪替規則可被正確解析(
-d為模擬執行,不會實際輪替)
sudo logrotate -d /etc/logrotate.d/clamav-custom
補充說明
- 如果有 Windows Server 要加入 Samba AD 網域控制站,記得在
vim /etc/samba/smb.conf加入以下參數才能正常加入
dsdb:schema update allowed=true
- 掃描結果的狀態檔
/var/lib/clamav/last-scan.state內容為單行的timestamp、rc、elapsed三組鍵值,可供 LibreNMS 等監控系統以自訂腳本讀取。要判斷「掃描是否已停擺」請比對timestamp與當前時間的差距,不要只看rc:排程完全沒有執行時,rc會一直停留在上一次的成功值,看起來與正常運作毫無差別。
疑難排解
-
錯誤訊息: provision 階段出現密碼複雜度相關錯誤而中斷 解決方式: 管理者密碼至少 8 個字元,且需包含大寫字母與數字。provision 會重設網域的密碼原則,因此事前調整無效,請直接改用符合複雜度的密碼;若確有放寬需求,待建置完成後再以
samba-tool domain passwordsettings set --complexity=off調整,但會明顯降低網域安全性。 -
錯誤訊息: 稽核記錄檔完全沒有任何輸出 解決方式:
log level中只要有任一 class 名稱拼錯,整串設定都會被拒絕。請執行smbcontrol all debuglevel確認實際載入的結果是否與設定檔一致。 -
錯誤訊息: 稽核記錄輪替後新檔案永遠是空的 解決方式: 輪替後必須通知 samba 重新開啟 file descriptor,否則會繼續寫入已改名的舊 inode。請確認 logrotate 設定中的
postrotate區塊有執行smbcontrol all reload-config。 -
錯誤訊息: 無法修改
/etc/resolv.conf,提示 Operation not permitted 解決方式: 該檔案已被設為 immutable,需先執行sudo chattr -i /etc/resolv.conf解除屬性,修改完成後再以sudo chattr +i /etc/resolv.conf鎖回。 -
錯誤訊息: 第二台 DC 執行
samba-tool domain join時找不到網域 解決方式: 加入網域前/etc/resolv.conf的 nameserver 必須指向第一台 DC,若指向自己或外部 DNS 將無法解析網域相關的 SRV 記錄。 -
錯誤訊息: 執行
systemctl restart chronyd提示找不到服務 解決方式: Ubuntu 與 Debian 的服務名稱為chrony,chronyd是 RHEL 系的命名,請改用sudo systemctl restart chrony。 -
錯誤訊息: 執行
freshclam出現403 Forbidden或Blocked by CDN,完全取不到病毒碼 解決方式: ClamAV 官方已封鎖 0.105 及以下所有版本的病毒碼下載,請先以clamscan --version確認版本。Ubuntu 20.04 套件庫提供的 0.103.x 正落在封鎖範圍內,官方自 2025-09-14 起停止對其提供病毒碼。解法為升級 ClamAV、升級作業系統,或改以內部鏡像提供病毒碼,單純重試不會成功。 -
錯誤訊息: 編輯
/etc/clamav/freshclam.conf時 vim 提示唯讀而無法存檔 解決方式: 該檔案的擁有者為clamav使用者,請確認是以sudo開啟,並於存檔時以:w!強制寫入。 -
錯誤訊息:
freshclam執行失敗,並抱怨找不到名為false的檔案 解決方式:NotifyClamd接受的是 clamd.conf 的路徑,不是true或false,寫成NotifyClamd false會讓 freshclam 把false當成設定檔路徑。本案未執行 clamd,請將整行註解停用。 -
現象: 排程確實有執行,但掃描結果永遠是乾淨的 解決方式: 先以
clamscan -r /usr/share/clamav-testfiles確認掃描器抓得到測試特徵碼。若連測試檔都回報 0,代表病毒碼並未成功下載(最常見的原因即為前述的 CDN 封鎖),而不是系統真的乾淨。 -
現象: 掃描已發現病毒,但
systemctl status samba-dc-avscan仍顯示執行成功 解決方式: 這是刻意的設計。掃描腳本最後一律exit 0,避免 systemd 將「發現病毒」判定為服務失敗。實際結果請改看/var/lib/clamav/last-scan.state中的rc值(0 為乾淨、1 為發現病毒、2 為掃描錯誤),或 syslog 中標籤為dc-avscan的紀錄。
備註
參考相關網頁
- Setting up Samba as an Active Directory Domain Controller - SambaWiki
- Managing the Samba AD DC Service Using Systemd - SambaWiki
- chrony.conf(5) - chrony 官方設定文件
- ClamAV 官方文件 - Configuration(freshclam.conf 參數說明)
- ClamAV 官方文件 - End of Life policy(版本封鎖與病毒碼供應期限)
- ClamAV 官方文件 - Scanning(clamscan 選項與回傳碼)
- systemd - Running Services After the Network is up
- Samba 4 Active Directory Domain Controller on Ubuntu 20.04 Server
- Samba Based Active Directory on Ubuntu 22.04
- Adding a Windows 2019 DC to Your Samba Domain







