mirror of
https://github.com/linux-system-roles/network.git
synced 2026-07-19 01:17:00 +00:00
Add ca_path option for 802.1x connections
Special notes: Hash the cacert in the ca_path as OpenSSL needs symlinks for ca certificates from their symlinks to their filename if specifying a directory. In case ieee802_1x.ca_path is specified but not supported by NetworkManager, fail to ensure the setting is not silently ignored. References: https://stackoverflow.com/questions/25889341/what-is-the-equivalent-of-unix-c-rehash-command-script-on-linux https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/448 Co-authored-by: Till Maas <opensource@till.name>
This commit is contained in:
parent
f72fc394ea
commit
dd4ae77cbf
10 changed files with 287 additions and 57 deletions
|
|
@ -459,6 +459,12 @@ SSL certificates and keys must be deployed on the host prior to running the role
|
|||
|
||||
Absolute path to the PEM encoded certificate authority used to verify the EAP server.
|
||||
|
||||
* `ca_path`
|
||||
|
||||
Absolute path to directory containing additional pem encoded ca certificates used to
|
||||
verify the EAP server. Can be used instead of or in addition to ca_cert. Cannot be
|
||||
used if system_ca_certs is enabled.
|
||||
|
||||
* `system_ca_certs`
|
||||
|
||||
If set to `True`, NetworkManager will use the system's trusted ca certificates to verify the EAP server.
|
||||
|
|
|
|||
|
|
@ -1005,6 +1005,11 @@ class NMUtil:
|
|||
Util.path_to_glib_bytes(connection["ieee802_1x"]["ca_cert"]),
|
||||
)
|
||||
|
||||
if connection["ieee802_1x"]["ca_path"]:
|
||||
s_8021x.set_property(
|
||||
NM.SETTING_802_1X_CA_PATH, connection["ieee802_1x"]["ca_path"],
|
||||
)
|
||||
|
||||
s_8021x.set_property(
|
||||
NM.SETTING_802_1X_SYSTEM_CA_CERTS,
|
||||
connection["ieee802_1x"]["system_ca_certs"],
|
||||
|
|
@ -2086,6 +2091,30 @@ class Cmd_nm(Cmd):
|
|||
% (con_cur.get_id(), con_cur.get_uuid()),
|
||||
)
|
||||
|
||||
if (
|
||||
self.check_mode == CheckMode.REAL_RUN
|
||||
and connection["ieee802_1x"] is not None
|
||||
and connection["ieee802_1x"].get("ca_path")
|
||||
):
|
||||
# It seems that NM on Fedora 31
|
||||
# (NetworkManager-1.20.4-1.fc31.x86_64) does need some time so that
|
||||
# the D-Bus information is actually up-to-date.
|
||||
time.sleep(0.1)
|
||||
Util.GMainLoop_iterate_all()
|
||||
updated_connection = Util.first(
|
||||
self.nmutil.connection_list(
|
||||
name=connection["name"], uuid=connection["nm.uuid"]
|
||||
)
|
||||
)
|
||||
ca_path = updated_connection.get_setting_802_1x().props.ca_path
|
||||
if not ca_path:
|
||||
self.log_fatal(
|
||||
idx,
|
||||
"ieee802_1x.ca_path specified but not supported by "
|
||||
"NetworkManager. Please update NetworkManager or use "
|
||||
"ieee802_1x.ca_cert.",
|
||||
)
|
||||
|
||||
seen = set()
|
||||
if con_cur is not None:
|
||||
seen.add(con_cur)
|
||||
|
|
|
|||
|
|
@ -871,12 +871,21 @@ class ArgValidator_Dict802_1X(ArgValidatorDict):
|
|||
),
|
||||
ArgValidatorPath("client_cert", required=True),
|
||||
ArgValidatorPath("ca_cert"),
|
||||
ArgValidatorPath("ca_path"),
|
||||
ArgValidatorBool("system_ca_certs", default_value=False),
|
||||
ArgValidatorStr("domain_suffix_match", required=False),
|
||||
],
|
||||
default_value=None,
|
||||
)
|
||||
|
||||
def _validate_post(self, value, name, result):
|
||||
if result["system_ca_certs"] is True and result["ca_path"] is not None:
|
||||
raise ValidationError(
|
||||
name,
|
||||
"ca_path will be ignored by NetworkManager if system_ca_certs is used",
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class ArgValidator_DictConnection(ArgValidatorDict):
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ NM_ONLY_TESTS = {
|
|||
"playbooks/tests_reapply.yml": {},
|
||||
"playbooks/tests_states.yml": {},
|
||||
"playbooks/tests_802_1x.yml": {},
|
||||
"playbooks/tests_802_1x_updated.yml": {},
|
||||
}
|
||||
|
||||
IGNORE = [
|
||||
|
|
|
|||
|
|
@ -7,17 +7,7 @@
|
|||
- name: "INIT: 802.1x tests"
|
||||
debug:
|
||||
msg: "##################################################"
|
||||
- include_tasks: tasks/setup_802_1x_server.yml
|
||||
- name: Copy client certs
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/etc/pki/tls/{{ item }}"
|
||||
mode: 0644
|
||||
with_items:
|
||||
- client.key
|
||||
- client.key.nocrypt
|
||||
- client.pem
|
||||
- cacert.pem
|
||||
- include_tasks: tasks/setup_802.1x.yml
|
||||
- block:
|
||||
- name: "TEST: 802.1x profile with private key password and ca cert"
|
||||
debug:
|
||||
|
|
@ -96,52 +86,8 @@
|
|||
- name: "{{ interface }}"
|
||||
persistent_state: absent
|
||||
state: absent
|
||||
- name: >-
|
||||
TEST: 802.1x profile with unencrypted private key and ca_path
|
||||
debug:
|
||||
msg: "##################################################"
|
||||
- name: Create directory for ca_path test
|
||||
file:
|
||||
path: "/etc/pki/tls/my_ca_certs"
|
||||
state: directory
|
||||
mode: 0755
|
||||
- name: Copy cacert to ca_path
|
||||
copy:
|
||||
src: "cacert.pem"
|
||||
dest: "/etc/pki/tls/my_ca_certs/cacert.pem"
|
||||
mode: 0644
|
||||
- name: Hash cacert
|
||||
command: openssl x509 -hash -noout
|
||||
-in /etc/pki/tls/my_ca_certs/cacert.pem
|
||||
register: cacert_hash
|
||||
- name: Add symlink for cacert
|
||||
file:
|
||||
state: link
|
||||
path: "/etc/pki/tls/my_ca_certs/{{ cacert_hash.stdout }}.0"
|
||||
src: cacert.pem
|
||||
- import_role:
|
||||
name: linux-system-roles.network
|
||||
vars:
|
||||
network_connections:
|
||||
- name: "{{ interface }}"
|
||||
interface_name: veth2
|
||||
state: up
|
||||
type: ethernet
|
||||
ip:
|
||||
address:
|
||||
- 203.0.113.2/24
|
||||
dhcp4: "no"
|
||||
auto6: "no"
|
||||
ieee802_1x:
|
||||
identity: myhost
|
||||
eap: tls
|
||||
private_key: /etc/pki/tls/client.key.nocrypt
|
||||
client_cert: /etc/pki/tls/client.pem
|
||||
private_key_password_flags:
|
||||
- not-required
|
||||
ca_path: /etc/pki/tls/my_ca_certs
|
||||
- name: "TEST: I can ping the EAP server"
|
||||
command: ping -c1 203.0.113.1
|
||||
|
||||
- include_tasks: tasks/test_802.1x_capath.yml
|
||||
always:
|
||||
- block:
|
||||
- import_role:
|
||||
|
|
@ -170,6 +116,7 @@
|
|||
state: absent
|
||||
path: "{{ item }}"
|
||||
with_items:
|
||||
- /etc/pki/tls/my_ca_certs
|
||||
- /etc/pki/ca-trust/source/anchors/cacert.pem
|
||||
- name: Update ca trust
|
||||
command: update-ca-trust
|
||||
|
|
|
|||
13
tests/playbooks/tests_802_1x_updated.yml
Normal file
13
tests/playbooks/tests_802_1x_updated.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
---
|
||||
- hosts: all
|
||||
tasks:
|
||||
- name: Update NetworkManager
|
||||
package:
|
||||
name: NetworkManager
|
||||
state: latest
|
||||
- name: Restart NetworkManager
|
||||
service:
|
||||
name: NetworkManager
|
||||
state: restarted
|
||||
- import_playbook: tests_802_1x.yml
|
||||
11
tests/tasks/setup_802.1x.yml
Normal file
11
tests/tasks/setup_802.1x.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
- include_tasks: tasks/setup_802_1x_server.yml
|
||||
- name: Copy client certs
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/etc/pki/tls/{{ item }}"
|
||||
mode: 0644
|
||||
with_items:
|
||||
- client.key
|
||||
- client.key.nocrypt
|
||||
- client.pem
|
||||
- cacert.pem
|
||||
94
tests/tasks/test_802.1x_capath.yml
Normal file
94
tests/tasks/test_802.1x_capath.yml
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
- name: >-
|
||||
TEST: 802.1x profile with unencrypted private key and ca_path
|
||||
debug:
|
||||
msg: "##################################################"
|
||||
- set_fact:
|
||||
# Fixed versions/NVRs:
|
||||
# 1.25.2
|
||||
# NetworkManager-1.24.2-1.fc33
|
||||
# NetworkManager-1.22.14-1.fc32
|
||||
# NetworkManager-1.20.12-1.fc31
|
||||
# 1.18.8
|
||||
__NM_capath_ignored_NVRs:
|
||||
- NetworkManager-1.18.0-5.el7.x86_64
|
||||
- NetworkManager-1.18.4-3.el7.x86_64
|
||||
- NetworkManager-1.20.0-3.el8.x86_64
|
||||
- NetworkManager-1.22.8-4.el8.x86_64
|
||||
- NetworkManager-1.20.4-1.fc31.x86_64
|
||||
- NetworkManager-1.22.10-1.fc32.x86_64
|
||||
- NetworkManager-1.22.12-1.fc32.x86_64
|
||||
- name: Create directory for ca_path test
|
||||
file:
|
||||
path: "/etc/pki/tls/my_ca_certs"
|
||||
state: directory
|
||||
mode: 0755
|
||||
- name: Copy cacert to ca_path
|
||||
copy:
|
||||
src: "cacert.pem"
|
||||
dest: "/etc/pki/tls/my_ca_certs/cacert.pem"
|
||||
mode: 0644
|
||||
- name: Hash cacert
|
||||
command: openssl x509 -hash -noout
|
||||
-in /etc/pki/tls/my_ca_certs/cacert.pem
|
||||
register: cacert_hash
|
||||
- name: Add symlink for cacert
|
||||
file:
|
||||
state: link
|
||||
path: "/etc/pki/tls/my_ca_certs/{{ cacert_hash.stdout }}.0"
|
||||
src: cacert.pem
|
||||
- name: Get NetworkManager version
|
||||
command:
|
||||
cmd: rpm -qa NetworkManager
|
||||
warn: false
|
||||
register: __network_NM_NVR
|
||||
- block:
|
||||
- import_role:
|
||||
name: linux-system-roles.network
|
||||
vars:
|
||||
network_connections:
|
||||
- name: "{{ interface | default('802-1x-test') }}"
|
||||
interface_name: veth2
|
||||
state: up
|
||||
type: ethernet
|
||||
ip:
|
||||
address:
|
||||
- 203.0.113.2/24
|
||||
dhcp4: "no"
|
||||
auto6: "no"
|
||||
ieee802_1x:
|
||||
identity: myhost_capath
|
||||
eap: tls
|
||||
private_key: /etc/pki/tls/client.key.nocrypt
|
||||
client_cert: /etc/pki/tls/client.pem
|
||||
private_key_password_flags:
|
||||
- not-required
|
||||
ca_path: /etc/pki/tls/my_ca_certs
|
||||
- name: "TEST: I can ping the EAP server"
|
||||
command: ping -c1 203.0.113.1
|
||||
- name: trigger failure in case the role did not fail
|
||||
fail:
|
||||
msg: after test
|
||||
rescue:
|
||||
- debug:
|
||||
var: "{{ item }}"
|
||||
with_items:
|
||||
- ansible_failed_result
|
||||
- ansible_failed_task
|
||||
- __network_NM_NVR.stdout
|
||||
- __NM_capath_ignored_NVRs
|
||||
|
||||
- name: Assert role behavior
|
||||
assert:
|
||||
that: __network_connections_result.failed ==
|
||||
(__network_NM_NVR.stdout in __NM_capath_ignored_NVRs)
|
||||
msg: "Role {{ __network_connections_result.failed and 'failed'
|
||||
or 'did not fail' }} but was expected
|
||||
{{ (__network_NM_NVR.stdout in __NM_capath_ignored_NVRs)
|
||||
and '' or 'not' }} to fail. NM NVR: {{ __network_NM_NVR.stdout }}"
|
||||
|
||||
- name: Assert ping succeeded
|
||||
assert:
|
||||
that:
|
||||
- "not 'cmd' in ansible_failed_result"
|
||||
...
|
||||
21
tests/tests_802_1x_updated_nm.yml
Normal file
21
tests/tests_802_1x_updated_nm.yml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# This file was generated by ensure_provider_tests.py
|
||||
---
|
||||
# set network provider and gather facts
|
||||
- hosts: all
|
||||
name: Run playbook 'playbooks/tests_802_1x_updated.yml' with nm as provider
|
||||
tasks:
|
||||
- name: Set network provider to 'nm'
|
||||
set_fact:
|
||||
network_provider: nm
|
||||
|
||||
|
||||
# workaround for: https://github.com/ansible/ansible/issues/27973
|
||||
# There is no way in Ansible to abort a playbook hosts with specific OS
|
||||
# releases Therefore we include the playbook with the tests only if the hosts
|
||||
# would support it.
|
||||
# The test requires or should run with NetworkManager, therefore it cannot run
|
||||
# on RHEL/CentOS 6
|
||||
- import_playbook: playbooks/tests_802_1x_updated.yml
|
||||
when:
|
||||
- ansible_distribution_major_version != '6'
|
||||
|
|
@ -1956,6 +1956,7 @@ class TestValidator(unittest.TestCase):
|
|||
"private_key_password_flags": None,
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"ca_cert": "/etc/pki/tls/cacert.pem",
|
||||
"ca_path": None,
|
||||
"system_ca_certs": False,
|
||||
"domain_suffix_match": None,
|
||||
},
|
||||
|
|
@ -2029,6 +2030,7 @@ class TestValidator(unittest.TestCase):
|
|||
"private_key_password_flags": ["not-required"],
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"ca_cert": None,
|
||||
"ca_path": None,
|
||||
"system_ca_certs": True,
|
||||
"domain_suffix_match": "example.com",
|
||||
},
|
||||
|
|
@ -2061,6 +2063,79 @@ class TestValidator(unittest.TestCase):
|
|||
],
|
||||
)
|
||||
|
||||
def test_802_1x_3(self):
|
||||
"""
|
||||
Test 802.1x profile with unencrypted private key and ca_path
|
||||
"""
|
||||
self.maxDiff = None
|
||||
self.do_connections_validate(
|
||||
[
|
||||
{
|
||||
"actions": ["present", "up"],
|
||||
"autoconnect": True,
|
||||
"check_iface_exists": True,
|
||||
"ethernet": ETHERNET_DEFAULTS,
|
||||
"ethtool": ETHTOOL_DEFAULTS,
|
||||
"force_state_change": None,
|
||||
"ignore_errors": None,
|
||||
"interface_name": "eth0",
|
||||
"ip": {
|
||||
"gateway6": None,
|
||||
"gateway4": None,
|
||||
"route_metric4": None,
|
||||
"auto6": True,
|
||||
"dhcp4": True,
|
||||
"address": [],
|
||||
"route_append_only": False,
|
||||
"rule_append_only": False,
|
||||
"route": [],
|
||||
"dns": [],
|
||||
"dns_search": [],
|
||||
"route_metric6": None,
|
||||
"dhcp4_send_hostname": None,
|
||||
},
|
||||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": {
|
||||
"identity": "myhost",
|
||||
"eap": "tls",
|
||||
"private_key": "/etc/pki/tls/client.key",
|
||||
"private_key_password": None,
|
||||
"private_key_password_flags": ["not-required"],
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"ca_cert": None,
|
||||
"ca_path": "/etc/pki/tls/my_ca_certs",
|
||||
"system_ca_certs": False,
|
||||
"domain_suffix_match": None,
|
||||
},
|
||||
"mtu": None,
|
||||
"name": "eth0",
|
||||
"parent": None,
|
||||
"persistent_state": "present",
|
||||
"slave_type": None,
|
||||
"state": "up",
|
||||
"type": "ethernet",
|
||||
"wait": None,
|
||||
"zone": None,
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "eth0",
|
||||
"state": "up",
|
||||
"type": "ethernet",
|
||||
"ieee802_1x": {
|
||||
"identity": "myhost",
|
||||
"eap": "tls",
|
||||
"private_key": "/etc/pki/tls/client.key",
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"private_key_password_flags": ["not-required"],
|
||||
"ca_path": "/etc/pki/tls/my_ca_certs",
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def test_invalid_cert_path(self):
|
||||
"""
|
||||
should fail if a relative path is used for 802.1x certs/keys
|
||||
|
|
@ -2107,6 +2182,30 @@ class TestValidator(unittest.TestCase):
|
|||
]
|
||||
)
|
||||
|
||||
def test_802_1x_ca_path_and_system_ca_certs(self):
|
||||
"""
|
||||
should fail if ca_path and system_ca_certs are used together
|
||||
"""
|
||||
self.maxDiff = None
|
||||
self.do_connections_check_invalid(
|
||||
[
|
||||
{
|
||||
"name": "eth0",
|
||||
"state": "up",
|
||||
"type": "ethernet",
|
||||
"ieee802_1x": {
|
||||
"identity": "myhost",
|
||||
"eap": "tls",
|
||||
"private_key": "/etc/pki/tls/client.key",
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"private_key_password_flags": ["not-required"],
|
||||
"ca_path": "/etc/pki/my_ca_certs",
|
||||
"system_ca_certs": True,
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def test_802_1x_initscripts(self):
|
||||
"""
|
||||
should fail to create ieee802_1x connection with initscripts
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue