Add support for 802.1x wired connections (EAP-TLS only)

Only EAP-TLS method is supported. Must use NetworkManager as the network_provider.
Also fixed bug in do_connections_validate_nm() function.
This commit is contained in:
Jack Adolph 2020-03-02 01:19:14 +11:00 committed by Till Maas
parent d32f4e5a7b
commit 4af8f23955
22 changed files with 961 additions and 7 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@
/tests/tmp_merge_coveragerc
/tests/total-*coveragedata
/.tox
/.vscode

View file

@ -409,6 +409,58 @@ kernel and device, changing some features might not be supported.
txvlan: yes|no # optional
```
### `802.1x`
Configures 802.1x authentication for an interface.
Currently, NetworkManager is the only supported provider and EAP-TLS is the only supported EAP method.
SSL certificates and keys must be deployed on the host prior to running the role.
* `eap`
The allowed EAP method to be used when authenticating to the network with 802.1x.
Currently, `tls` is the default and the only accepted value.
* `identity` (required)
Identity string for EAP authentication methods.
* `private-key` (required)
Absolute path to the client's PEM or PKCS#12 encoded private key used for 802.1x authentication.
* `private-key-password`
Password to the private key specified in `private-key`.
* `private-key-password-flags`
List of flags to configure how the private key password is managed.
Multiple flags may be specified.
Valid flags are:
- `none`
- `agent-owned`
- `not-saved`
- `not-required`
See NetworkManager documentation on "Secret flag types" more details (`man 5 nm-settings`).
* `client-cert` (required)
Absolute path to the client's PEM encoded certificate used for 802.1x authentication.
* `ca-cert`
Absolute path to the PEM encoded certificate authority used to verify the EAP server.
* `system-ca-certs`
If set to `True`, NetworkManager will use the system's trusted ca certificates to verify the EAP server.
Examples of Options
-------------------
@ -603,6 +655,23 @@ network_connections:
rule_append_only: yes
```
Configuring 802.1x:
```yaml
network_connections:
- name: eth0
type: ethernet
802.1x:
identity: myhost
eap: tls
private-key: /etc/pki/tls/client.key
# recommend vault encrypting the private key password
# see https://docs.ansible.com/ansible/latest/user_guide/vault.html
private-key-password: "p@55w0rD"
client-cert: /etc/pki/tls/client.pem
ca-cert: /etc/pki/tls/cacert.pem
```
### Invalid and Wrong Configuration
The `network` role rejects invalid configurations. It is recommended to test the role

View file

@ -20,16 +20,23 @@ network_provider_current: "{{
# Default to the auto-detected value
network_provider: "{{ network_provider_current }}"
# wpa_supplicant is required if any 802.1x connections are defined
wpa_supplicant_required: "{{ network_connections |
json_query('[*][\"802.1x\"]') | flatten | count > 0 }}"
_network_packages_default_802_1x: ["{% if wpa_supplicant_required
%}wpa_supplicant{% endif %}"]
# The python-gobject-base package depends on the python version and
# distribution:
# - python-gobject-base on RHEL7 (no python2-gobject-base :-/)
# - python3-gobject-base on Fedora 28+
_network_packages_default_gobject_packages: ["python{{
ansible_python['version']['major'] | replace('2', '')}}-gobject-base"]
network_service_name_default_nm: NetworkManager
network_packages_default_nm:
- ethtool
- NetworkManager
- "python{{ ansible_python['version']['major'] | replace('2', '')
}}-gobject-base"
network_packages_default_nm: "{{ ['ethtool', 'NetworkManager']
+ _network_packages_default_gobject_packages|select()|list()
+ _network_packages_default_802_1x|select()|list()}}"
network_service_name_default_initscripts: network

View file

@ -0,0 +1,29 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- hosts: network-test
vars:
network_connections:
- name: eth0
type: ethernet
802.1x:
identity: myhost
eap: tls
private-key: /etc/pki/tls/client.key
# recommend vault encrypting the private key password
# see https://docs.ansible.com/ansible/latest/user_guide/vault.html
private-key-password: "p@55w0rD"
client-cert: /etc/pki/tls/client.pem
ca-cert: /etc/pki/tls/cacert.pem
# certs have to be deployed first
pre_tasks:
- name: copy certs/keys for 802.1x auth
copy:
src: "{{ item }}"
dest: "/etc/pki/tls/{{ item }}"
with_items:
- client.key
- client.pem
- cacert.pem
roles:
- linux-system-roles.network

View file

@ -974,6 +974,51 @@ class NMUtil:
else:
s_ip6.add_route(rr)
if connection["802.1x"]:
s_8021x = self.connection_ensure_setting(con, NM.Setting8021x)
s_8021x.set_property(NM.SETTING_802_1X_EAP, [connection["802.1x"]["eap"]])
s_8021x.set_property(
NM.SETTING_802_1X_IDENTITY, connection["802.1x"]["identity"]
)
s_8021x.set_property(
NM.SETTING_802_1X_PRIVATE_KEY,
Util.path_to_glib_bytes(connection["802.1x"]["private-key"]),
)
if connection["802.1x"]["private-key-password"]:
s_8021x.set_property(
NM.SETTING_802_1X_PRIVATE_KEY_PASSWORD,
connection["802.1x"]["private-key-password"],
)
if connection["802.1x"]["private-key-password-flags"]:
s_8021x.set_secret_flags(
NM.SETTING_802_1X_PRIVATE_KEY_PASSWORD,
Util.NM().SettingSecretFlags(
Util.convert_passwd_flags_nm(
connection["802.1x"]["private-key-password-flags"]
),
),
)
s_8021x.set_property(
NM.SETTING_802_1X_CLIENT_CERT,
Util.path_to_glib_bytes(connection["802.1x"]["client-cert"]),
)
if connection["802.1x"]["ca-cert"]:
s_8021x.set_property(
NM.SETTING_802_1X_CA_CERT,
Util.path_to_glib_bytes(connection["802.1x"]["ca-cert"]),
)
s_8021x.set_property(
NM.SETTING_802_1X_SYSTEM_CA_CERTS,
connection["802.1x"]["system-ca-certs"],
)
try:
con.normalize()
except Exception as e:

