Lxc

如何在 LXC 下僅將 ntpd 作為伺服器執行,而不調整本地機器時間

  • July 15, 2020

我需要將設備指向我在 LXC 中執行的 CentOS-7 伺服器同步它們的時鐘。在我試圖ntpdntp包中使用的伺服器上,但對其他產品開放。這個問題是關於在伺服器上設置ntpd或等效的。

到目前為止,我試過這個/etc/ntp.conf

driftfile /var/lib/ntp/drift
restrict default nomodify notrap nopeer noquery

restrict 127.0.0.1
restrict ::1

server 127.127.1.1 iburst
fudge  127.127.1.1 stratum 8

disable monitor

這裡有兩個問題。

  1. ntpd記錄後終止cap_set_proc() failed to drop root privileges: Operation not permitted
  2. ntpd正在嘗試調整當地時間。它失敗了,但它會嘗試。如果這是唯一的問題並且我在日誌中有錯誤消息,我可以接受。

/var/log/messages嘗試啟動 ntpd 導致的完整輸出:

systemd: Starting Network Time Service...
ntpd[20154]: ntpd 4.2.6p5@1.2349-o Wed Apr 12 21:24:06 UTC 2017 (1)
ntpd[20155]: proto: precision = 0.120 usec
ntpd[20155]: ntp_adjtime() failed: Operation not permitted
systemd: Started Network Time Service.
ntpd[20155]: 0.0.0.0 c01d 0d kern kernel time sync enabled
ntpd[20155]: Listen and drop on 0 v4wildcard 0.0.0.0 UDP 123
ntpd[20155]: Listen and drop on 1 v6wildcard :: UDP 123
ntpd[20155]: Listen normally on 2 lo 127.0.0.1 UDP 123
ntpd[20155]: Listen normally on 3 eth0 hidden:A.B.C.D UDP 123
ntpd[20155]: Listen normally on 4 tun0 hidden:E.F.G.H UDP 123
ntpd[20155]: Listening on routing socket on fd #21 for interface updates
ntpd[20155]: 0.0.0.0 c016 06 restart
ntpd[20155]: ntp_adjtime() failed: Operation not permitted
ntpd[20155]: 0.0.0.0 c012 02 freq_set kernel 0.000 PPM
ntpd[20155]: 0.0.0.0 c011 01 freq_not_set
ntpd[20155]: cap_set_proc() failed to drop root privileges: Operation not permitted
systemd: ntpd.service: main process exited, code=exited, status=255/n/a
systemd: Unit ntpd.service entered failed state.
systemd: ntpd.service failed.

正如評論中所討論的,chrony最近收到了一個-x不嘗試更改系統時鐘的新選項,使其特別適用於容器操作。唉,接收這個選項的第一個版本(3.2)並不徹底,仍然要求 Linux 功能,所以仍然失敗。

使用選項從CentOS7 LXC 容器(帶有非 CentOS 主機)中的 chrony 版本 3.2-2.el7 中跟踪 chronyd -x,實際上錯誤修復不在這裡:

# strace /usr/sbin/chronyd -x -d

$$ … $$

[pid   571] capget({_LINUX_CAPABILITY_VERSION_3, 0}, NULL) = 0
[pid   571] capset({_LINUX_CAPABILITY_VERSION_3, 0}, {1<<CAP_NET_BIND_SERVICE|1<<CAP_SYS_TIME, 1<<CAP_NET_BIND_SERVICE|1<<CAP_SYS_TIME, 0}) = -1 EPERM (Operation not permitted)

因此,如果您可以阻止不可修改的二進制 chronyd 請求禁止的功能,它將執行(這就是 3.3 錯誤修復的內容)。好消息,可以使用LD_PRELOAD/dlsym()包裝器。

在其他地方的其他 Linux 系統上編譯(編譯實際上是在 Debian 9 主機上進行的,在 CentOS7 容器上執行沒有任何麻煩)這個程式碼稱為,例如在那裡capsetwrapper.c找到結構定義(這沒有t 從核心 3.10 更改)。

#define _GNU_SOURCE 1
#include <dlfcn.h>

#include <sys/capability.h>

int capset(cap_user_header_t hdrp, const cap_user_data_t datap) {
   int (*orig_capset)(cap_user_header_t,const cap_user_data_t)=dlsym(RTLD_NEXT,"capset");
   datap->effective &= ~ (1<<CAP_SYS_TIME);
   datap->permitted &= ~ (1<<CAP_SYS_TIME);
   return orig_capset(hdrp, datap);
}

使用這種特定方式(使庫適合LD_PRELOAD使用):

gcc -shared -fPIC -o libcapsetwrapper.so capsetwrapper.c -ldl

它的工作方式如下所示:

[root@centos7-amd64bis ~]# LD_PRELOAD=/root/libcapsetwrapper.so /usr/sbin/chronyd -x -d
2019-03-24T10:09:58Z chronyd version 3.2 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SECHASH +SIGND +ASYNCDNS +IPV6 +DEBUG)
2019-03-24T10:09:58Z Disabled control of system clock
2019-03-24T10:09:58Z Frequency 0.000 +/- 1000000.000 ppm read from /var/lib/chrony/drift

在執行時檢查其功能:

# egrep '^Cap(Prm|Eff)' /proc/$(pidof chronyd)/status
CapPrm: 0000000000000400
CapEff: 0000000000000400

1<<CAP_NET_BIND_SERVICE顯示 0x400 這是上面看到的剩餘部分。

要將其集成到系統中:

  • libcapsetwrapper.so包裝器放置為/usr/local/lib/libcapsetwrapper.so
  • 使用systemctl edit chronyd, 覆蓋CAP_SYS_TIME檢查,並且執行檔以此開頭:
[Unit]
ConditionCapability=

[Service]
ExecStart=
ExecStart=/bin/sh -c 'export LD_PRELOAD=/usr/local/lib/libcapsetwrapper.so; exec /usr/sbin/chronyd -x'

抱歉,我無法重用$OPTIONS參數(它是空的,應該重新接收-x選項,從 載入/etc/sysconfig/chronyd),但有了更多的系統知識,它應該是可能的。

工作結果:

# systemctl status chronyd
● chronyd.service - NTP client/server
  Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
 Drop-In: /etc/systemd/system/chronyd.service.d
          `-override.conf
  Active: active (running) since Sun 2019-03-24 10:24:26 UTC; 13min ago
    Docs: man:chronyd(8)
          man:chrony.conf(5)
 Process: 843 ExecStartPost=/usr/libexec/chrony-helper update-daemon (code=exited, status=0/SUCCESS)
 Process: 839 ExecStart=/bin/sh -c export LD_PRELOAD=/usr/local/lib/libcapsetwrapper.so; exec /usr/sbin/chronyd -x (code=exited, status=0/SUCCESS)
Main PID: 841 (chronyd)
  CGroup: /system.slice/chronyd.service
          `-841 /usr/sbin/chronyd -x

沒有測試的是預設的 SELinux 環境(此處不可用)是否允許預載入操作,或者是否應該做更多的事情/usr/local/lib/libcapsetwrapper.so(一定要使用restorecon它)。

引用自:https://unix.stackexchange.com/questions/468302