Skip to content

Commit 5efb123

Browse files
authored
[NTP] Add NTP extended configuration (#15058)
hld [#1296](sonic-net/SONiC#1296) closes [#1254](sonic-net/SONiC#1254) depends-on [#60](sonic-net/sonic-host-services#60), [#781](sonic-net/sonic-swss-common#781), [#2835](sonic-net/sonic-utilities#2835), [#10749](sonic-net/sonic-mgmt#10749) #### Why I did it To cover the next AIs: * Configure NTP global parameters * Add/remove new NTP servers * Change the configuration for NTP servers * Show NTP status * Show NTP configuration ### How I did it * Add YANG model for a new configuration * Extend configuration templates to support new knobs ### Description for the changelog * Add ability to configure NTP global parameters such as authentication, dhcp, admin state * Change the configuration for NTP servers * Add an ability to show NTP configuration #### Link to config_db schema for YANG module changes [NTP configuration](https://github.com/sonic-net/sonic-buildimage/blob/master/src/sonic-yang-models/doc/Configuration.md#ntp-and-syslog-servers)
1 parent b0bb3d4 commit 5efb123

File tree

21 files changed

+888
-244
lines changed

21 files changed

+888
-244
lines changed

files/build_templates/init_cfg.json.j2

+10
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,15 @@
157157
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M",
158158
"num_dumps": "3"
159159
}
160+
},
161+
"NTP": {
162+
"global": {
163+
"authentication": "disabled",
164+
"dhcp": "enabled",
165+
"server_role": "disabled",
166+
"src_intf": "eth0",
167+
"admin_state": "enabled",
168+
"vrf": "default"
169+
}
160170
}
161171
}

files/build_templates/sonic_debian_extension.j2