View file

@ -2,6 +2,7 @@
# vim: fileencoding=utf8
# SPDX-License-Identifier: BSD-3-Clause
import posixpath
import socket
# pylint: disable=import-error, no-name-in-module
@ -698,6 +699,59 @@ class ArgValidator_DictMacvlan(ArgValidatorDict):
return result
class ArgValidatorPath(ArgValidatorStr):
"""
Valides that the value is a valid posix absolute path
"""
def __init__(self, name, required=False, default_value=None):
ArgValidatorStr.__init__(self, name, required, default_value, None)
def _validate_impl(self, value, name):
ArgValidatorStr._validate_impl(self, value, name)
if posixpath.isabs(value) is False:
raise ValidationError(
name, "value '%s' is not a valid posix absolute path" % (value),
)
return value
class ArgValidator_Dict802_1X(ArgValidatorDict):
VALID_EAP_TYPES = ["tls"]
VALID_PRIVATE_KEY_FLAGS = ["none", "agent-owned", "not-saved", "not-required"]
def __init__(self):
ArgValidatorDict.__init__(
self,
name="802.1x",
nested=[
ArgValidatorStr(
"eap",
enum_values=ArgValidator_Dict802_1X.VALID_EAP_TYPES,
default_value="tls",
),
ArgValidatorStr("identity", required=True),
ArgValidatorPath("private-key", required=True),
ArgValidatorStr("private-key-password"),
ArgValidatorList(
"private-key-password-flags",
nested=ArgValidatorStr(
"private-key-password-flags[?]",
enum_values=ArgValidator_Dict802_1X.VALID_PRIVATE_KEY_FLAGS,
),
default_value=None,
),
ArgValidatorPath("client-cert", required=True),
ArgValidatorPath("ca-cert"),
ArgValidatorBool("system-ca-certs", default_value=False),
],
default_value=None,
)
class ArgValidator_DictConnection(ArgValidatorDict):
VALID_PERSISTENT_STATES = ["absent", "present"]
@ -759,6 +813,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
ArgValidator_DictInfiniband(),
ArgValidator_DictVlan(),
ArgValidator_DictMacvlan(),
ArgValidator_Dict802_1X(),
# deprecated options:
ArgValidatorStr(
"infiniband_transport_mode",
@ -1208,3 +1263,18 @@ class ArgValidator_ListConnections(ArgValidatorList):
"profile references a master '%s' which has 'interface_name' "
"missing" % (connection["master"]),
)
# check if 802.1x connection is valid
if connection["802.1x"]:
if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS:
raise ValidationError.from_connection(
idx,
"802.1x authentication is not supported by initscripts. "
"Configure 802.1x in /etc/wpa_supplicant.conf "
"if you need to use initscripts.",
)
if connection["type"] != "ethernet":
raise ValidationError.from_connection(
idx, "802.1x settings only allowed for ethernet interfaces."
)

View file

@ -39,6 +39,33 @@ class Util:
raise MyError("failure calling %s: exit with %s" % (argv, p.returncode))
return out
@staticmethod
def path_to_glib_bytes(path):
"""
Converts a path to a GLib.Bytes object that can be accepted by NM
"""
return Util.GLib().Bytes.new(("file://%s\x00" % path).encode("utf-8"))
@staticmethod
def convert_passwd_flags_nm(secret_flags):
"""
Converts an array of "secret flags" strings
to an integer represantion understood by NetworkManager
"""
flag_int = 0
if "none" in secret_flags:
flag_int += 0
if "agent-owned" in secret_flags:
flag_int += 1
if "not-saved" in secret_flags:
flag_int += 2
if "not-required" in secret_flags:
flag_int += 4
return flag_int
@classmethod
def create_uuid(cls):
return str(uuid.uuid4())

View file

@ -32,6 +32,17 @@
when:
- network_provider == "nm"
# If any 802.1x connections are used, the wpa_supplicant
# service is required to be running
- name: Enable and start wpa_supplicant
service:
name: wpa_supplicant
state: started
enabled: true
when:
- network_provider == "nm"
- wpa_supplicant_required
- name: Enable network service
service:
name: "{{ network_service_name }}"

View file

@ -24,6 +24,7 @@ IGNORE = [
"tests_default_nm.yml",
"tests_default_initscripts.yml",
"tests_default.yml",
"tests_802_1x_nm.yml",
]
OTHER_PLAYBOOK = """

32
tests/files/cacert.pem Normal file
View file

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFiTCCA3GgAwIBAgIUK0vXQP5fEgyCtfY4pt2yU9bDMlAwDQYJKoZIhvcNAQEL
BQAwVDELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
CgwTRGVmYXVsdCBDb21wYW55IEx0ZDEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yMDAy
MjgwODUxMzdaFw00MDAyMjMwODUxMzdaMFQxCzAJBgNVBAYTAlhYMRUwEwYDVQQH
DAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxEDAO
BgNVBAMMB1Rlc3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDa
kLkNqQmEWCs/o39iPSlHrnuXvvqQUziGO635w16OeGgix4QY+H7My0uY801/WN2F
8dzHbpKkFJ3pvFhnbq/vcoUvY/Folb9YoMyjwEJoUiSyZ2n9YiV4HkpC+Y+aFG2P
IlBcGnRxBV4M35ZNpbvOwSWqgwIgN5Z7ET4nP0P1v5BzQa3FJYdd1nh2YEljGRE+
aTchVhdIYN4+4YXx1rvzQs4I0Tqr3tGM4zCWTQp34LYnyQMAysXQOnwJsX8/G4Lw
AtH8cFexdoHft8uL8YHbxV8gDVBpvZSl9kmkX204hozJzsRQzSbuVw6wb2ggaYei
HdOwORYJfoC6eKOiYJEHczHkkchmbj8qFP1IKUQOPE39W8swO/2j56Qvfv9vBIUw
4QklSU3CLYpr1jjeHuKuOkSTlFSOyvN6HOxLla/YqdshGwdo8duKz3gr85lJbzHT
9IfI2r7g8fTwcVcH0DfS7Ku4OXTIqyJw/ZO77hMpF0x6U6GCHxi8I9sGjN0JVCeE
43/ad3tr9KOlGQuiDR9rz5XkCbdPgaiJ+P3nfO2fMFCUaFOr0lFj4meCKC49+sdw
CDDpW0Cqkm142co1/J64YNw/z2A3ESJrckP9NCOb6zkCZS6j+T2Nir0fI6Ix1uOX
SHKinD4KT0WgOrJLxlZdLxsCnHp57G9PQqK8RXU5VwIDAQABo1MwUTAdBgNVHQ4E
FgQUOJF6JV8Q3HPuOp1UZo+KyYcddgwwHwYDVR0jBBgwFoAUOJF6JV8Q3HPuOp1U
Zo+KyYcddgwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAUQ3f
e6EiWU/TBewakD/yrNzMxuqOS6ptDq6OPYolpdrkCogtUvL6wga7PVgEb8v+kBD1
A0C4eA6Q+M/xOiFKSiErckQzEJggxN+C3N4rRS7F2fnMUjWjwApMxW/zPyTIGKDP
rxRNIJyfjuvxv8XnugDNjdg496beYlhOzvCVDO6hxt/V83NZBbcXY2GBUKnuldmd
21x7g/coajPuocWgK2s4OWU251ujCCggru5hRsxm3Ag68ZWCIqYu/rRN0z1V31OI
xc5OQsrkdrDez/iZMrw6N/rK8Xez0zqXU7v32gou7FoZxqx1yOZtkBMBsSXf/Fue
28maCNsE4S/HRcQQv91F/e/QtpfLmRBa6FIslBh334YzBKyuD6QLjMraDO3X8DjD
3i434Fgf5v7jttfEOrRKN7Yvb+IXZ25UhVGWUY1CsGQ57ZdUwwuD/bdeZLLAhl9X
r8PhHQ5YJV6NcGXMsvuvARlPILXLpAsP3IpXQq5l0GeHuRV2hwqiIV//qcgIz5qb
B7voqbr+2k652OOsG/tRIOvZlJBmdPkq+Beeutvx0j1VKNflBnxDMMO6zyhLVdO2
RB8Lx2IEM8KA4p1IhnEi7g3mtQsu6IlY4Qfuje8rE0xMY28rxr977sBvvdsfkeUd
O/Ut71oKxQ/z0H/Iy1BiBoUzuK0XL079lou4ft0=
-----END CERTIFICATE-----

31
tests/files/client.key Normal file
View file

@ -0,0 +1,31 @@
# password=test
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,C4A5E9A189773AB0F3CE3DCC98F208AE
LPNSExpEERS+/qHJxd8puT+EaZ/dZ20gkU/C2eaNNJerzr4moSXG4ioh5ggz4utQ
w57fD5OYqPiloNIawi/Ta5Opo3zU+iMZPVQALLbemXWXmNMxqxNCGdonc4enxMoN
auLxpdYPW+infFmf0UPwZjWkrLnK8XFapTGDaNesfgMNSRVSt+DQL3xeKUjcuXfh
rYvF26/Ls8NHB0tCU449vCa5ta1fHPT78B0cWgCmhcg/L8/0veBYfwxnyu6l3E6Q
RXWcyaJoihhCSg9kCZOqQFKDtz3B9G8/G8P5n5udN2TYUK0ieCktocOip0r/aUfk
Rz/NPjej18tuvA9e+uho2DuEj7OV1Rt0Fr6G2NySDYAIjlzM1+GoDdX3R8Rva2eX
SJYEjQvvLMAXU9wLEGd2u9jw3h8g2rNPF34Mo/fZsU6f83WceN7wzaDjKBM9TC/U
DjeUpJ2LHr3SduRoq5K7PqTG6LlRx4ZC06P8Gwu/cjlHqHuMlLE6wWPHowp9O08S
zMzJji6csSzZ5x5U41xiBJd19G0tbfjGBOvxhVLC3hmfqMtRwgeKSZMUz5f0iFvS
V4LE/ZNXWv5OybEzMyIiQBRB0G8mq5BkQ3rU9uTMO6Xc6mosQy0jiCsQLYaX2IoT
kyU6ZqPgAeBD3g5zCGudcF4qqY3pWRU6cijpivsuyX58YmulhQJsB2rnoImv8ZOR
4Uw+fvAx38v/dH/aAGKNdQV/4z+CXpAX4SdqYgBx9wXu6Wva31AVrbDrKnpSlWYF
M9gAHgpuhW9OH7du/y7sePU6k37fHtqDX0V5XoyeRxixR+KGb8k3tt0HFA1GExSu
XyXcOOfwec7xNQjZBM9jREI0yO1tCbHEeLsLpQnf31cpfSQumBZoiim6Vyk7vCN8
YBJ9qiVNrFiVogWl5hUrSS2MLQP1ZQBkedmOeKZpkZ26GW5yY0y27v2mHdhU2Dvd
otvLGiVKxSXlu+tqt1WkMvu6hcfrDZDCONW7emGW7xs2vdYdvADVlYs/Eb0WFXb1
tLkwg3v7I23LeFRrKX4Fm5/biG4GuR4sj9iPLayrKWhpujIVFJqHTI3YhjIU56Qp
uPuClnoFsKrWS9DXaziuuXmLZlXH3e5aOO+M2H3JmXTRCojyjKlIJiJJmHGrfwfe
oJkSF+ABs2zrpteXU+Cnfn8V01TrtxPYIBF3CbOMZEvwgjPLX0UNtnss0hXH4rJe
9yF/PiKWehUow8q4Gpwt2PnLkUWyL21GwCwXf5Cq3yRAKtyrJTlJsdYV1f3brzfb
JkBgKaFJ44Ee7D75PAio8g/BIDpvUdZVXwn3FizjfAU+HhXonPSYb2M34C6I/frk
mJPgZ5hbpt1SoCCER48+rQygiLdNQH6OsuhJeEElPFYwNo6i5jZsZ9iE0rmJxGgk
m7Mhi491NdK8L6Kh8kM2Dgupsfcstmx4+pI3gmgnsYZApmFoQlfcg4MhbWqxznv+
cPm1n2SZMoMLru44vbnjW+ZAggen5zNZOrsVt8UImSBVKfAIrgDUuYIv7uqUiKHI
yHmAkZDlqEbpkbUG9m60OeuEIgpN7MT3Kod387ZyOu9uaTZWdD18/N83E4eFecND
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuQipSk9+0rd/qBMDRiFzV8vDksaueVphejGEgiQhqtUDgjc/
ot/o7M8fFVC6wau2ixTnEMHuZXgoBOKATxX805FggEsLLL98OnN7AyTTKOtHVfIm
gK3fJ1Y9l95+2nuJWhmaan0vr3YMp6z3lSa+hlhhTYx/mIvTZho/K3+METg8DEfl
QUSkhAlrFSEahr2Pu/yETr8c+8vKTDnZDLvcFyyuDtAz+clEQUVndWJQpQpSVfR9
4xKuzaj10mUA9Utv4RkbNJ78/KgdTbaGIOVLUnYCJUg8d3/YV7aNCqraHBAZ9aoP
S4dl46KXC3qpaEBFfKaF+RcPSVUtc4eCQ74kKwIDAQABAoIBAGoArUN2IVjEaSy3
n7OIrFSK1oL6sa+x+JARWDFaU7NTj0wFLL65ee5Yhh0m/6a+IbiyA+IUx+d3m62Y
uRsVpJ7r9RXqZ/99v8SYrctSSGpzx41USXyEn4ggnu6nN5MhHMHyUwVYrH3fqkZR
EBFxfcrnTO8pY1vYFwayWKgpzOt7ip30JF1E7RH0IWfA2koJ+hZgSumPmF31btBK
eqDaQ168u0at6I7nYvRIWVT68D2k+PMb/c/rlOUYSyy+VfCgnShWD+m1hlyaDF1c
cbVvOhsul3rFeEqbToGN/6yyDDcyolTvYxMm3vb6jmoExZyRsShv0XyhokSuCN9P
v5SeNpkCgYEA7OpIlsZUoTXm2ffCQiZd8gRtKk0O3dzmWTkcNEgj2uUNH6ANNy3W
gLojKeF2EyC3appRWLVRYN/m6r/Qj+rztZfW3Jw1UJQV+tLEOBzk3yBnRdh1aRgW
8YTH1+HJqlJ/2iKJRKRhseM5AHiTslp7ude6cWQxO52pJ6Rbp1z3fBUCgYEAx/B4
LreIDJYDnYSyL/CvVkHEn1hCYX0oBpefzV6ofYDqv0OLe8BWOBsShQ3Crh0FuQTa
xV2xc+OzDewlu2OwNm4/X0qjXvoWkEMLBXKEHjPyxnbHLCYaaA/9ENmVIkc8aZWE
p7KcCYGlfiHpbdYWAD8KYdv5CsFHFbwhPwrD7z8CgYAEtsSq+1dDvebR/3QGDO1h
m2TwqofZMkQDEnfVMnpEKLqSHoUky+ywswNwGeRXjRcZL+jecv0jiFD36skjk/E1
c8f6q8ED0W5+hyMQWsLTDboAUcZESQ5rz9CKIxv4H5wbowRIMV0gRP0lXUDTE6nS
kNBM4Ul5fjGXcFXChr8F4QKBgGSmAeoKi9tCHTnLVePaNnmmi/Nm+6uV1HNVGqXI
k+rx3bpAp1O5o+2Ee1MtdSYvB/V2oyadnrnnEvjcOrZVXZxY7V/r88fY/0jJ5x9r
4WRO5FTR8DuiRsLB4bP8xB1IXPoNwYSl3fTPJd8T9S1MizC+i1xt3rVyTHV9igLx
SWcDAoGBAMoynJvQUOssWwFTtNQK0ptz95rrTkO2bri+8MJfSh8tessekwPHVe6M
SBofFhDiesrHBHczJ61qDnb3GemA0kEbo023mxNo0HPam+OFgX5mrihizBZnRZjh
aecVouDd0uwacsB76fwP6Fl5GhkFvOSBKr2IKNJjUMXyvW8/XGZE
-----END RSA PRIVATE KEY-----

27
tests/files/client.pem Normal file
View file

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEqDCCApCgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwVDELMAkGA1UEBhMCWFgx
FTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55
IEx0ZDEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yMDAyMjgwOTQ0MjlaFw00NzA3MTUw
OTQ0MjlaMF0xCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAa
BgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxGTAXBgNVBAMMEFRlc3QgQ2xpZW50
IENlcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5CKlKT37St3+o
EwNGIXNXy8OSxq55WmF6MYSCJCGq1QOCNz+i3+jszx8VULrBq7aLFOcQwe5leCgE
4oBPFfzTkWCASwssv3w6c3sDJNMo60dV8iaArd8nVj2X3n7ae4laGZpqfS+vdgyn
rPeVJr6GWGFNjH+Yi9NmGj8rf4wRODwMR+VBRKSECWsVIRqGvY+7/IROvxz7y8pM
OdkMu9wXLK4O0DP5yURBRWd1YlClClJV9H3jEq7NqPXSZQD1S2/hGRs0nvz8qB1N
toYg5UtSdgIlSDx3f9hXto0KqtocEBn1qg9Lh2XjopcLeqloQEV8poX5Fw9JVS1z
h4JDviQrAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5T
U0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBShQJXhPekXBpAdhKUK
vwip1379NzAfBgNVHSMEGDAWgBQ4kXolXxDcc+46nVRmj4rJhx12DDANBgkqhkiG
9w0BAQsFAAOCAgEAzY1ifNUYYVvWt0vR+gq37rLG5QZvlt5/pkVDYHgolChZCDmD
NWTH5MR/T+L+akFzbuIeOfQzTQMW/8fm9lHf6/e2coDtzLDKkukVCKIGZgrkyF9N
wT1xnvqCAERw0WfGFJn7auhIrExqgsrJbAorUMnVZAdrBosDWGMULNquyZMfVvAE
VXDDqjQIXe3AMA2yOgMEWoBVQMFl33BTz/NlGaE15wR8OsYRAs8/8nJewCLg4f6a
n2wz1/JIZI0ztTCO1cMrmI8i7TYIUlHQMK1QhF0aEFV1yFj+QDEc0iiIZu+i6O45
K1TjElXIr4MrEzdI3A5ZIN+VZ7mtzq/2CIZTTir94XLcQ3BvwYJssMWxAPQb6lRA
4k+7xifr3V7u7glMxpilIxV5CpZZ4anqRMzFbJa2MOmyAXZxk7SpcD2/MugHfJiL
TWfwFcClRogqXx/loEgzXExV1MrYKQBrqMlHB5eZ77M+PFpMhVh7yY5jjWT0giOM
MXFFKL8sI3VZdT9VcKZLgKpl4KWDh+cldyJdNNCDA29gtCRO+N+d4CAl6dK6nRrI
lL81UTEexmaCL9YjLBs70/HBKCkUDOmtAR7gJ01S4Kogv4gIepCAcGmVItzF+6fw
YsbkynxXIHQcODaB2d7FFzP0QJkc6pSMIi7ioO7a+aYx8NpIDYIxxLzF39Q=
-----END CERTIFICATE-----

8
tests/files/dh.pem Normal file
View file

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAjbYPkANn2XGqDGCzse9wAfM0I5WJpp+Xl+iNJFmaKXBguo0BPYQt
hZOpJbKL3aNaFsRxhdAJ8UXzBP6oIzCejcGti+jw+xtVk8ietWEK6e91yi+Ak2g2
/Xtt9hoYQkeoe5hkcv35NcJ0xdQwlSvMbY/j8HtKamx/A3zu+YPQAe/3AOe3L+JT
iEL5Gw00NPVnyEWKX4fVchAbMUkRsQKeXtsyOyDc4/RccjfLa1toyj8PRommK5UH
dkSqi04FTOUIx6aTwt21EehJuggLVDShoQdxGV+FzXmdtelLmerGMtVPBbf8DSkN
MKMBEg4d28DzjXPAWUHMD+JGPzAlvf87EwIBAg==
-----END DH PARAMETERS-----

31
tests/files/server.key Normal file
View file

@ -0,0 +1,31 @@
# password=test
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,ED349A8B098E2D1DB70C30F77EF599AB
j1rzje2sWFk3B9kD6eE7WrqVDynFEJ3t3kdOv0iUvH5Ybll1C7Qx3EFEdoM4z2OV
E6q3nr2DOvpMPox1DvBdIipWOQWJxkZyBHqNn4v4GR4c0uxLswsk7XSBQLUclRsn
QBGO6x8pcEA9u/O3PSrTt+pVozWrXWmR2UHNM//9WUsRpWF4Lv0EINzsfwmD7aJQ
nRcSfXsCggXP6wnJX5dgo5PlRm6R+bodgzePr0QRlh8TT6wnixZfWalYM5iUKlEF
GcE+VejZuBL69byl2AcRt8I5tQ+UZxmzhKPSsYN0NKD8vbcVVnp2sre/rbdTzWz5
laF386g1M8QBimDE/V3Bw5b9Bg1ZP3arlpugVXGVNA+HFti8PVdkaMqLgkFIC2Xu
OwmNKffAPIItuB8leg5A76oLoIlllRqjWO9M/O+MqAlrJ96xLRiUeGkez4Pp7eFV
30YrlOXyzwZKfXoOPIfE5Mbz4CPqR67XuqW8jOryIGOryMB17b0+vdRpDY0wxk8/
lGmc5rglDxLFA8dNemAHDednasCuVlrbsQsZRnPkKavXiSu7QCbvm1frAXZfnyRp
TpPmE6L4+nEy8PQnK/IxOCqRcy6e1SPezRpajRjB5ooDT8hDmDkG47NdnrB+kOKL
5LIpATLSGS9IVk0RW/M8EqJP1kRh2JOCQT3V+gUN0ttz8bjZpivKnp76/ztg0lo0
oC2lhuXV5HOYHw1z5jDazsYpQDYoHgYWXnzPJJp6Ecn+nkjZMKQjDV9ZqE1miPrZ
E4V0ULNmWaAQHvwc98yR97ui1YHmw5XVMoeDhy3fhB6IOyaGGdEj9o2iQr8kp9GC
dxBKK/xMOU6kwDF9Nsfh46veRGTbhAJdGeWqdxscdCupkO8KRtZqzL454+9GnYfe
n1f7wxJh7aTLNjF2an5Qa9v7uU6D58+9blxG7ls5qGt4xjBNAXCc8bPpmLqeCW4G
Xz8iwxECvwWIQ+SjUcXuP8+/NO58B14kDNP03+1gA7AHIesa2CTvHLCyMPaN2oGK
3R4LNxQQDNygEzRj8vHjURU1FNRJ4RjCi7SbqoOsl31Hvef6j0lcW0Sz4UICcCJI
p4NPnApoaHewL4exvlJ80qPbFscuVevXBlUC2LdxXS+9E+c0NaLauEeNYCUoaBDi
HIpbxRKXmqLc4LAKYVuEcIBFhdXp3UC9niVd7Nrguu0lUJXC78OzpltxWrqX/u4E
O2aCNK0Yg9U+rxm6wyccqEyptIS2GRCIpUGD/LVF3mOC16NB/JeYGrOWvDptdCeg
9pJrakJjE1Fm3pg4Xc74bT6IDj0EKwKSvZhtlcsM9JaXWChe/ZrDPPI/NP6MuyW4
jcqpa9HPBBSyaxKsEPXFJhdhrz8VfsU2e5VvcALaJaAOpHwZgaNUpvpsY4LPW9mi
lHsecEBiq6re0r7TAgBE1AnlaI4ho0fKSgSub3NWUZlEaBK3X2n/Li6op6LIsvM5
iySYaAluQy4dANww0KhQHMIh0jbuZGzmG2Hxk/poorYRf60YJlbTnHVD/FKUdFX+
rUow0iy8Ez1uF272u5orYW2tBbkhSaieKOT8f4HFCxUsgITbd8Lf/XJ6l6Qns6SK
-----END RSA PRIVATE KEY-----

27
tests/files/server.pem Normal file
View file

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEqDCCApCgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwVDELMAkGA1UEBhMCWFgx
FTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55
IEx0ZDEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yMDAyMjgwOTA2MTlaFw00NzA3MTUw
OTA2MTlaMF0xCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAa
BgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxGTAXBgNVBAMMEFRlc3QgU2VydmVy
IENlcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFwLjgCZ0pAEPB
HrtoxM+qY27od+Np1mqwPbFmPlcxV2fiRquqxVghsow6r5JHOCblpn+TNAgKlckx
d+PXpwgPoELE15x8lU5+8/IjSQJRsx/VhmJEbYWYHZWwbwBUMZaafhrtKZfJoIDR
pzx2SKmeUIXad10uFwFqT8uz6GNFg3tVIsu/E7wpPjaK+G4/1iAZjUrli1p73Qfg
cGIrFueTA5TGL3ChsRFJdIuBvh765fxZGurEghiYcX4bO/mSKVEWs/AoGajeJ1U+
uqOxyFl7Sjyb+ds2jdLNYYj191efT4qBmhB0bdmLFfq46GsKoneImaceSnhpcovl
jsmZf8UVAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5T
U0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTHtNcLFSKihvvmv/WQ
zKkJjL0yPTAfBgNVHSMEGDAWgBQ4kXolXxDcc+46nVRmj4rJhx12DDANBgkqhkiG
9w0BAQsFAAOCAgEALDrMv9TBWjVMeBUaRiaF+CDVD+Ng1Frzp37tSLbBmfcViaPl
2vQ23wDTc0SU+bamfyrIF0IRDDNM6cUkuC306+18AtXcz1taPFu6mWhtz+j4Hncm
EpMSUxvmTvr9dSUuEdX74v7tA4YqThzolEbsedAr8oe6RBjntAKugG9035z/mWuO
WNw8P3QI6eSX/APzFON/9dt9fkKU2piKgDQYvNEtCPqp1Tr2MkdRzfvyL/ZGR5yn
kuSRZLSFJyBHnnjCjrK40nt0ooVFXTpqDb8gEFn3DOLXHWLy6tWAQi6fKBQWai7v
8Mz7OyQHEwEsdM3bLWVJwe4tzJA2Ct22HsHWixuEEtQFwnuGm/tEOJ3HwVPjn5QP
2ut8yH5Ij45XeBTxkMiKie/Eb43ob8o30jCUtGuM48azyZ+byaIqVYZ/DX8NQAdC
EGRA1nWHmr9nFvcpa98kjiKcQtb7Nb/Ewq5ys/mYAwAs3yD9FNt+ujb1/y9PLN5Q
+NkcFrPuCmIj5c3jOi0AwPD+WuTHBwJ3D7+2gUByVYf6GravI6N6uXEjOD2/Wcl0
TtjlhiWvMK9bXu2F4FBLc+GXrawiG4aTNmi278bTSF6qIxrN5+A5JRU7eUtbCnpY
piA3Y/2Pu8YoM3coGWqtVfFddJr1raQ5LHjxIfDUd0kKPbfpjlaaa3aJQdg=
-----END CERTIFICATE-----

1
tests/playbooks/files Symbolic link
View file

@ -0,0 +1 @@
../files

View file

@ -0,0 +1,106 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- hosts: all
vars:
interface: 802-1x-test
tasks:
- 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
- block:
- name: "TEST: 802.1x profile with private key password and ca cert"
debug:
msg: "##################################################"
- 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"
802.1x:
identity: myhost
eap: tls
private-key: /etc/pki/tls/client.key
private-key-password: test
private-key-password-flags:
- none
client-cert: /etc/pki/tls/client.pem
ca-cert: /etc/pki/tls/cacert.pem
- name: "TEST: I can ping the EAP server"
shell: ping -c1 203.0.113.1
- import_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ interface }}"
persistent_state: absent
state: absent
- name: >-
TEST: 802.1x profile with unencrypted private key and
system ca certs
debug:
msg: "##################################################"
- name: Copy cacert to system truststore
copy:
src: cacert.pem
dest: /etc/pki/ca-trust/source/anchors/cacert.pem
mode: 0644
- name: Update ca trust
shell: update-ca-trust
- 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"
802.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
system-ca-certs: True
- name: "TEST: I can ping the EAP server"
shell: ping -c1 203.0.113.1
always:
- block:
- import_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ interface }}"
persistent_state: absent
state: absent
- name: br1
persistent_state: absent
state: absent
ignore_errors: true
- include_tasks: tasks/cleanup-802_1x_server.yml
tags:
- "tests::cleanup"

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Remove test interfaces
shell: |
ip netns delete ns1
ip link delete veth1-br
ip link delete veth2-br
ip link delete br1
- name: Kill hostapd process
shell: pkill hostapd

View file

@ -0,0 +1,80 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Install EPEL on enterprise Linux for hostapd
# yamllint disable-line rule:line-length
command: yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm
args:
warn: false
creates: /etc/yum.repos.d/epel.repo
when:
- ansible_distribution in ['RedHat', 'CentOS']
- name: Install hostapd
package:
name: hostapd
state: present
- name: Copy server certificates
copy:
src: "{{ item }}"
dest: "/etc/pki/tls/{{ item }}"
with_items:
- server.key
- dh.pem
- server.pem
- cacert.pem
- name: Create test interfaces
shell: |
ip link add veth1 type veth peer name veth1-br
ip link add veth2 type veth peer name veth2-br
ip link add br1 type bridge
ip link set br1 up
ip netns add ns1
ip link set veth1 netns ns1
ip netns exec ns1 ip addr add 203.0.113.1/24 dev veth1
ip link set veth1-br up
ip link set veth2-br up
ip link set veth1-br master br1
ip link set veth2-br master br1
ip netns exec ns1 ip link set veth1 up
ip link set veth2 up
# Enable forwarding of EAP 802.1x messages through software bridge "br1".
echo 8 > /sys/class/net/br1/bridge/group_fwd_mask
- name: Create hostapd config
copy:
content: |
interface=veth1
driver=wired
debug=2
ieee8021x=1
eap_reauth_period=3600
eap_server=1
use_pae_group_addr=1
eap_user_file=/etc/hostapd/hostapd.eap_user
ca_cert=/etc/pki/tls/cacert.pem
dh_file=/etc/pki/tls/dh.pem
server_cert=/etc/pki/tls/server.pem
private_key=/etc/pki/tls/server.key
private_key_passwd=test
logger_syslog=-1
logger_syslog_level=0
dest: /etc/hostapd/wired.conf
- name: Create eap_user_file config
copy:
content: |
* TLS
dest: /etc/hostapd/hostapd.eap_user
- name: Run hostapd in namespace
shell: ip netns exec ns1 hostapd -B /etc/hostapd/wired.conf && sleep 5

15
tests/tests_802_1x_nm.yml Normal file
View file

@ -0,0 +1,15 @@
---
# set network provider and gather facts
- hosts: all
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 NetworkManager, therefore it cannot run on RHEL/CentOS 6
- import_playbook: playbooks/tests_802_1x.yml
when: ansible_distribution_major_version != '6'

View file

@ -66,6 +66,7 @@ def pprint(msg, obj):
ARGS_CONNECTIONS = network_lsr.argument_validator.ArgValidator_ListConnections()
VALIDATE_ONE_MODE_INITSCRIPTS = ARGS_CONNECTIONS.VALIDATE_ONE_MODE_INITSCRIPTS
VALIDATE_ONE_MODE_NM = ARGS_CONNECTIONS.VALIDATE_ONE_MODE_NM
ETHTOOL_FEATURES_DEFAULTS = {
"esp-hw-offload": None,
@ -153,6 +154,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "5",
"parent": None,
@ -197,7 +199,8 @@ class TestValidator(unittest.TestCase):
if "type" in connection:
connection["nm.exists"] = False
connection["nm.uuid"] = n.Util.create_uuid()
mode = VALIDATE_ONE_MODE_INITSCRIPTS
mode = VALIDATE_ONE_MODE_NM
for idx, connection in enumerate(connections):
try:
ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx)
@ -248,7 +251,7 @@ class TestValidator(unittest.TestCase):
continue
if "type" not in connection:
continue
if connection["type"] in ["macvlan"]:
if connection["type"] in ["macvlan"] or connection["802.1x"]:
# initscripts do not support this type. Skip the test.
continue
content_current = kwargs.get("initscripts_content_current", None)
@ -394,6 +397,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "5",
"parent": None,
@ -444,6 +448,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "5",
"parent": None,
@ -488,6 +493,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "5",
"parent": None,
@ -575,6 +581,7 @@ class TestValidator(unittest.TestCase):
},
"mac": "52:54:00:44:9f:ba",
"master": None,
"802.1x": None,
"mtu": 1450,
"name": "prod1",
"parent": None,
@ -636,6 +643,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "prod1",
"parent": None,
@ -699,6 +707,7 @@ class TestValidator(unittest.TestCase):
},
"mac": "52:54:00:44:9f:ba",
"master": None,
"802.1x": None,
"mtu": 1450,
"name": "prod1",
"parent": None,
@ -754,6 +763,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "prod.100",
"parent": "prod1",
@ -836,6 +846,7 @@ class TestValidator(unittest.TestCase):
},
"mac": "52:54:00:44:9f:ba",
"master": None,
"802.1x": None,
"mtu": 1450,
"name": "prod1",
"parent": None,
@ -891,6 +902,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "prod.100",
"parent": "prod1",
@ -968,6 +980,7 @@ class TestValidator(unittest.TestCase):
},
"mac": "33:24:10:24:2f:b9",
"master": None,
"802.1x": None,
"mtu": 1450,
"name": "eth0-parent",
"parent": None,
@ -1018,6 +1031,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"macvlan": {"mode": "bridge", "promiscuous": True, "tap": False},
"master": None,
"802.1x": None,
"mtu": None,
"name": "veth0.0",
"parent": "eth0-parent",
@ -1068,6 +1082,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"macvlan": {"mode": "passthru", "promiscuous": False, "tap": True},
"master": None,
"802.1x": None,
"mtu": None,
"name": "veth0.1",
"parent": "eth0-parent",
@ -1153,6 +1168,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "prod2",
"parent": None,
@ -1189,6 +1205,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": "prod2",
"802.1x": None,
"mtu": None,
"name": "prod2-slave1",
"parent": None,
@ -1249,6 +1266,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "bond1",
"parent": None,
@ -1294,6 +1312,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "bond1",
"parent": None,
@ -1349,6 +1368,7 @@ class TestValidator(unittest.TestCase):
},
"mac": "aa:bb:cc:dd:ee:ff",
"master": None,
"802.1x": None,
"mtu": None,
"name": "5",
"parent": None,
@ -1392,6 +1412,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "5",
"parent": None,
@ -1463,6 +1484,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "6643-master",
"parent": None,
@ -1499,6 +1521,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": "6643-master",
"802.1x": None,
"mtu": None,
"name": "6643",
"parent": None,
@ -1551,6 +1574,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "infiniband.1",
"parent": None,
@ -1621,6 +1645,7 @@ class TestValidator(unittest.TestCase):
"mac": "11:22:33:44:55:66:77:88:99:00:"
"11:22:33:44:55:66:77:88:99:00",
"master": None,
"802.1x": None,
"mtu": None,
"name": "infiniband.2",
"parent": None,
@ -1711,6 +1736,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "555",
"parent": None,
@ -1809,6 +1835,7 @@ class TestValidator(unittest.TestCase):
},
"mac": None,
"master": None,
"802.1x": None,
"mtu": None,
"name": "e556",
"parent": None,
@ -1887,6 +1914,255 @@ class TestValidator(unittest.TestCase):
],
)
def test_802_1x_1(self):
"""
Test private key with password
"""
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": "802.1x-1",
"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,
"802.1x": {
"identity": "myhost",
"eap": "tls",
"private-key": "/etc/pki/tls/client.key",
"private-key-password": "p@55w0rD",
"private-key-password-flags": None,
"client-cert": "/etc/pki/tls/client.pem",
"ca-cert": "/etc/pki/tls/cacert.pem",
"system-ca-certs": False,
},
"mtu": None,
"name": "802.1x-1",
"parent": None,
"persistent_state": "present",
"slave_type": None,
"state": "up",
"type": "ethernet",
"wait": None,
"zone": None,
}
],
[
{
"name": "802.1x-1",
"state": "up",
"type": "ethernet",
"802.1x": {
"identity": "myhost",
"eap": "tls",
"private-key": "/etc/pki/tls/client.key",
"private-key-password": "p@55w0rD",
"client-cert": "/etc/pki/tls/client.pem",
"ca-cert": "/etc/pki/tls/cacert.pem",
},
}
],
)
def test_802_1x_2(self):
"""
Test private key without password and system-ca-certs
"""
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": "802.1x-2",
"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,
"802.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,
"system-ca-certs": True,
},
"mtu": None,
"name": "802.1x-2",
"parent": None,
"persistent_state": "present",
"slave_type": None,
"state": "up",
"type": "ethernet",
"wait": None,
"zone": None,
}
],
[
{
"name": "802.1x-2",
"state": "up",
"type": "ethernet",
"802.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"],
"system-ca-certs": True,
},
}
],
)
def test_invalid_cert_path(self):
"""
should fail if a relative path is used for 802.1x certs/keys
"""
self.maxDiff = None
self.do_connections_check_invalid(
[
{
"name": "802.1x-bad",
"state": "up",
"type": "ethernet",
"802.1x": {
"identity": "myhost",
"eap": "tls",
"private-key": "client.key",
"client-cert": "client.pem",
"private-key-password-flags": ["not-required"],
"system-ca-certs": True,
},
}
]
)
def test_invalid_password_flag(self):
"""
should fail if an invalid private key password flag is set
"""
self.maxDiff = None
self.do_connections_check_invalid(
[
{
"name": "802.1x-bad",
"state": "up",
"type": "ethernet",
"802.1x": {
"identity": "myhost",
"eap": "tls",
"private-key": "/etc/pki/tls/client.key",
"client-cert": "/etc/pki/tls/client.pem",
"private-key-password-flags": ["bad-flag"],
"system-ca-certs": True,
},
}
]
)
def test_802_1x_initscripts(self):
"""
should fail to create 802.1x connection with initscripts
"""
input_connections = [
{
"name": "802.1x-is",
"state": "up",
"type": "ethernet",
"802.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"],
"system-ca-certs": True,
},
}
]
connections = ARGS_CONNECTIONS.validate(input_connections)
self.assertRaises(
n.ValidationError,
ARGS_CONNECTIONS.validate_connection_one,
VALIDATE_ONE_MODE_INITSCRIPTS,
connections,
0,
)
def test_802_1x_non_ethernet(self):
"""
should fail if a non-ethernet interface has 802.1x settings defined
"""
input_connections = [
{
"name": "802.1x-bond",
"state": "up",
"type": "bond",
"802.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"],
"system-ca-certs": True,
},
}
]
connections = ARGS_CONNECTIONS.validate(input_connections)
self.assertRaises(
n.ValidationError,
ARGS_CONNECTIONS.validate_connection_one,
VALIDATE_ONE_MODE_NM,
connections,
0,
)
def test_invalid_mac(self):
self.maxDiff = None
self.do_connections_check_invalid(
@ -2216,6 +2492,11 @@ class TestNM(unittest.TestCase):
connections = nmutil.connection_list()
self.assertIsNotNone(connections)
def test_path_to_glib_bytes(self):
result = Util.path_to_glib_bytes("/my/test/path")
self.assertIsInstance(result, Util.GLib().Bytes)
self.assertEqual(result.get_data(), b"file:///my/test/path\x00")
class TestUtils(unittest.TestCase):
def test_check_output(self):
@ -2223,6 +2504,23 @@ class TestUtils(unittest.TestCase):
self.assertEqual(res, "test\n")
self.assertRaises(n.MyError, Util.check_output, ["false"])
def test_convert_passwd_flags_nm(self):
test_cases = [
([], 0),
(["none"], 0),
(["agent-owned"], 1),
(["not-saved"], 2),
(["agent-owned", "not-saved"], 3),
(["not-required"], 4,),
(["agent-owned", "not-required"], 5),
(["not-saved", "not-required"], 6),
(["agent-owned", "not-saved", "not-required"], 7),
]
for test_case in test_cases:
result = Util.convert_passwd_flags_nm(test_case[0])
self.assertEqual(result, test_case[1])
class TestSysUtils(unittest.TestCase):
def test_link_read_permaddress(self):