Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,120 @@
# disruption = low
{{{ ansible_instantiate_variables("var_multiple_time_servers") }}}

- name: "Detect if chrony is already configured with pools or servers"
ansible.builtin.find:
path: /etc
patterns: chrony.conf
contains: '^[\s]*(?:server|pool)[\s]+[\w]+'
register: chrony_servers

- name: "Configure remote time servers"
- name: "Check if chrony main config has active server/pool entries"
ansible.builtin.command:
cmd: grep -q '^[[:space:]]*\(server\|pool\)[[:space:]]\+[[:graph:]]\+' {{{ chrony_conf_path }}}
register: chrony_conf_has_servers
changed_when: false
failed_when: false

- name: "Extract sourcedir paths from chrony configuration"
ansible.builtin.shell:
cmd: grep '^[[:space:]]*sourcedir[[:space:]]\+' {{{ chrony_conf_path }}} | awk '{print $2}'
register: chrony_sourcedir_paths
changed_when: false
failed_when: false
when: chrony_conf_has_servers.rc != 0

- name: "Check for server/pool entries in sourcedir .sources files"
ansible.builtin.shell:
cmd: |
for dir in {{ chrony_sourcedir_paths.stdout_lines | join(' ') }}; do
if [ -d "$dir" ]; then
grep -q '^[[:space:]]*\(server\|pool\)[[:space:]]\+[[:graph:]]\+' "$dir"/*.sources 2>/dev/null && exit 0
fi
done
exit 1
register: chrony_sourcedir_has_servers
changed_when: false
failed_when: false
when:
- chrony_conf_has_servers.rc != 0
- chrony_sourcedir_paths.stdout_lines | length > 0

- name: "Extract confdir paths from chrony configuration"
ansible.builtin.shell:
cmd: grep '^[[:space:]]*confdir[[:space:]]\+' {{{ chrony_conf_path }}} | awk '{print $2}'
register: chrony_confdir_paths
changed_when: false
failed_when: false
when:
- chrony_conf_has_servers.rc != 0
- (chrony_sourcedir_paths.stdout_lines | default([]) | length == 0 or chrony_sourcedir_has_servers.rc != 0)

- name: "Check for server/pool entries in confdir .conf files"
ansible.builtin.shell:
cmd: |
for dir in {{ chrony_confdir_paths.stdout_lines | join(' ') }}; do
if [ -d "$dir" ]; then
grep -q '^[[:space:]]*\(server\|pool\)[[:space:]]\+[[:graph:]]\+' "$dir"/*.conf 2>/dev/null && exit 0
fi
done
exit 1
register: chrony_confdir_has_servers
changed_when: false
failed_when: false
when:
- chrony_conf_has_servers.rc != 0
- chrony_confdir_paths.stdout_lines | default([]) | length > 0
- (chrony_sourcedir_paths.stdout_lines | default([]) | length == 0 or chrony_sourcedir_has_servers.rc != 0)

- name: "Create sourcedir directory if needed"
ansible.builtin.file:
path: "{{ chrony_sourcedir_paths.stdout_lines[0] }}"
state: directory
mode: '0755'
when:
- chrony_conf_has_servers.rc != 0
- chrony_sourcedir_paths.stdout_lines | default([]) | length > 0
- chrony_sourcedir_has_servers.rc != 0

- name: "Add remote time servers to sourcedir .sources file"
ansible.builtin.lineinfile:
path: "{{ chrony_sourcedir_paths.stdout_lines[0] }}/ntp-servers.sources"
line: 'server {{ item }}'
state: present
create: true
mode: '0644'
loop: '{{ var_multiple_time_servers.split(",") }}'
when:
- chrony_conf_has_servers.rc != 0
- chrony_sourcedir_paths.stdout_lines | default([]) | length > 0
- chrony_sourcedir_has_servers.rc != 0

- name: "Create confdir directory if needed"
ansible.builtin.file:
path: "{{ chrony_confdir_paths.stdout_lines[0] }}"
state: directory
mode: '0755'
when:
- chrony_conf_has_servers.rc != 0
- (chrony_sourcedir_paths.stdout_lines | default([]) | length == 0 or chrony_sourcedir_has_servers.rc != 0)
- chrony_confdir_paths.stdout_lines | default([]) | length > 0
- chrony_confdir_has_servers.rc != 0

- name: "Add remote time servers to confdir .conf file"
ansible.builtin.lineinfile:
path: "{{ chrony_confdir_paths.stdout_lines[0] }}/ntp-servers.conf"
line: 'server {{ item }}'
state: present
create: true
mode: '0644'
loop: '{{ var_multiple_time_servers.split(",") }}'
when:
- chrony_conf_has_servers.rc != 0
- (chrony_sourcedir_paths.stdout_lines | default([]) | length == 0 or chrony_sourcedir_has_servers.rc != 0)
- chrony_confdir_paths.stdout_lines | default([]) | length > 0
- chrony_confdir_has_servers.rc != 0

- name: "Add remote time servers to main chrony configuration"
ansible.builtin.lineinfile:
path: {{{ chrony_conf_path }}}
line: 'server {{ item }}'
state: present
create: True
create: true
loop: '{{ var_multiple_time_servers.split(",") }}'
when: chrony_servers.matched == 0
when:
- chrony_conf_has_servers.rc != 0
- (chrony_sourcedir_paths.stdout_lines | default([]) | length == 0 or chrony_sourcedir_has_servers.rc != 0)
- (chrony_confdir_paths.stdout_lines | default([]) | length == 0 or chrony_confdir_has_servers.rc != 0)
Original file line number Diff line number Diff line change
@@ -1,21 +1,107 @@
<def-group>
<definition class="compliance" id="chronyd_specify_remote_server" version="1">
<definition class="compliance" id="chronyd_specify_remote_server" version="2">
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule is a part of many profiles in many products. Do all of these profiles allow chrony to use sourcedir and confdir?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a feature of Chrony. And it is supported for long time.

{{{ oval_metadata("A remote NTP Server for time synchronization should be
specified (and dependencies are met)", rule_title=rule_title) }}}
<criteria comment="chrony.conf conditions are met">
<criterion test_ref="test_chronyd_remote_server" />
<criteria comment="chrony.conf conditions are met" operator="OR">
<criterion test_ref="test_chronyd_server_in_main_conf"
comment="server/pool in main chrony.conf"/>
<criterion test_ref="test_chronyd_server_in_sourcedir_files"
comment="server/pool in sourcedir .sources files"/>
<criterion test_ref="test_chronyd_server_in_confdir_files"
comment="server/pool in confdir .conf files"/>
</criteria>
</definition>

<!-- Test 1: Check main chrony.conf for server/pool -->
<ind:textfilecontent54_test check="all" check_existence="at_least_one_exists"
comment="Ensure at least one NTP server is set" id="test_chronyd_remote_server"
version="1">
<ind:object object_ref="object_chronyd_remote_server" />
id="test_chronyd_server_in_main_conf" version="1"
comment="Check for server/pool in main chrony.conf">
<ind:object object_ref="obj_chronyd_server_in_main_conf" />
</ind:textfilecontent54_test>

<ind:textfilecontent54_object comment="Ensure at least one NTP server is set"
id="object_chronyd_remote_server" version="1">
<ind:filepath operation="pattern match">^({{{ chrony_conf_path }}}|{{{ chrony_d_path }}}.+\.conf)$</ind:filepath>
<ind:textfilecontent54_object id="obj_chronyd_server_in_main_conf" version="1"
comment="Check main chrony.conf for server/pool directives">
<ind:filepath>{{{ chrony_conf_path }}}</ind:filepath>
<ind:pattern operation="pattern match">^[\s]*(?:server|pool)[\s]+.+$</ind:pattern>
<ind:instance datatype="int">1</ind:instance>
</ind:textfilecontent54_object>

<!-- Extract sourcedir paths from chrony.conf -->
<ind:textfilecontent54_object id="obj_extract_sourcedir_paths" version="1"
comment="Extract sourcedir paths from chrony.conf">
<ind:filepath>{{{ chrony_conf_path }}}</ind:filepath>
<ind:pattern operation="pattern match">^[\s]*sourcedir[\s]+(\S+)[\s]*(?:#.*)?$</ind:pattern>
<ind:instance operation="greater than or equal" datatype="int">1</ind:instance>
</ind:textfilecontent54_object>

<!-- Build glob patterns for .sources files in sourcedir directories -->
<local_variable id="var_sourcedir_file_paths" datatype="string" version="1"
comment="Construct glob patterns for .sources files">
<concat>
<object_component item_field="subexpression" object_ref="obj_extract_sourcedir_paths" />
<literal_component>/*.sources</literal_component>
</concat>
</local_variable>

<!-- Convert glob patterns to regex for filepath matching -->
<local_variable id="var_sourcedir_file_paths_regex" datatype="string" version="1"
comment="Convert to regex for filepath matching">
<glob_to_regex>
<variable_component var_ref="var_sourcedir_file_paths" />
</glob_to_regex>
</local_variable>

<!-- Test 2: Check sourcedir .sources files for server/pool -->
<ind:textfilecontent54_test check="all" check_existence="at_least_one_exists"
id="test_chronyd_server_in_sourcedir_files" version="1"
comment="Check for server/pool in sourcedir .sources files">
<ind:object object_ref="obj_chronyd_server_in_sourcedir_files" />
</ind:textfilecontent54_test>

<ind:textfilecontent54_object id="obj_chronyd_server_in_sourcedir_files" version="1"
comment="Check .sources files in sourcedir for server/pool directives">
<ind:filepath operation="pattern match" var_ref="var_sourcedir_file_paths_regex"
var_check="at least one"/>
<ind:pattern operation="pattern match">^[\s]*(?:server|pool)[\s]+.+$</ind:pattern>
<ind:instance datatype="int">1</ind:instance>
</ind:textfilecontent54_object>

<!-- Extract confdir paths from chrony.conf -->
<ind:textfilecontent54_object id="obj_extract_confdir_paths" version="1"
comment="Extract confdir paths from chrony.conf">
<ind:filepath>{{{ chrony_conf_path }}}</ind:filepath>
<ind:pattern operation="pattern match">^[\s]*confdir[\s]+(\S+)[\s]*(?:#.*)?$</ind:pattern>
<ind:instance operation="greater than or equal" datatype="int">1</ind:instance>
</ind:textfilecontent54_object>

<!-- Build glob patterns for .conf files in confdir directories -->
<local_variable id="var_confdir_file_paths" datatype="string" version="1"
comment="Construct glob patterns for .conf files">
<concat>
<object_component item_field="subexpression" object_ref="obj_extract_confdir_paths" />
<literal_component>/*.conf</literal_component>
</concat>
</local_variable>

<!-- Convert glob patterns to regex for filepath matching -->
<local_variable id="var_confdir_file_paths_regex" datatype="string" version="1"
comment="Convert to regex for filepath matching">
<glob_to_regex>
<variable_component var_ref="var_confdir_file_paths" />
</glob_to_regex>
</local_variable>

<!-- Test 3: Check confdir .conf files for server/pool -->
<ind:textfilecontent54_test check="all" check_existence="at_least_one_exists"
id="test_chronyd_server_in_confdir_files" version="1"
comment="Check for server/pool in confdir .conf files">
<ind:object object_ref="obj_chronyd_server_in_confdir_files" />
</ind:textfilecontent54_test>

<ind:textfilecontent54_object id="obj_chronyd_server_in_confdir_files" version="1"
comment="Check .conf files in confdir for server/pool directives">
<ind:filepath operation="pattern match" var_ref="var_confdir_file_paths_regex"
var_check="at least one"/>
<ind:pattern operation="pattern match">^[\s]*(?:server|pool)[\s]+.+$</ind:pattern>
<ind:instance datatype="int">1</ind:instance>
</ind:textfilecontent54_object>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ description: |-
<tt>Chrony</tt> can be configured to be a client and/or a server.
Add or edit server or pool lines to <tt>{{{ chrony_conf_path }}}</tt> as appropriate:
<pre>server &lt;remote-server&gt;</pre>
Alternatively, server or pool directives can be specified in files included via
<tt>sourcedir</tt> or <tt>confdir</tt> directives in <tt>{{{ chrony_conf_path }}}</tt>.
When using <tt>sourcedir</tt>, create <tt>.sources</tt> files in the specified directory:
<pre># In {{{ chrony_conf_path }}}:
sourcedir /etc/chrony/sources.d
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the remediations namely Ansible remediation should be improved so that they would remove problematic items from files in sourcedir or confdir.


# In /etc/chrony/sources.d/ntp.sources:
server 0.pool.ntp.org</pre>
When using <tt>confdir</tt>, create <tt>.conf</tt> files in the specified directory:
<pre># In {{{ chrony_conf_path }}}:
confdir /etc/chrony/conf.d

# In /etc/chrony/conf.d/ntp-servers.conf:
pool 1.pool.ntp.org</pre>
Multiple servers may be configured.

rationale: |-
Expand Down Expand Up @@ -38,5 +52,13 @@ references:
ocil_clause: 'a remote time server is not configured'

ocil: |-
Run the following command and verify remote server is configured properly:
Verify that a remote time server is configured. First, check the main configuration file:
<pre># grep -E "^(server|pool)" {{{ chrony_conf_path }}}</pre>
If no server or pool directive is found, check for sourcedir or confdir directives:
<pre># grep -E "^(sourcedir|confdir)" {{{ chrony_conf_path }}}</pre>
For each sourcedir found, check <tt>.sources</tt> files in that directory:
<pre># grep -E "^(server|pool)" /path/to/sourcedir/*.sources</pre>
For each confdir found, check <tt>.conf</tt> files in that directory:
<pre># grep -E "^(server|pool)" /path/to/confdir/*.conf</pre>
At least one server or pool directive must be present in the main configuration file
or in files within directories specified by sourcedir or confdir directives.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu

# Test: Commented sourcedir should be ignored - should fail
SOURCES_DIR="/etc/chrony/sources.d"
rm -rf /etc/chrony/conf.d
rm -rf $SOURCES_DIR

# Create main conf with commented sourcedir

cat > {{{ chrony_conf_path }}} << EOF
# sourcedir $SOURCES_DIR
EOF

# Create sources.d directory with server (should be ignored)
mkdir -p $SOURCES_DIR
echo "server 0.pool.ntp.org" > $SOURCES_DIR/ntp.sources
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu

# Test: server directive in confdir .conf file
CONF_DIR="/etc/chrony/conf.d"
rm -rf $CONF_DIR
rm -rf /etc/chrony/sources.d

# Create main conf with confdir
cat > {{{ chrony_conf_path }}} << EOF
confdir $CONF_DIR
EOF

# Create conf.d directory and file
mkdir -p $CONF_DIR
echo "pool 1.pool.ntp.org" > $CONF_DIR/ntp-servers.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu

sed -i '/server 0.pool.ntp.org/d' /etc/chrony/conf.d/*.conf
sed -i '/server 0.pool.ntp.org/d' /etc/chrony/sources.d/*.sources

echo "server 0.pool.ntp.org" > {{{ chrony_conf_path }}}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux

sed -i '/pool 0.pool.ntp.org/d' /etc/chrony/conf.d/*.conf
sed -i '/pool 0.pool.ntp.org/d' /etc/chrony/sources.d/*.sources

echo "pool 0.pool.ntp.org" > {{{ chrony_conf_path }}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu

# Test: Empty sourcedir but server in main conf - should pass
SOURCES_DIR="/etc/chrony/sources.d"
rm -rf /etc/chrony/conf.d
rm -rf $SOURCES_DIR

# Create main conf with server AND sourcedir
cat > {{{ chrony_conf_path }}} << EOF
server 0.pool.ntp.org
sourcedir $SOURCES_DIR
EOF

# Create empty sources.d directory
mkdir -p $SOURCES_DIR
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu

rm -rf /etc/chrony/conf.d
rm -rf /etc/chrony/sources.d

echo "" > {{{ chrony_conf_path }}}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu
# variables = var_multiple_time_servers=0.pool.ntp.org,1.pool.ntp.org

rm -rf /etc/chrony/conf.d
rm -rf /etc/chrony/sources.d

rm -f {{{ chrony_conf_path }}}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu

rm -rf /etc/chrony/conf.d
rm -rf /etc/chrony/sources.d

echo "some line" > {{{ chrony_conf_path }}}
echo "another line" >> {{{ chrony_conf_path }}}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
# platform = multi_platform_ubuntu
# remediation = None

rm -rf /etc/chrony/conf.d
rm -rf /etc/chrony/sources.d

echo "server 0.pool.ntp.org" > {{{ chrony_conf_path }}}
echo "server 0.ubuntu.pool.ntp.org" >> {{{ chrony_conf_path }}}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
# packages = chrony
# platform = multi_platform_fedora,multi_platform_ol,multi_platform_rhel,multi_platform_almalinux,multi_platform_ubuntu

rm -rf /etc/chrony/conf.d
rm -rf /etc/chrony/sources.d

echo "server 0.pool.ntp.org" > {{{ chrony_conf_path }}}
echo "server 1.pool.ntp.org" >> {{{ chrony_conf_path }}}
Loading
Loading