+4
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,14 @@ sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/flashrom_*.deb
373373
sudo cp -f $IMAGE_CONFIGS/cron.d/* $FILESYSTEM_ROOT/etc/cron.d/
374374

375375
# Copy NTP configuration files and templates
376+
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT \
377+
apt-get -y install ntpdate
378+
sudo rm -f $FILESYSTEM_ROOT/etc/network/if-up.d/ntpsec-ntpdate
376379
sudo cp $IMAGE_CONFIGS/ntp/ntp-config.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM
377380
echo "ntp-config.service" | sudo tee -a $GENERATED_SERVICE_FILE
378381
sudo cp $IMAGE_CONFIGS/ntp/ntp-config.sh $FILESYSTEM_ROOT/usr/bin/
379382
sudo cp $IMAGE_CONFIGS/ntp/ntp.conf.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
383+
sudo cp $IMAGE_CONFIGS/ntp/ntp.keys.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
380384
sudo cp $IMAGE_CONFIGS/ntp/ntp-systemd-wrapper $FILESYSTEM_ROOT/usr/libexec/ntpsec/
381385
sudo mkdir $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM/ntpsec.service.d
382386
sudo cp $IMAGE_CONFIGS/ntp/sonic-target.conf $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM/ntpsec.service.d/

files/image_config/ntp/ntp-config.sh

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ function modify_ntp_default
2424
}
2525

2626
sonic-cfggen -d -t /usr/share/sonic/templates/ntp.conf.j2 >/etc/ntpsec/ntp.conf
27+
sonic-cfggen -d -t /usr/share/sonic/templates/ntp.keys.j2 >/etc/ntpsec/ntp.keys
28+
chmod o-r /etc/ntp.keys
2729

2830
get_database_reboot_type
2931
echo "Disabling NTP long jump for reboot type ${reboot_type} ..."

files/image_config/ntp/ntp-systemd-wrapper

+10-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ if [ -r /etc/default/ntpsec ]; then
1313
. /etc/default/ntpsec
1414
fi
1515

16-
if [ "$IGNORE_DHCP" != "yes" ] && [ -e /run/ntpsec/ntp.conf.dhcp ]; then
16+
dhcp=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["dhcp"]' 2> /dev/null)
17+
if [ "$IGNORE_DHCP" != "yes" ] && [ -e /run/ntpsec/ntp.conf.dhcp ] && [ "$dhcp" = "enabled" ]; then
1718
NTPD_OPTS="$NTPD_OPTS -c /run/ntpsec/ntp.conf.dhcp"
1819
else
1920
# List the default -c first, so if the admin has specified -c in
@@ -26,6 +27,14 @@ NTPD_OPTS="$NTPD_OPTS -u ntpsec:ntpsec"
2627
# Protect the service startup against concurrent ntpdate ifup hooks
2728
(
2829
if flock -w 180 9; then
30+
ntpEnabled=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["admin_state"]' 2> /dev/null)
31+
if [ "$ntpEnabled" = "disabled" ]
32+
then
33+
echo "Stopping NTP daemon"
34+
kill -9 $(cat $PIDFILE)
35+
exit 0
36+
fi
37+
2938
# when mgmt vrf is configured, ntp starts in mgmt vrf by default unless user configures otherwise
3039
vrfEnabled=$(/usr/local/bin/sonic-cfggen -d -v 'MGMT_VRF_CONFIG["vrf_global"]["mgmtVrfEnabled"]' 2> /dev/null)
3140
vrfConfigured=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["vrf"]' 2> /dev/null)

files/image_config/ntp/ntp.conf.j2

+88-38
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
###############################################################################
2-
# Managed by Ansible
3-
# file: ansible/roles/acs/templates/ntp.conf.j2
2+
# This file was AUTOMATICALLY GENERATED. DO NOT MODIFY.
3+
# Controlled by ntp-config.service
44
###############################################################################
55

6-
# /etc/ntpsec/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
6+
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
77

88
# To avoid ntpd from panic and exit if the drift between new time and
99
# current system time is large.
@@ -12,35 +12,82 @@ tinker panic 0
1212
driftfile /var/lib/ntpsec/ntp.drift
1313
leapfile /usr/share/zoneinfo/leap-seconds.list
1414

15-
# To enable Network Time Security support as a server, obtain a certificate
16-
# (e.g. with Let's Encrypt), configure the paths below, and uncomment:
17-
# nts cert CERT_FILE
18-
# nts key KEY_FILE
19-
# nts enable
20-
21-
# You must create /var/log/ntpsec (owned by ntpsec:ntpsec) to enable logging.
22-
#statsdir /var/log/ntpsec/
23-
#statistics loopstats peerstats clockstats
24-
#filegen loopstats file loopstats type day enable
25-
#filegen peerstats file peerstats type day enable
26-
#filegen clockstats file clockstats type day enable
27-
28-
# Specify one or more NTP servers.
29-
30-
# Public NTP servers supporting Network Time Security:
31-
# server time.cloudflare.com nts
32-
{% for ntp_server in NTP_SERVER %}
33-
server {{ ntp_server }} iburst
15+
{# Getting NTP global configuration -#}
16+
{% set global = (NTP | d({})).get('global', {}) -%}
17+
18+
{# Adding NTP servers. We need to know if we have some pools, to set proper
19+
config -#}
20+
{% set ns = namespace(is_pools=false) %}
21+
{% for server in NTP_SERVER if NTP_SERVER[server].admin_state != 'disabled' and
22+
NTP_SERVER[server].resolve_as and
23+
NTP_SERVER[server].association_type -%}
24+
{% set config = NTP_SERVER[server] -%}
25+
{# Server options -#}
26+
{% set soptions = '' -%}
27+
{# Server access control options -#}
28+
{% set aoptions = '' -%}
29+
30+
{# Authentication key -#}
31+
{% if global.authentication == 'enabled' -%}
32+
{% if config.key -%}
33+
{% set soptions = soptions ~ ' key ' ~ config.key -%}
34+
{% endif -%}
35+
{% endif -%}
36+
37+
{# Aggressive polling -#}
38+
{% if config.iburst -%}
39+
{% set soptions = soptions ~ ' iburst' -%}
40+
{% endif -%}
41+
42+
{# Protocol version -#}
43+
{% if config.version -%}
44+
{% set soptions = soptions ~ ' version ' ~ config.version -%}
45+
{% endif -%}
46+
47+
{# Check if there are any pool configured. BTW it doesn't matter what was
48+
configured as "resolve_as" for pools. If they were configured with FQDN they
49+
must remain like that -#}
50+
{% set config_as = config.resolve_as -%}
51+
{% if config.association_type == 'pool' -%}
52+
{% set ns.is_pools = true -%}
53+
{% set config_as = server -%}
54+
{% else -%}
55+
{% set aoptions = aoptions ~ ' nopeer' -%}
56+
{% endif -%}
57+
58+
{{ config.association_type }} {{ config_as }}{{ soptions }}
59+
{% if global.server_role == 'disabled' %}
60+
restrict {{ config_as }} kod limited nomodify notrap noquery{{ aoptions }}
61+
{% endif %}
62+
63+
{% endfor -%}
64+
65+
{% set trusted_keys_arr = [] -%}
66+
{% for key in NTP_KEY -%}
67+
{% set keydata = NTP_KEY[key] -%}
68+
{% if keydata.trusted == 'yes' -%}
69+
{% set trusted_keys_arr = trusted_keys_arr.append(key) -%}
70+
{% endif -%}
3471
{% endfor %}
3572

36-
# pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will
37-
# pick a different set every time it starts up. Please consider joining the
38-
# pool: <https://www.pool.ntp.org/join.html>
73+
{% if global.authentication == 'enabled' %}
74+
keys /etc/ntpsec/ntp.keys
75+
{% if trusted_keys_arr != [] %}
76+
trustedkey {{ trusted_keys_arr|join(' ') }}
77+
{% endif %}
78+
{% endif %}
3979

40-
#listen on source interface if configured, else
41-
#only listen on MGMT_INTERFACE, LOOPBACK_INTERFACE ip when MGMT_INTERFACE is not defined, or eth0
42-
# if we don't have both of them (default is to listen on all ip addresses)
80+
{# listen on source interface if configured, else only listen on MGMT_INTERFACE,
81+
LOOPBACK_INTERFACE ip when MGMT_INTERFACE is not defined, or eth0 if we don't
82+
have both of them (default is to listen on all ip addresses) -#}
4383
interface ignore wildcard
84+
85+
{# Set interface to listen on:
86+
* Set global variable for configured source interface name.
87+
* Set global boolean to indicate if the ip of the configured source
88+
interface is configured.
89+
* If the source interface is configured but no ip on that
90+
interface, then listen on another interface based on existing logic. -#}
4491
{%- macro check_ip_on_interface(interface_name, table_name) %}
4592
{%- set ns = namespace(valid_intf = 'false') %}
4693
{%- if table_name %}
@@ -55,8 +102,8 @@ interface ignore wildcard
55102

56103
{% set ns = namespace(source_intf = "") %}
57104
{%- set ns = namespace(source_intf_ip = 'false') %}
58-
{%- if (NTP) and (NTP['global']['src_intf']) %}
59-
{%- set ns.source_intf = (NTP['global']['src_intf']) %}
105+
{%- if global.src_intf %}
106+
{%- set ns.source_intf = global.src_intf %}
60107
{%- if ns.source_intf != "" %}
61108
{%- if ns.source_intf == "eth0" %}
62109
{%- set ns.source_intf_ip = 'true' %}
@@ -91,16 +138,19 @@ interface listen eth0
91138
{% endif %}
92139
interface listen 127.0.0.1
93140

94-
# Access control configuration; see /usr/share/doc/ntpsec-doc/html/accopt.html
95-
# for details.
96-
#
97-
# Note that "restrict" applies to both servers and clients, so a configuration
98-
# that might be intended to block requests from certain clients could also end
99-
# up blocking replies from your own upstream servers.
141+
{# Access control options -#}
142+
{% set options = '' -%}
143+
144+
{# Disable NTP server functionality. Should stay on when dhcp is enabled -#}
145+
{# {% if global.server_role == 'disabled' and global.dhcp == 'disabled' -%}
146+
{% set options = options ~ ' ignore' -%}
147+
{% endif -%} #}
100148

149+
# Access control configuration
101150
# By default, exchange time with everybody, but don't allow configuration.
102-
# NTPsec doesn't establish peer associations, and so nopeer has no effect, and has been removed from here
103-
restrict default kod nomodify noquery limited
151+
# NTPsec doesn't establish peer associations, and so nopeer has no effect, and
152+
# has been removed from here
153+
restrict default kod nomodify noquery limited{{ options }}
104154

105155
# Local users may interrogate the ntp server more closely.
106156
restrict 127.0.0.1

files/image_config/ntp/ntp.keys.j2

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
###############################################################################
2+
# This file was AUTOMATICALLY GENERATED. DO NOT MODIFY.
3+
# Controlled by ntp-config.service
4+
###############################################################################
5+
6+
{# We can connect only to the servers we trust. Determine those servers -#}
7+
{% set trusted_arr = [] -%}
8+
{% for server in NTP_SERVER if NTP_SERVER[server].trusted == 'yes' and
9+
NTP_SERVER[server].resolve_as -%}
10+
{% set _ = trusted_arr.append(NTP_SERVER[server].resolve_as) -%}
11+
{% endfor -%}
12+
13+
{# Define authentication keys inventory -#}
14+
{% set trusted_str = ' ' ~ trusted_arr|join(',') -%}
15+
{% for keyid in NTP_KEY if NTP_KEY[keyid].type and NTP_KEY[keyid].value %}
16+
{% set keyval = NTP_KEY[keyid].value | b64decode %}
17+
{{ keyid }} {{ NTP_KEY[keyid].type }} {{ keyval }}{{trusted_str}}
18+
{% endfor -%}

files/image_config/ntp/ntpsec

-100
This file was deleted.

src/sonic-config-engine/sonic-cfggen

+28
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import os
2626
import sys
2727
import yaml
2828
import ipaddress
29+
import base64
2930

3031
from collections import OrderedDict
3132
from config_samples import generate_sample_config, get_available_config
@@ -139,6 +140,29 @@ def ip_network(value):
139140
return "Invalid ip address %s" % value
140141
return r_v.network
141142

143+
def b64encode(value):
144+
"""Base64 encoder
145+
Return:
146+
encoded string or the same value in case of error
147+
"""
148+
try:
149+
ret = base64.b64encode(value.encode()).decode()
150+
except:
151+
return value
152+
return ret
153+
154+
155+
def b64decode(value):
156+
"""Base64 decoder
157+
Return:
158+
decoded string or the same value in case of error
159+
"""
160+
try:
161+
ret = base64.b64decode(value.encode()).decode()
162+
except:
163+
return value
164+
return ret
165+
142166
def get_primary_addr(value):
143167
if not value:
144168
return ""
@@ -274,6 +298,10 @@ def _get_jinja2_env(paths):
274298
for attr in ['ip', 'network', 'prefixlen', 'netmask', 'broadcast']:
275299
env.filters[attr] = partial(prefix_attr, attr)
276300

301+
# Base64 encoder/decoder
302+
env.filters['b64encode'] = b64encode
303+
env.filters['b64decode'] = b64decode
304+
277305
return env
278306

279307
def main():

0 commit comments

Comments
 (0)