[202311][cherry-pick][NTP] Add NTP extended configuration (#17487)

* Add NTP YANG model

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Extend NTP config generation mechanism

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Add NTP YANG nodel tests

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Add test for NTP Jinja templates

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Add ntpdate package

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Fix 'bad' when auth disabled

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* [NTP] Changed owner for ntp keys config file to root and remove read access for other.

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Fix NTP warnings after restarting the service

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Add ability to encrypt/decrypt NTP keys

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Update Configuration reference

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Fix NTP configuration template

* Align the description for setting interface
* Fix the usage of scoped variable

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Fix YANG model description and tests

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Align NTP test according to fixed condition

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Allow eth0 to be as source ifc without defining it

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

* Update sample config with NTP config

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>

---------

Signed-off-by: Yevhen Fastiuk <yfastiuk@nvidia.com>
This commit is contained in:
Yevhen Fastiuk 2023-12-21 19:45:29 +02:00 committed by GitHub
parent bd8ed6bc6d
commit f78cb9c55c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 891 additions and 282 deletions

View File

@ -157,5 +157,15 @@
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M",
"num_dumps": "3"
}
},
"NTP": {
"global": {
"authentication": "disabled",
"dhcp": "enabled",
"server_role": "disabled",
"src_intf": "eth0",
"admin_state": "enabled",
"vrf": "default"
}
}
}

View File

