如何在LXC下仅将ntpd作为服务器运行,而不调整本地机器时间

如何在LXC下仅将ntpd作为服务器运行,而不调整本地机器时间

我需要将设备的时钟同步到 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 [email protected] 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.

答案1

正如评论中所讨论的,慢性的最近收到了一个新选项-x,不尝试更改系统时钟,使其特别适合容器操作。可惜的是,接收此选项的第一个版本(3.2)并不彻底,并且仍然请求 Linux 功能,因此仍然失败。

施展CentOS7 LXC 容器(带有非 CentOS 主机)中的 chronyd 包 chrony 版本 3.2-2.el7 带有选项-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,找到了结构定义那里例如(这在内核 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

显示 0x400,这是上面看到的剩余部分1<<CAP_NET_BIND_SERVICE

要将其集成到系统中:

  • 将包装纸放置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),但有了更多的 systemd 知识,这应该是可能的。

工作结果:

# 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它)。

答案2

自版本以来1908年7月7日CentOS有chrony版本3.4-x正确支持该标志,并且可以在 LXC 下运行,只需进行一些小的配置调整。

  • 默认情况/usr/lib/systemd/system/chronyd.service下,在 LXC 下ConditionCapability=CAP_SYS_TIME运行时不应该存在-x,因此通过在文件中添加此删除项来删除它/etc/systemd/system/chronyd.service.d/lxc.conf
    [Unit]
    # Default service has:
    # ConditionCapability=CAP_SYS_TIME
    # which does not work under LXC and causes service to refuse to start
    # Clear it.
    ConditionCapability=
  • 编辑/etc/sysconfig/chronyd并添加-xOPTIONS最后OPTIONS="-x"

  • 更改/etc/chrony.conf为不同步到任何 NTP 服务器,而是从本地时钟提供时间。删除/禁用所有其他server条目,并添加:

    # Comment out default/other server entries
    #server 0.centos.pool.ntp.org iburst
    #server 1.centos.pool.ntp.org iburst
    #server 2.centos.pool.ntp.org iburst
    #server 3.centos.pool.ntp.org iburst
    #
    # Add these as per https://www.thegeekdiary.com/centos-rhel-7-chrony-how-to-sync-to-local-clock/
    server 127.127.1.0
    local stratum 10
    allow all

请查看allow指令以满足您的需求,因为allow all示例中的内容可能不适合您。

  • 重新加载并重新启动服务
    systemctl daemon-reload
    systemctl restart chronyd

应该是这样。

答案3

我找到了一种让它工作的方法,但我对我的解决方案不满意,所以我仍在寻找更好的答案。

第 1 部分:我阻止ntpd尝试放弃 root 权限。

其中/usr/lib/systemd/system/ntpd.service默认有:

[Service]
EnvironmentFile=-/etc/sysconfig/ntpd
ExecStart=/usr/sbin/ntpd -u ntp:ntp $OPTIONS

-u ntp:ntp所以我通过放置“删除”/etc/systemd/system/ntpd.service.d/local.conf

[Service]
ExecStart=
ExecStart=/usr/sbin/ntpd $OPTIONS

这允许ntpd继续以 root 身份运行。它有效,但我对这种方法不满意。

第2部分.不更新当地时间部分

添加/etc/ntp.confdisable kernel会导致ntpd服务启动时ntp_adjtime() failed: Operation not permitted仅记录一次错误,而不是在没有设置的情况下记录 3 次错误。由于它不能改变时钟,这是可以接受的。理想情况下,它甚至不会尝试改变时钟。

相关内容