@ -370,10 +370,13 @@ sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/flashrom_*.deb
sudo cp -f $IMAGE_CONFIGS/cron.d/* $FILESYSTEM_ROOT/etc/cron.d/
# Copy NTP configuration files and templates
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT \
apt-get -y install ntpdate
sudo cp $IMAGE_CONFIGS/ntp/ntp-config.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM
echo "ntp-config.service" | sudo tee -a $GENERATED_SERVICE_FILE
sudo cp $IMAGE_CONFIGS/ntp/ntp-config.sh $FILESYSTEM_ROOT/usr/bin/
sudo cp $IMAGE_CONFIGS/ntp/ntp.conf.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
sudo cp $IMAGE_CONFIGS/ntp/ntp.keys.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
sudo cp $IMAGE_CONFIGS/ntp/ntp-systemd-wrapper $FILESYSTEM_ROOT/usr/lib/ntp/
sudo cp $IMAGE_CONFIGS/ntp/ntp.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM
echo "ntp.service" | sudo tee -a $GENERATED_SERVICE_FILE

View File

@ -1,100 +0,0 @@
#!/bin/sh
# This file was originally created automatically as part of default NTP application installation from debian package.
# This is now manually modified for supporting NTP in management VRF.
# When management VRF is enabled, the NTP application should be started using "cgexec -g l3mdev:mgmt".
# Check has been added to verify the management VRF enabled status and use cgexec when it is enabled.
# This file will be copied on top of the etc/init.d/ntp file that gets created during build process.
### BEGIN INIT INFO
# Provides: ntp
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Start NTP daemon
### END INIT INFO
PATH=/sbin:/bin:/usr/sbin:/usr/bin
. /lib/lsb/init-functions
DAEMON=/usr/sbin/ntpd
PIDFILE=/var/run/ntpd.pid
test -x $DAEMON || exit 5
if [ -r /etc/default/ntp ]; then
. /etc/default/ntp
fi
if [ -e /run/ntp.conf.dhcp ]; then
NTPD_OPTS="$NTPD_OPTS -c /run/ntp.conf.dhcp"
fi
LOCKFILE=/run/lock/ntpdate
RUNASUSER=ntp
UGID=$(getent passwd $RUNASUSER | cut -f 3,4 -d:) || true
if test "$(uname -s)" = "Linux"; then
NTPD_OPTS="$NTPD_OPTS -u $UGID"
fi
case $1 in
start)
log_daemon_msg "Starting NTP server" "ntpd"
if [ -z "$UGID" ]; then
log_failure_msg "user \"$RUNASUSER\" does not exist"
exit 1
fi
(
flock -w 180 9
# when mgmt vrf is configured, ntp starts in mgmt vrf by default unless user configures otherwise
vrfEnabled=$(/usr/local/bin/sonic-cfggen -d -v 'MGMT_VRF_CONFIG["vrf_global"]["mgmtVrfEnabled"]' 2> /dev/null)
vrfConfigured=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["vrf"]' 2> /dev/null)
if [ "$vrfEnabled" = "true" ]
then
if [ "$vrfConfigured" = "default" ]
then
log_daemon_msg "Starting NTP server in default-vrf for default set as NTP vrf" "ntpd"
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
else
log_daemon_msg "Starting NTP server in mgmt-vrf" "ntpd"
cgexec -g l3mdev:mgmt start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
fi
else
log_daemon_msg "Starting NTP server in default-vrf" "ntpd"
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
fi
) 9>$LOCKFILE
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping NTP server" "ntpd"
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --retry=TERM/30/KILL/5 --exec $DAEMON
log_end_msg $?
rm -f $PIDFILE
;;
restart|force-reload)
$0 stop && sleep 2 && $0 start
;;
try-restart)
if $0 status >/dev/null; then
$0 restart
else
exit 0
fi
;;
reload)
exit 3
;;
status)
status_of_proc $DAEMON "NTP server"
;;
*)
echo "Usage: $0 {start|stop|restart|try-restart|force-reload|status}"
exit 2
;;
esac

View File

@ -24,6 +24,10 @@ function modify_ntp_default
}
sonic-cfggen -d -t /usr/share/sonic/templates/ntp.conf.j2 >/etc/ntp.conf
sonic-cfggen -d -t /usr/share/sonic/templates/ntp.keys.j2 >/etc/ntp.keys
chown root:ntp /etc/ntp.keys
chmod o-r /etc/ntp.keys
get_database_reboot_type
echo "Disabling NTP long jump for reboot type ${reboot_type} ..."

View File

@ -13,7 +13,8 @@ if [ -r /etc/default/ntp ]; then
. /etc/default/ntp
fi
if [ -e /run/ntp.conf.dhcp ]; then
dhcp=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["dhcp"]' 2> /dev/null)
if [ -e /run/ntp.conf.dhcp ] && [ "$dhcp" = "enabled" ]; then
NTPD_OPTS="$NTPD_OPTS -c /run/ntp.conf.dhcp"
fi
@ -27,6 +28,14 @@ fi
(
flock -w 180 9
ntpEnabled=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["admin_state"]' 2> /dev/null)
if [ "$ntpEnabled" = "disabled" ]
then
logger -p INFO -t "ntpd" "Stopping NTP daemon"
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
exit 0
fi
# when mgmt vrf is configured, ntp starts in mgmt vrf by default unless user configures otherwise
vrfEnabled=$(/usr/local/bin/sonic-cfggen -d -v 'MGMT_VRF_CONFIG["vrf_global"]["mgmtVrfEnabled"]' 2> /dev/null)
vrfConfigured=$(/usr/local/bin/sonic-cfggen -d -v 'NTP["global"]["vrf"]' 2> /dev/null)
@ -34,14 +43,14 @@ fi
then
if [ "$vrfConfigured" = "default" ]
then
log_daemon_msg "Starting NTP server in default-vrf for default set as NTP vrf" "ntpd"
logger -p INFO -t "ntpd" "Starting NTP server in default-vrf for default set as NTP vrf" "ntpd"
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
else
log_daemon_msg "Starting NTP server in mgmt-vrf" "ntpd"
logger -p INFO -t "ntpd" "Starting NTP server in mgmt-vrf"
ip vrf exec mgmt start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
fi
else
log_daemon_msg "Starting NTP server in default-vrf" "ntpd"
logger -p INFO -t "ntpd" "Starting NTP server in default-vrf"
start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE $NTPD_OPTS
fi
) 9>$LOCKFILE

View File

@ -1,45 +1,95 @@
###############################################################################
# Managed by Ansible
# file: ansible/roles/acs/templates/ntp.conf.j2
# This file was AUTOMATICALLY GENERATED. DO NOT MODIFY.
# Controlled by ntp-config.service
###############################################################################
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
# To avoid ntpd from panic and exit if the drift between new time and
# current system time is large.
tinker panic 0
driftfile /var/lib/ntp/ntp.drift
# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
{# Getting NTP global configuration -#}
{% set global = (NTP | d({})).get('global', {}) -%}
# You do need to talk to an NTP server or two (or three).
#server ntp.your-provider.example
{# Adding NTP servers. We need to know if we have some pools, to set proper
config -#}
{% set ns = namespace(is_pools=false) %}
{% for server in NTP_SERVER if NTP_SERVER[server].admin_state != 'disabled' and
NTP_SERVER[server].resolve_as and
NTP_SERVER[server].association_type -%}
{% set config = NTP_SERVER[server] -%}
{# Server options -#}
{% set soptions = '' -%}
{# Server access control options -#}
{% set aoptions = '' -%}
# pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will
# pick a different set every time it starts up. Please consider joining the
# pool: <http://www.pool.ntp.org/join.html>
{% for ntp_server in NTP_SERVER %}
server {{ ntp_server }} iburst
{# Authentication key -#}
{% if global.authentication == 'enabled' -%}
{% if config.key -%}
{% set soptions = soptions ~ ' key ' ~ config.key -%}
{% endif -%}
{% endif -%}
{# Aggressive polling -#}
{% if config.iburst -%}
{% set soptions = soptions ~ ' iburst' -%}
{% endif -%}
{# Protocol version -#}
{% if config.version -%}
{% set soptions = soptions ~ ' version ' ~ config.version -%}
{% endif -%}
{# Check if there are any pool configured. BTW it doesn't matter what was
configured as "resolve_as" for pools. If they were configured with FQDN they
must remain like that -#}
{% set config_as = config.resolve_as -%}
{% if config.association_type == 'pool' -%}
{% set ns.is_pools = true -%}
{% set config_as = server -%}
{% else -%}
{% set aoptions = aoptions ~ ' nopeer' -%}
{% endif -%}
{{ config.association_type }} {{ config_as }}{{ soptions }}
{% if global.server_role == 'disabled' %}
restrict {{ config_as }} kod limited nomodify notrap noquery{{ aoptions }}
{% endif %}
{% endfor -%}
{% set trusted_keys_arr = [] -%}
{% for key in NTP_KEY -%}
{% set keydata = NTP_KEY[key] -%}
{% if keydata.trusted == 'yes' -%}
{% set trusted_keys_arr = trusted_keys_arr.append(key) -%}
{% endif -%}
{% endfor %}
#listen on source interface if configured, else
#only listen on MGMT_INTERFACE, LOOPBACK_INTERFACE ip when MGMT_INTERFACE is not defined, or eth0
# if we don't have both of them (default is to listen on all ip addresses)
{% if global.authentication == 'enabled' %}
keys /etc/ntp.keys
{% if trusted_keys_arr != [] %}
trustedkey {{ trusted_keys_arr|join(' ') }}
{% endif %}
{% endif %}
{# listen on source interface if configured, else only listen on MGMT_INTERFACE,
LOOPBACK_INTERFACE ip when MGMT_INTERFACE is not defined, or eth0 if we don't
have both of them (default is to listen on all ip addresses) -#}
interface ignore wildcard
# set global variable for configured source interface name
# set global boolean to indicate if the ip of the configured source interface is configured
# if the source interface is configured but no ip on that interface, then listen on another
# interface based on existing logic
{# Set interface to listen on:
* Set global variable for configured source interface name.
* Set global boolean to indicate if the ip of the configured source
interface is configured.
* If the source interface is configured but no ip on that
interface, then listen on another interface based on existing logic. -#}
{%- macro check_ip_on_interface(interface_name, table_name) %}
{%- set ns = namespace(valid_intf = 'false') %}
{%- if table_name %}
@ -54,8 +104,8 @@ interface ignore wildcard
{% set ns = namespace(source_intf = "") %}
{% set ns = namespace(source_intf_ip = 'false') %}
{% if (NTP) and (NTP['global']['src_intf']) %}
{% set ns.source_intf = (NTP['global']['src_intf']) %}
{% if global.src_intf %}
{% set ns.source_intf = global.src_intf %}
{% if ns.source_intf != "" %}
{% if ns.source_intf == "eth0" %}
{% set ns.source_intf_ip = 'true' %}
@ -90,32 +140,24 @@ interface listen eth0
{% endif %}
interface listen 127.0.0.1
# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
# details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
# might also be helpful.
#
# Note that "restrict" applies to both servers and clients, so a configuration
# that might be intended to block requests from certain clients could also end
# up blocking replies from your own upstream servers.
{# Access control options -#}
{% set options = '' -%}
{# Allow additional servers mobilization from the pool. Otherwise we don't need
that -#}
{% if ns.is_pools == false -%}
{% set options = options ~ ' nopeer' -%}
{% endif -%}
{# Disable NTP server functionality. Should stay on when dhcp is enabled -#}
{# {% if global.server_role == 'disabled' and global.dhcp == 'disabled' -%}
{% set options = options ~ ' ignore' -%}
{% endif -%} #}
# Access control configuration
# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
restrict -4 default kod limited notrap nomodify noquery{{ options }}
restrict -6 default kod limited notrap nomodify noquery{{ options }}
# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1
# Clients from this (example!) subnet have unlimited access, but only if
# cryptographically authenticated.
#restrict 192.168.123.0 mask 255.255.255.0 notrust
# If you want to provide time to your local subnet, change the next line.
# (Again, the address is an example only.)
#broadcast 192.168.123.255
# If you want to listen to time broadcasts on your local subnet, de-comment the
# next lines. Please do this only if you trust everybody on the network!
#disable auth
#broadcastclient

View File

@ -0,0 +1,18 @@
###############################################################################
# This file was AUTOMATICALLY GENERATED. DO NOT MODIFY.
# Controlled by ntp-config.service
###############################################################################
{# We can connect only to the servers we trust. Determine those servers -#}
{% set trusted_arr = [] -%}
{% for server in NTP_SERVER if NTP_SERVER[server].trusted == 'yes' and
NTP_SERVER[server].resolve_as -%}
{% set _ = trusted_arr.append(NTP_SERVER[server].resolve_as) -%}
{% endfor -%}
{# Define authentication keys inventory -#}
{% set trusted_str = ' ' ~ trusted_arr|join(',') -%}
{% for keyid in NTP_KEY if NTP_KEY[keyid].type and NTP_KEY[keyid].value %}
{% set keyval = NTP_KEY[keyid].value | b64decode %}
{{ keyid }} {{ NTP_KEY[keyid].type }} {{ keyval }}{{trusted_str}}
{% endfor -%}

View File

@ -26,6 +26,7 @@ import os
import sys
import yaml
import ipaddress
import base64
from collections import OrderedDict
from config_samples import generate_sample_config, get_available_config
@ -138,6 +139,28 @@ def ip_network(value):
return "Invalid ip address %s" % value
return r_v.network
def b64encode(value):
"""Base64 encoder
Return:
encoded string or the same value in case of error
"""
try:
ret = base64.b64encode(value.encode()).decode()
except:
return value
return ret
def b64decode(value):
"""Base64 decoder
Return:
decoded string or the same value in case of error
"""
try:
ret = base64.b64decode(value.encode()).decode()
except:
return value
return ret
def get_primary_addr(value):
if not value:
return ""
@ -273,6 +296,10 @@ def _get_jinja2_env(paths):
for attr in ['ip', 'network', 'prefixlen', 'netmask', 'broadcast']:
env.filters[attr] = partial(prefix_attr, attr)
# Base64 encoder/decoder
env.filters['b64encode'] = b64encode
env.filters['b64decode'] = b64decode
return env
def main():

View File

@ -1,7 +1,49 @@
{
"NTP": {
"global": {
"src_intf": "Ethernet0"
"src_intf": "eth0",
"vrf": "default",
"authentication": "enabled",
"dhcp": "disabled",
"server_role": "disabled",
"admin_state": "enabled"
}
},
"NTP_SERVER": {
"my_ntp_server": {
"association_type": "server",
"iburst": "off",
"admin_state": "disabled",
"version": 3,
"resolve_as": "10.20.30.40"
},
"server2": {
"association_type": "server",
"iburst": "off",
"admin_state": "enabled",
"version": 3,
"resolve_as": "10.20.30.50",
"key": 42,
"trusted": "no"
},
"pool.ntp.org": {
"association_type": "pool",
"iburst": "on",
"admin_state": "enabled",
"version": 3,
"resolve_as": "pool.ntp.org"
}
},
"NTP_KEY": {
"1": {
"type": "md5",
"trusted": "no",
"value": "blabla"
},
"42": {
"type": "sha1",
"trusted": "yes",
"value": "the_answer"
}
},
"INTERFACE": {

View File

@ -0,0 +1 @@
../../../files/image_config/ntp/ntp.keys.j2

View File

@ -1,72 +0,0 @@
###############################################################################
# Managed by Ansible
# file: ansible/roles/acs/templates/ntp.conf.j2
###############################################################################
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
# To avoid ntpd from panic and exit if the drift between new time and
# current system time is large.
tinker panic 0
driftfile /var/lib/ntp/ntp.drift
# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
# You do need to talk to an NTP server or two (or three).
#server ntp.your-provider.example
# pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will
# pick a different set every time it starts up. Please consider joining the
# pool: <http://www.pool.ntp.org/join.html>
#listen on source interface if configured, else
#only listen on MGMT_INTERFACE, LOOPBACK_INTERFACE ip when MGMT_INTERFACE is not defined, or eth0
# if we don't have both of them (default is to listen on all ip addresses)
interface ignore wildcard
# set global variable for configured source interface name
# set global boolean to indicate if the ip of the configured source interface is configured
# if the source interface is configured but no ip on that interface, then listen on another
# interface based on existing logic
interface listen Ethernet0
interface listen 127.0.0.1
# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
# details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
# might also be helpful.
#
# Note that "restrict" applies to both servers and clients, so a configuration
# that might be intended to block requests from certain clients could also end
# up blocking replies from your own upstream servers.
# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1
# Clients from this (example!) subnet have unlimited access, but only if
# cryptographically authenticated.
#restrict 192.168.123.0 mask 255.255.255.0 notrust
# If you want to provide time to your local subnet, change the next line.
# (Again, the address is an example only.)
#broadcast 192.168.123.255
# If you want to listen to time broadcasts on your local subnet, de-comment the
# next lines. Please do this only if you trust everybody on the network!
#disable auth
#broadcastclient

View File

@ -0,0 +1 @@
../py3/ntp.conf

View File

@ -0,0 +1 @@
../py3/ntp.keys

View File

@ -1,72 +1,42 @@
###############################################################################
# Managed by Ansible
# file: ansible/roles/acs/templates/ntp.conf.j2
# This file was AUTOMATICALLY GENERATED. DO NOT MODIFY.
# Controlled by ntp-config.service
###############################################################################
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
# To avoid ntpd from panic and exit if the drift between new time and
# current system time is large.
tinker panic 0
driftfile /var/lib/ntp/ntp.drift
# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
server 10.20.30.50 key 42 iburst version 3
restrict 10.20.30.50 kod limited nomodify notrap noquery nopeer
# You do need to talk to an NTP server or two (or three).
#server ntp.your-provider.example
pool pool.ntp.org iburst version 3
restrict pool.ntp.org kod limited nomodify notrap noquery
# pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will
# pick a different set every time it starts up. Please consider joining the
# pool: <http://www.pool.ntp.org/join.html>
#listen on source interface if configured, else
#only listen on MGMT_INTERFACE, LOOPBACK_INTERFACE ip when MGMT_INTERFACE is not defined, or eth0
# if we don't have both of them (default is to listen on all ip addresses)
keys /etc/ntp.keys
trustedkey 42
interface ignore wildcard
# set global variable for configured source interface name
# set global boolean to indicate if the ip of the configured source interface is configured
# if the source interface is configured but no ip on that interface, then listen on another
# interface based on existing logic
interface listen Ethernet0
interface listen eth0
interface listen 127.0.0.1
# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
# details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
# might also be helpful.
#
# Note that "restrict" applies to both servers and clients, so a configuration
# that might be intended to block requests from certain clients could also end
# up blocking replies from your own upstream servers.
# Access control configuration
# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
restrict -4 default kod limited notrap nomodify noquery
restrict -6 default kod limited notrap nomodify noquery
# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1
# Clients from this (example!) subnet have unlimited access, but only if
# cryptographically authenticated.
#restrict 192.168.123.0 mask 255.255.255.0 notrust
# If you want to provide time to your local subnet, change the next line.
# (Again, the address is an example only.)
#broadcast 192.168.123.255
# If you want to listen to time broadcasts on your local subnet, de-comment the
# next lines. Please do this only if you trust everybody on the network!
#disable auth
#broadcastclient

View File

@ -0,0 +1,8 @@
###############################################################################
# This file was AUTOMATICALLY GENERATED. DO NOT MODIFY.
# Controlled by ntp-config.service
###############################################################################
1 md5 blabla
42 sha1 the_answer

View File

@ -663,10 +663,19 @@ class TestJ2Files(TestCase):
def test_ntp_conf(self):
conf_template = os.path.join(self.test_dir, "ntp.conf.j2")
ntp_interfaces_json = os.path.join(self.test_dir, "data", "ntp", "ntp_interfaces.json")
config_db_ntp_json = os.path.join(self.test_dir, "data", "ntp", "ntp_interfaces.json")
expected = os.path.join(self.test_dir, "sample_output", utils.PYvX_DIR, "ntp.conf")
argument = ['-j', ntp_interfaces_json, '-t', conf_template]
argument = ['-j', config_db_ntp_json, '-t', conf_template]
self.run_script(argument, output_file=self.output_file)
assert utils.cmp(expected, self.output_file), self.run_diff(expected, self.output_file)
def test_ntp_keys(self):
conf_template = os.path.join(self.test_dir, "ntp.keys.j2")
config_db_ntp_json = os.path.join(self.test_dir, "data", "ntp", "ntp_interfaces.json")
expected = os.path.join(self.test_dir, "sample_output", utils.PYvX_DIR, "ntp.keys")
argument = ['-j', config_db_ntp_json, '-t', conf_template]
self.run_script(argument, output_file=self.output_file)
assert utils.cmp(expected, self.output_file), self.run_diff(expected, self.output_file)

View File

@ -1538,6 +1538,35 @@ These configuration options are used to modify the way that
ntp binds to the ports on the switch and which port it uses to
make ntp update requests from.
***NTP Admin state***
If this option is set to `enabled` then ntp client will try to sync system time with configured NTP servers.
Otherwise, NTP client feature will be disabled.
```
{
"NTP": {
"global": {
"admin_state": "enabled"
}
}
}
```
***NTP Server role***
This option is used to control NTP server state on the switch.
If this option is set to `enabled` switch will act as NTP server.
By default `server_role` is `disabled`.
```
{
"NTP": {
"global": {
"server_role": "enabled"
}
}
}
```
***NTP VRF***
If this option is set to `default` then ntp will run within the default vrf
@ -1575,6 +1604,36 @@ for that address.
}
```
***NTP Authentication***
If this option is set to `enabled` then ntp will try to verify NTP servers it connects to.
This option **has no effect** if key is not set for NTP server.
By default it is `disabled`
```
{
"NTP": {
"global": {
"authentication": "enabled"
}
}
}
```
***NTP DHCP leases***
If this option is set to `enabled` then ntp client will try to use NTP servers provided by DHCP server.
If this option is set to `disabled` you will be able to use the user-configured NTP servers.
By default it is `enabled`
```
{
"NTP": {
"global": {
"dhcp": "enabled"
}
}
}
```
### NTP servers
These information are configured in individual tables. Domain name or IP
@ -1585,18 +1644,77 @@ attributes in those objects.
```
{
"NTP_SERVER": {
"2.debian.pool.ntp.org": {},
"1.debian.pool.ntp.org": {},
"3.debian.pool.ntp.org": {},
"0.debian.pool.ntp.org": {}
"2.debian.pool.ntp.org": {
"association_type": "pool",
"iburst": "on",
"admin_state": "enabled",
"version": 4
},
"1.debian.pool.ntp.org": {
"association_type": "pool",
"iburst": "off",
"admin_state": "enabled",
"version": 3
},
"3.debian.pool.ntp.org": {
"association_type": "pool",
"iburst": "on",
"admin_state": "disabled",
"version": 4
},
"0.debian.pool.ntp.org": {
"association_type": "pool",
"iburst": "off",
"admin_state": "disabled",
"version": 3
}
},
"NTP_SERVER": {
"23.92.29.245": {},
"204.2.134.164": {}
"23.92.29.245": {
"association_type": "server",
"iburst": "on",
"admin_state": "enabled",
"version": 4,
"key": 3,
"trusted": "yes"
},
"204.2.134.164": {
"association_type": "server",
"iburst": "on",
"admin_state": "enabled",
"version": 3
}
}
}
```
* `association_type` - is used to control the type of the server. It can be `server` or `pool`.
* `iburst` - agressive server polling `{on, off}`.
* `version` - NTP protool version to use `[3..4]`.
* `key` - authentication key id `[1..65535]` to use to auth the server.
* `admin_state` - enable or disable specific server.
* `trusted` - trust this server when auth is enabled.
***NTP keys***
```
{
"NTP_KEY": {
"1": {
"type": "md5",
"value": "bXlwYXNzd29yZA==",
"trusted": "yes"
},
"42": {
"type": "sha1",
"value": "dGhlYW5zd2Vy",
"trusted": "no"
}
}
}
```
* `type` - key type to use `{md5, sha1, sha256, sha384, sha512}`.
* `value` - base64 encoded key value.
* `trusted` - trust this NTP key `{yes, no}`.
### Peer Switch

View File

@ -477,14 +477,36 @@
},
"NTP": {
"global": {
"authentication": "disabled",
"dhcp": "enabled",
"server_role": "disabled",
"admin_state": "enabled",
"vrf": "mgmt",
"src_intf": "eth0;Loopback0"
}
},
"NTP_SERVER": {
"0.debian.pool.ntp.org": {},
"23.92.29.245": {},
"2001:aa:aa::aa": {}
"0.debian.pool.ntp.org": {
"association_type": "pool",
"resolve_as": "0.debian.pool.ntp.org"
},
"time.google.com": {
"association_type": "server",
"resolve_as": "216.239.35.4"
},
"23.92.29.245": {
"admin_state": "enabled",
"association_type": "server",
"resolve_as": "23.92.29.245",
"iburst": "off",
"trusted": "yes"
},
"2001:aa:aa::aa": {
"admin_state": "disabled",
"iburst": "on",
"association_type": "server",
"resolve_as": "2001:aa:aa::aa"
}
},
"SYSLOG_SERVER" : {
"10.13.14.17": {

View File

@ -58,5 +58,82 @@
"desc": "CONFIGURE NON-EXISTING MGMT INTERFACE AS NTP SOURCE INTERFACE.",
"eStrKey": "InvalidValue",
"eStr": ["src"]
},
"NTP_GLOB_VALID1": {
"desc": "NTP global params valid config 1"
},
"NTP_GLOB_VALID2": {
"desc": "NTP global params valid config 2"
},
"NTP_AUTH_INVALID1": {
"desc": "NTP authentication state invalid 1",
"eStrKey": "InvalidValue"
},
"NTP_AUTH_INVALID2": {
"desc": "NTP authentication state invalid 2",
"eStrKey": "InvalidValue"
},
"NTP_DHCP_INVALID1": {
"desc": "NTP DHCP state invalid 1",
"eStrKey": "InvalidValue"
},
"NTP_DHCP_INVALID2": {
"desc": "NTP DHCP state invalid 2",
"eStrKey": "InvalidValue"
},
"NTP_SERVER_ROLE_INVALID1": {
"desc": "NTP server role state invalid 1",
"eStrKey": "InvalidValue"
},
"NTP_SERVER_ROLE_INVALID2": {
"desc": "NTP server role state invalid 2",
"eStrKey": "InvalidValue"
},
"NTP_STATE_INVALID1": {
"desc": "NTP daemon state invalid 1",
"eStrKey": "InvalidValue"
},
"NTP_STATE_INVALID2": {
"desc": "NTP daemon state invalid 2",
"eStrKey": "InvalidValue"
},
"NTP_SERVER_ASSOCIATION_INVALID": {
"desc": "NTP server type invalid",
"eStrKey": "InvalidValue"
},
"NTP_SERVER_IBURST_INVALID": {
"desc": "NTP server aggressive mode invalid",
"eStrKey": "InvalidValue"
},
"NTP_SERVER_KEY_INVALID": {
"desc": "NTP server authentication key invalid",
"eStrKey": "InvalidValue"
},
"NTP_SERVER_STATE_INVALID": {
"desc": "NTP server state invalid",
"eStrKey": "InvalidValue"
},
"NTP_SERVER_TRUSTED_INVALID": {
"desc": "NTP server trusted mode invalid",
"eStrKey": "InvalidValue"
},
"NTP_KEY_VALID": {
"desc": "NTP authentication keys inventory"
},
"NTP_KEY_ID_INVALID": {
"desc": "NTP authentication keys invalid key id",
"eStrKey": "InvalidValue"
},
"NTP_KEY_TRUSTED_INVALID": {
"desc": "NTP authentication keys invalid trustiness",
"eStrKey": "InvalidValue"
},
"NTP_KEY_TYPE_INVALID": {
"desc": "NTP authentication keys invalid key type",
"eStrKey": "InvalidValue"
},
"NTP_KEY_VALUE_INVALID": {
"desc": "NTP authentication keys bad key value",
"eStrKey": "Range"
}
}

View File

@ -4,13 +4,38 @@
"sonic-ntp:NTP_SERVER": {
"NTP_SERVER_LIST": [
{
"server_address": "10.11.12.13"
"server_address": "10.11.12.13",
"association_type": "server",
"iburst": "on",
"key": 10,
"admin_state": "enabled",
"trusted": "no"
},
{
"server_address": "2001:aa:aa::aa"
"server_address": "2001:aa:aa::aa",
"association_type": "server",
"iburst": "off",
"key": 15,
"admin_state": "disabled",
"trusted": "yes"
},
{
"server_address": "pool.ntp.org"
"server_address": "pool.ntp.org",
"association_type": "pool",
"iburst": "on",
"admin_state": "enabled"
}
]
},
"sonic-ntp:NTP_KEY": {
"NTP_KEYS_LIST": [
{
"id": 10,
"value": "bHVtb3M="
},
{
"id": 15,
"value": "Ym9tYmFyZGE="
}
]
}
@ -237,5 +262,234 @@
]
}
}
},
"NTP_GLOB_VALID1": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"authentication": "enabled",
"dhcp": "enabled",
"server_role": "enabled",
"admin_state": "enabled"
}
}
}
},
"NTP_GLOB_VALID2": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"authentication": "disabled",
"dhcp": "disabled",
"server_role": "disabled",
"admin_state": "disabled"
}
}
}
},
"NTP_AUTH_INVALID1": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"authentication": ""
}
}
}
},
"NTP_AUTH_INVALID2": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"authentication": "blahblah"
}
}
}
},
"NTP_DHCP_INVALID1": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"dhcp": ""
}
}
}
},
"NTP_DHCP_INVALID2": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"dhcp": "abracadabra"
}
}
}
},
"NTP_SERVER_ROLE_INVALID1": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"server_role": ""
}
}
}
},
"NTP_SERVER_ROLE_INVALID2": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"server_role": "olololo"
}
}
}
},
"NTP_STATE_INVALID1": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"admin_state": ""
}
}
}
},
"NTP_STATE_INVALID2": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP": {
"sonic-ntp:global": {
"admin_state": "azazaza"
}
}
}
},
"NTP_SERVER_ASSOCIATION_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_SERVER": {
"NTP_SERVER_LIST": [
{
"server_address": "2001:aa:aa:aa",
"association_type": "puul"
}
]
}
}
},
"NTP_SERVER_IBURST_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_SERVER": {
"NTP_SERVER_LIST": [
{
"server_address": "2001:aa:aa:aa",
"iburst": "of"
}
]
}
}
},
"NTP_SERVER_KEY_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_SERVER": {
"NTP_SERVER_LIST": [
{
"server_address": "2001:aa:aa:aa",
"key": 0
}
]
}
}
},
"NTP_SERVER_STATE_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_SERVER": {
"NTP_SERVER_LIST": [
{
"server_address": "2001:aa:aa:aa",
"admin_state": "enable"
}
]
}
}
},
"NTP_SERVER_TRUSTED_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_SERVER": {
"NTP_SERVER_LIST": [
{
"server_address": "2001:aa:aa:aa",
"trusted": "not"
}
]
}
}
},
"NTP_KEY_VALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_KEY": {
"NTP_KEYS_LIST": [
{
"id": 20,
"type": "md5",
"value": "anNkZjg4MzIwZnNkMkBANDQ1",
"trusted": "no"
},
{
"id": 30,
"type": "sha1",
"value": "YWFiYmNjZGRlZWZm",
"trusted": "yes"
},
{
"id": 42,
"type": "md5",
"value": "dGhlYW5zd2Vy",
"trusted": "yes"
}
]
}
}
},
"NTP_KEY_ID_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_KEY": {
"NTP_KEYS_LIST": [
{
"id": 100000
}
]
}
}
},
"NTP_KEY_TRUSTED_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_KEY": {
"NTP_KEYS_LIST": [
{
"id": 20,
"trusted": "nope"
}
]
}
}
},
"NTP_KEY_TYPE_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_KEY": {
"NTP_KEYS_LIST": [
{
"id": 20,
"type": "md6"
}
]
}
}
},
"NTP_KEY_VALUE_INVALID": {
"sonic-ntp:sonic-ntp": {
"sonic-ntp:NTP_KEY": {
"NTP_KEYS_LIST": [
{
"id": 20,
"value": ""
}
]
}
}
}
}

View File

@ -33,6 +33,10 @@ module sonic-ntp {
prefix mprt;
}
import sonic-types {
prefix stypes;
}
description
"NTP yang Module for SONiC OS";
@ -41,6 +45,39 @@ module sonic-ntp {
"First revision";
}
revision 2023-03-20 {
description
"Add extended configuration options";
}
typedef association-type {
description "NTP server association type";
type enumeration {
enum server;
enum pool;
}
}
typedef key-type {
description "NTP key encryption type";
type enumeration {
enum md5;
enum sha1;
enum sha256;
enum sha384;
enum sha512;
}
}
typedef key-id {
description "NTP key ID";
type uint16 {
range 1..65535 {
error-message "Failed NTP key ID";
}
}
}
container sonic-ntp {
container NTP {
@ -68,6 +105,9 @@ module sonic-ntp {
type leafref {
path /mprt:sonic-mgmt_port/mprt:MGMT_PORT/mprt:MGMT_PORT_LIST/mprt:name;
}
type string {
pattern 'eth0';
}
}
description
@ -92,6 +132,30 @@ module sonic-ntp {
default VRF or Management VRF.";
}
leaf authentication {
type stypes:admin_mode;
default disabled;
description "NTP authentication state";
}
leaf dhcp {
type stypes:admin_mode;
default enabled;
description "Use NTP servers distributed by DHCP";
}
leaf server_role {
type stypes:admin_mode;
default enabled;
description "NTP server functionality state";
}
leaf admin_state {
type stypes:admin_mode;
default enabled;
description "NTP feature state";
}
} /* end of container global */
} /* end of container NTP */
@ -112,10 +176,95 @@ module sonic-ntp {
leaf server_address {
type inet:host;
}
leaf association_type {
type association-type;
default server;
description "NTP remote association type: server or pool.";
}
leaf iburst {
type stypes:on-off;
default on;
description "NTP aggressive polling";
}
leaf key {
description "NTP server key ID";
type leafref {
path /ntp:sonic-ntp/ntp:NTP_KEY/ntp:NTP_KEYS_LIST/ntp:id;
}
}
leaf resolve_as {
type inet:host;
description "Server resolved IP address";
}
leaf admin_state {
type stypes:admin_mode;
default enabled;
description "NTP server state";
}
leaf trusted {
type stypes:yes-no;
default no;
description "Trust this server. It will force time
synchronization only to this server when
authentication is enabled";
}
leaf version {
type uint8 {
range "3..4" {
error-message "Failed NTP version";
}
}
default 4;
description "NTP proto version to communicate with NTP
server";
}
} /* end of list NTP_SERVER_LIST */
} /* end of container NTP_SERVER */
container NTP_KEY {
description "NTP authentication keys inventory";
list NTP_KEYS_LIST {
description "NTP authentication keys inventory";
key "id";
leaf id {
type key-id;
description "NTP key ID";
}
leaf trusted {
type stypes:yes-no;
default no;
description "Trust this NTP key";
}
leaf value {
type string {
length 1..64;
}
description "NTP encrypted authentication key";
}
leaf type {
type key-type;
default md5;
description "NTP authentication key type";
}
} /* end of list NTP_KEYS_LIST */
} /* end of container NTP_KEY */
} /* end of container sonic-ntp */
} /* end of module sonic-ntp */

View File

@ -360,6 +360,22 @@ module sonic-types {
"BCP 175: Procedures for Maintaining the Time Zone Database";
}
typedef yes-no {
description "Yes/No configuration";
type enumeration {
enum yes;
enum no;
}
}
typedef on-off {
description "On/Off configuration";
type enumeration {
enum on;
enum off;
}
}
{% if yang_model_type == "cvl" %}
/* Required for CVL */
container operation {