Add support for wireless connections

WPA-PSK and WPA-EAP are supported. Uses existing 802.1x features of the role.
Added extra functionality to ArgValidatorStr to enforce a min and max length.
This commit is contained in:
Jack Adolph 2020-03-27 22:11:15 +11:00 committed by Till Maas
parent 47ad99c7f0
commit 51f8e5b05f
12 changed files with 807 additions and 23 deletions

View file

@ -17,6 +17,7 @@ This role can be used to configure:
- VLAN interfaces
- MacVLAN interfaces
- Infiniband interfaces
- Wireless (WiFi) interfaces
- IP configuration
- 802.1x authentication
@ -63,10 +64,12 @@ List of variables:
provider (`nm` or `initscripts`) . Setting it to `{{ network_provider_os_default }}`,
the provider is set depending on the operating system. This is usually `nm`
except for RHEL 6 or CentOS 6 systems.
* `network_connections` - The connection profiles are configured as `network_connections`,
which is a list of dictionaries that include specific options.
* `network_allow_restart` - Certain configurations require the role to restart network services.
For example, if a wireless connection is configured and NetworkManager-wifi is not installed,
NetworkManager must be restarted prior to the connection being configured. Setting this to
`no` will prevent the role from restarting network service.
Examples of Variables
---------------------
@ -78,6 +81,7 @@ network_provider: nm
network_connections:
- name: eth0
#...
network_allow_restart: yes
```
Options
@ -186,6 +190,7 @@ The `type` option can be set to the following values:
- `vlan`
- `macvlan`
- `infiniband`
- `wireless`
#### `type: ethernet`
@ -232,6 +237,19 @@ role.
Similar to `master` and `vlan`, the `parent` references the connection profile in the ansible
role.
#### `type: wireless`
The `wireless` type supports WPA-PSK (password) authentication and WPA-EAP (802.1x) authentication.
`nm` (NetworkManager) is the only supported `network_provider` for this type.
If WPA-EAP is used, ieee802_1x settings must be defined in the [ieee802_1x](#-`ieee802_1x`) option.
The following options are supported:
* `ssid`: the SSID of the wireless network (required)
* `key_mgmt`: `wpa-psk` or `wpa-eap` (required)
* `password`: password for the network (required if `wpa-psk` is used)
### `autoconnect`
@ -642,6 +660,20 @@ network_connections:
- 192.168.1.1/24
```
Configuring a wireless connection:
```yaml
network_connections:
- name: wlan0
type: wireless
wireless:
ssid: "My WPA2-PSK Network"
key_mgmt: "wpa-psk"
# recommend vault encrypting the wireless password
# see https://docs.ansible.com/ansible/latest/user_guide/vault.html
password: "p@55w0rD"
```
Setting the IP configuration:
```yaml

View file

@ -2,6 +2,8 @@
---
network_connections: []
network_allow_restart: no
# Use initscripts for RHEL/CentOS < 7, nm otherwise
network_provider_os_default: "{{
'initscripts' if ansible_distribution in [
@ -20,10 +22,25 @@ __network_provider_current: "{{
# Default to the auto-detected value
network_provider: "{{ __network_provider_current }}"
# wpa_supplicant is required if any ieee802_1x connections are defined
__network_wpa_supplicant_required: "{{ network_connections |
# check if any 802.1x connections are defined
__network_ieee802_1x_connections_defined: "{{ network_connections |
selectattr('ieee802_1x', 'defined') | list | count > 0 }}"
__network_packages_default_802_1x: ["{% if __network_wpa_supplicant_required
# check if any wireless connections are defined
__network_wireless_connections_defined: "{{
['wireless'] in network_connections|json_query('[*][type]') }}"
# NetworkManager-wireless is required for wireless connections
__network_packages_default_wireless: ["{%
if __network_wireless_connections_defined
%}NetworkManager-wifi{% endif %}"]
# wpa_supplicant is required if any 802.1x or wireless connections are defined
__network_wpa_supplicant_required: "{{
__network_ieee802_1x_connections_defined or
__network_wireless_connections_defined }}"
__network_packages_default_wpa_supplicant: ["{%
if __network_wpa_supplicant_required
%}wpa_supplicant{% endif %}"]
# The python-gobject-base package depends on the python version and
@ -36,7 +53,8 @@ __network_packages_default_gobject_packages: ["python{{
__network_service_name_default_nm: NetworkManager
__network_packages_default_nm: "{{['NetworkManager']
+ __network_packages_default_gobject_packages|select()|list()
+ __network_packages_default_802_1x|select()|list()}}"
+ __network_packages_default_wpa_supplicant|select()|list()
+ __network_packages_default_wireless|select()|list()}}"
__network_service_name_default_initscripts: network

View file

@ -0,0 +1,13 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- hosts: network-test
vars:
network_connections:
- name: wlan0
type: wireless
wireless:
ssid: "My WPA2-PSK Network"
key_mgmt: "wpa-psk"
# recommend vault encrypting the wireless password
# see https://docs.ansible.com/ansible/latest/user_guide/vault.html
password: "p@55w0rD"

View file

@ -836,6 +836,28 @@ class NMUtil:
NM.SETTING_MACVLAN_PARENT,
ArgUtil.connection_find_master(connection["parent"], connections, idx),
)
elif connection["type"] == "wireless":
s_con.set_property(
NM.SETTING_CONNECTION_TYPE, NM.SETTING_WIRELESS_SETTING_NAME
)
s_wireless = self.connection_ensure_setting(con, NM.SettingWireless)
s_wireless.set_property(
NM.SETTING_WIRELESS_SSID,
Util.GLib().Bytes.new(connection["wireless"]["ssid"].encode("utf-8")),
)
s_wireless_sec = self.connection_ensure_setting(
con, NM.SettingWirelessSecurity
)
s_wireless_sec.set_property(
NM.SETTING_WIRELESS_SECURITY_KEY_MGMT,
connection["wireless"]["key_mgmt"],
)
if connection["wireless"]["key_mgmt"] == "wpa-psk":
s_wireless_sec.set_property(
NM.SETTING_WIRELESS_SECURITY_PSK, connection["wireless"]["password"]
)
else:
raise MyError("unsupported type %s" % (connection["type"]))

View file

@ -115,11 +115,27 @@ class ArgValidatorStr(ArgValidator):
default_value=None,
enum_values=None,
allow_empty=False,
min_length=None,
max_length=None,
):
ArgValidator.__init__(self, name, required, default_value)
self.enum_values = enum_values
self.allow_empty = allow_empty
if max_length is not None:
if not isinstance(max_length, int):
raise ValueError("max_length must be an integer")
elif max_length < 0:
raise ValueError("max_length must be a positive integer")
self.max_length = max_length
if min_length is not None:
if not isinstance(min_length, int):
raise ValueError("min_length must be an integer")
elif min_length < 0:
raise ValueError("min_length must be a positive integer")
self.min_length = min_length
def _validate_impl(self, value, name):
if not isinstance(value, Util.STRING_TYPE):
raise ValidationError(name, "must be a string but is '%s'" % (value))
@ -132,8 +148,36 @@ class ArgValidatorStr(ArgValidator):
)
if not self.allow_empty and not value:
raise ValidationError(name, "cannot be empty")
if not self._validate_string_max_length(value):
raise ValidationError(
name, "maximum length is %s characters" % (self.max_length)
)
if not self._validate_string_min_length(value):
raise ValidationError(
name, "minimum length is %s characters" % (self.min_length)
)
return value
def _validate_string_max_length(self, value):
"""
Ensures that the length of string `value` is less than or equal to
the maximum length
"""
if self.max_length is not None:
return len(str(value)) <= self.max_length
else:
return True
def _validate_string_min_length(self, value):
"""
Ensures that the length of string `value` is more than or equal to
the minimum length
"""
if self.min_length is not None:
return len(str(value)) >= self.min_length
else:
return True
class ArgValidatorNum(ArgValidator):
def __init__( # pylint: disable=too-many-arguments
@ -887,6 +931,42 @@ class ArgValidator_Dict802_1X(ArgValidatorDict):
return result
class ArgValidator_DictWireless(ArgValidatorDict):
VALID_KEY_MGMT = [
"wpa-psk",
"wpa-eap",
]
def __init__(self):
ArgValidatorDict.__init__(
self,
name="wireless",
nested=[
ArgValidatorStr("ssid", max_length=32),
ArgValidatorStr(
"key_mgmt", enum_values=ArgValidator_DictWireless.VALID_KEY_MGMT
),
ArgValidatorStr("password", default_value=None, max_length=63),
],
default_value=None,
)
def _validate_post(self, value, name, result):
if result["key_mgmt"] == "wpa-psk":
if result["password"] is None:
raise ValidationError(
name, "must supply a password if using 'wpa-psk' key management",
)
else:
if result["password"] is not None:
raise ValidationError(
name, "password only allowed if using 'wpa-psk' key management",
)
return result
class ArgValidator_DictConnection(ArgValidatorDict):
VALID_PERSISTENT_STATES = ["absent", "present"]
@ -899,6 +979,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
"bond",
"vlan",
"macvlan",
"wireless",
]
VALID_SLAVE_TYPES = ["bridge", "bond", "team"]
@ -949,6 +1030,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
ArgValidator_DictVlan(),
ArgValidator_DictMacvlan(),
ArgValidator_Dict802_1X(),
ArgValidator_DictWireless(),
# deprecated options:
ArgValidatorStr(
"infiniband_transport_mode",
@ -1081,9 +1163,42 @@ class ArgValidator_DictConnection(ArgValidatorDict):
self.VALID_FIELDS = valid_fields
return result
def _validate_post_wireless(self, value, name, result):
"""
Validate wireless settings
"""
if "type" in result:
if result["type"] == "wireless":
if "wireless" in result:
if (
result["wireless"]["key_mgmt"] == "wpa-eap"
and "ieee802_1x" not in result
):
raise ValidationError(
name + ".wireless",
"key management set to wpa-eap but no "
"'ieee802_1x' settings defined",
)
else:
raise ValidationError(
name + ".wireless",
"must define 'wireless' settings for 'type' 'wireless'",
)
else:
if "wireless" in result:
raise ValidationError(
name + ".wireless",
"'wireless' settings are not allowed for 'type' '%s'"
% (result["type"]),
)
return result
def _validate_post(self, value, name, result):
result = self._validate_post_state(value, name, result)
result = self._validate_post_fields(value, name, result)
result = self._validate_post_wireless(value, name, result)
if "type" in result:
@ -1299,6 +1414,15 @@ class ArgValidator_DictConnection(ArgValidatorDict):
% (result["type"]),
)
if "ieee802_1x" in result and result["type"] not in [
"ethernet",
"wireless",
]:
raise ValidationError(
name + ".ieee802_1x",
"802.1x settings only allowed for ethernet or wireless interfaces.",
)
for k in self.VALID_FIELDS:
if k in result:
continue
@ -1409,7 +1533,12 @@ class ArgValidator_ListConnections(ArgValidatorList):
"if you need to use initscripts.",
)
if connection["type"] != "ethernet":
# check if wireless connection is valid
if connection["type"] == "wireless":
if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS:
raise ValidationError.from_connection(
idx, "802.1x settings only allowed for ethernet interfaces."
idx,
"Wireless WPA auth is not supported by initscripts. "
"Configure wireless connection in /etc/wpa_supplicant.conf "
"if you need to use initscripts.",
)

View file

@ -23,6 +23,22 @@
state: present
when:
- not network_packages is subset(ansible_facts.packages.keys())
register: __network_package_install
# If network packages have changed and wireless connections are required,
# NetworkManager must be restarted
- name: Restart NetworkManager
service:
name: NetworkManager
state: restarted
when:
- __network_wireless_connections_defined
- network_provider == "nm"
- network_allow_restart
# ansible-lint wants this to be a handler, but this is not appropriate as
# NetworkManager must be restarted prior to the connections being created.
# see (https://docs.ansible.com/ansible-lint/rules/default_rules.html)
- __network_package_install.changed # noqa 503
- name: Enable and start NetworkManager
service:

View file

@ -61,10 +61,15 @@ NM_ONLY_TESTS = {
MINIMUM_VERSION: "'1.20.0'",
"comment": "# NetworkManager 1.20.0 introduced ethtool settings support",
},
"playbooks/tests_802_1x_updated.yml": {},
"playbooks/tests_802_1x.yml": {},
"playbooks/tests_reapply.yml": {},
"playbooks/tests_states.yml": {},
"playbooks/tests_802_1x.yml": {},
"playbooks/tests_802_1x_updated.yml": {},
# mac80211_hwsim (used for tests_wireless) only seems to be available
# and working on RHEL/CentOS 7
"playbooks/tests_wireless.yml": {
EXTRA_RUN_CONDITION: "ansible_distribution_major_version == '7'",
},
}
IGNORE = [

View file

@ -0,0 +1,88 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- hosts: all
vars:
interface: wlan0
tasks:
- name: "INIT: wireless tests"
debug:
msg: "##################################################"
- include_tasks: tasks/setup_mock_wifi.yml
- name: Copy client certs
copy:
src: "{{ item }}"
dest: "/etc/pki/tls/{{ item }}"
mode: 0644
with_items:
- client.key
- client.pem
- cacert.pem
- block:
- name: "TEST: wireless connection with WPA-PSK"
debug:
msg: "##################################################"
- import_role:
name: linux-system-roles.network
vars:
network_allow_restart: true
network_connections:
- name: "{{ interface }}"
state: up
type: wireless
ip:
address:
- 203.0.113.2/24
dhcp4: "no"
auto6: "no"
wireless:
ssid: "mock_wifi"
key_mgmt: "wpa-psk"
password: "p@55w0rD"
- import_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ interface }}"
persistent_state: absent
state: down
- name: "TEST: wireless connection with 802.1x TLS-EAP"
debug:
msg: "##################################################"
- import_role:
name: linux-system-roles.network
vars:
network_allow_restart: true
network_connections:
- name: "{{ interface }}"
state: up
type: wireless
ip:
address:
- 203.0.113.2/24
dhcp4: "no"
auto6: "no"
wireless:
ssid: "mock_wifi"
key_mgmt: "wpa-eap"
ieee802_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
always:
- block:
- import_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ interface }}"
persistent_state: absent
state: down
ignore_errors: true
- include_tasks: tasks/cleanup_mock_wifi.yml
tags:
- "tests::cleanup"

View file

@ -0,0 +1,7 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Unload mac80211_hwsim module
shell: modprobe -r mac80211_hwsim
- name: Kill hostapd process
shell: pkill hostapd

View file

@ -0,0 +1,91 @@
# 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 packages required to set up mock wifi network
package:
name:
- hostapd
- NetworkManager
- wpa_supplicant
state: present
- name: Ensure NetworkManager is running
service:
name: NetworkManager
state: started
- name: Copy server certificates
copy:
src: "{{ item }}"
dest: "/etc/pki/tls/{{ item }}"
with_items:
- server.key
- dh.pem
- server.pem
- cacert.pem
- name: Create hostapd config
copy:
content: |
interface=wlan1
driver=nl80211
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0
ssid=mock_wifi
country_code=EN
hw_mode=g
channel=7
auth_algs=3
wpa=3
ieee8021x=1
eapol_version=1
wpa_key_mgmt=WPA-EAP WPA-PSK
wpa_passphrase=p@55w0rD
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/wireless.conf
- name: Create eap_user_file config
copy:
content: |
* TLS
dest: /etc/hostapd/hostapd.eap_user
- name: Load mac80211_hwsim kernel module to mock a wifi network
shell: modprobe mac80211_hwsim && sleep 5
- name: Restart NetworkManager and wpa_supplicant
service:
name: "{{ item }}"
state: restarted
with_items:
- NetworkManager
- wpa_supplicant
- name: Configure wlan0 and wlan1 (mock wifi interfaces)
shell: |
ip link set up wlan0
ip link set up wlan1
nmcli device set wlan1 managed off
ip add add 203.0.113.1/24 dev wlan1
sleep 5
- name: Start hostapd
shell: hostapd -B /etc/hostapd/wireless.conf && sleep 5

View file

@ -0,0 +1,22 @@
# 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_wireless.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_wireless.yml
when:
- ansible_distribution_major_version != '6'
- ansible_distribution_major_version == '7'

View file

@ -156,6 +156,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "5",
"parent": None,
@ -252,7 +253,10 @@ class TestValidator(unittest.TestCase):
continue
if "type" not in connection:
continue
if connection["type"] in ["macvlan"] or connection["ieee802_1x"]:
if (
connection["type"] in ["macvlan", "wireless"]
or connection["ieee802_1x"]
):
# initscripts do not support this type. Skip the test.
continue
content_current = kwargs.get("initscripts_content_current", None)
@ -284,6 +288,50 @@ class TestValidator(unittest.TestCase):
v = network_lsr.argument_validator.ArgValidatorStr("state", required=True)
self.assertValidationError(v, None)
v = network_lsr.argument_validator.ArgValidatorStr(
"test_max_length", max_length=13
)
self.assertEqual("less_than_13", v.validate("less_than_13"))
self.assertValidationError(v, "longer_than_13")
v = network_lsr.argument_validator.ArgValidatorStr(
"test_min_length", min_length=13
)
self.assertEqual("longer_than_13", v.validate("longer_than_13"))
self.assertValidationError(v, "less_than_13")
v = network_lsr.argument_validator.ArgValidatorStr(
"test_min_max_length", min_length=10, max_length=15
)
self.assertEqual("13_characters", v.validate("13_characters"))
self.assertValidationError(v, "too_short")
self.assertValidationError(v, "string_is_too_long")
self.assertRaises(
ValueError,
network_lsr.argument_validator.ArgValidatorStr,
"non_int",
min_length="string",
)
self.assertRaises(
ValueError,
network_lsr.argument_validator.ArgValidatorStr,
"non_int",
max_length="string",
)
self.assertRaises(
ValueError,
network_lsr.argument_validator.ArgValidatorStr,
"negative_int",
min_length=-5,
)
self.assertRaises(
ValueError,
network_lsr.argument_validator.ArgValidatorStr,
"negative_int",
max_length=-5,
)
def test_validate_int(self):
v = network_lsr.argument_validator.ArgValidatorNum(
@ -399,6 +447,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "5",
"parent": None,
@ -450,6 +499,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "5",
"parent": None,
@ -495,6 +545,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "5",
"parent": None,
@ -583,6 +634,7 @@ class TestValidator(unittest.TestCase):
"mac": "52:54:00:44:9f:ba",
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": 1450,
"name": "prod1",
"parent": None,
@ -645,6 +697,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "prod1",
"parent": None,
@ -709,6 +762,7 @@ class TestValidator(unittest.TestCase):
"mac": "52:54:00:44:9f:ba",
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": 1450,
"name": "prod1",
"parent": None,
@ -765,6 +819,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "prod.100",
"parent": "prod1",
@ -848,6 +903,7 @@ class TestValidator(unittest.TestCase):
"mac": "52:54:00:44:9f:ba",
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": 1450,
"name": "prod1",
"parent": None,
@ -904,6 +960,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "prod.100",
"parent": "prod1",
@ -982,6 +1039,7 @@ class TestValidator(unittest.TestCase):
"mac": "33:24:10:24:2f:b9",
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": 1450,
"name": "eth0-parent",
"parent": None,
@ -1033,6 +1091,7 @@ class TestValidator(unittest.TestCase):
"macvlan": {"mode": "bridge", "promiscuous": True, "tap": False},
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "veth0.0",
"parent": "eth0-parent",
@ -1084,6 +1143,7 @@ class TestValidator(unittest.TestCase):
"macvlan": {"mode": "passthru", "promiscuous": False, "tap": True},
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "veth0.1",
"parent": "eth0-parent",
@ -1170,6 +1230,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "prod2",
"parent": None,
@ -1207,6 +1268,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": "prod2",
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "prod2-slave1",
"parent": None,
@ -1268,6 +1330,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "bond1",
"parent": None,
@ -1314,6 +1377,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "bond1",
"parent": None,
@ -1370,6 +1434,7 @@ class TestValidator(unittest.TestCase):
"mac": "aa:bb:cc:dd:ee:ff",
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "5",
"parent": None,
@ -1414,6 +1479,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "5",
"parent": None,
@ -1486,6 +1552,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "6643-master",
"parent": None,
@ -1523,6 +1590,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": "6643-master",
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "6643",
"parent": None,
@ -1576,6 +1644,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "infiniband.1",
"parent": None,
@ -1647,6 +1716,7 @@ class TestValidator(unittest.TestCase):
"11:22:33:44:55:66:77:88:99:00",
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "infiniband.2",
"parent": None,
@ -1738,6 +1808,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "555",
"parent": None,
@ -1837,6 +1908,7 @@ class TestValidator(unittest.TestCase):
"mac": None,
"master": None,
"ieee802_1x": None,
"wireless": None,
"mtu": None,
"name": "e556",
"parent": None,
@ -1960,6 +2032,7 @@ class TestValidator(unittest.TestCase):
"system_ca_certs": False,
"domain_suffix_match": None,
},
"wireless": None,
"mtu": None,
"name": "eth0",
"parent": None,
@ -2034,6 +2107,7 @@ class TestValidator(unittest.TestCase):
"system_ca_certs": True,
"domain_suffix_match": "example.com",
},
"wireless": None,
"mtu": None,
"name": "eth0",
"parent": None,
@ -2108,6 +2182,7 @@ class TestValidator(unittest.TestCase):
"system_ca_certs": False,
"domain_suffix_match": None,
},
"wireless": None,
"mtu": None,
"name": "eth0",
"parent": None,
@ -2136,6 +2211,150 @@ class TestValidator(unittest.TestCase):
],
)
def test_wireless_psk(self):
"""
Test wireless connection with wpa-psk auth
"""
self.maxDiff = None
self.do_connections_validate(
[
{
"actions": ["present", "up"],
"autoconnect": True,
"check_iface_exists": True,
"ethtool": ETHTOOL_DEFAULTS,
"force_state_change": None,
"ignore_errors": None,
"interface_name": "wireless1",
"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": None,
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-psk",
"password": "p@55w0rD",
},
"mtu": None,
"name": "wireless1",
"parent": None,
"persistent_state": "present",
"slave_type": None,
"state": "up",
"type": "wireless",
"wait": None,
"zone": None,
}
],
[
{
"name": "wireless1",
"state": "up",
"type": "wireless",
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-psk",
"password": "p@55w0rD",
},
}
],
)
def test_wireless_eap(self):
"""
Test wireless connection with wpa-eap
"""
self.maxDiff = None
self.do_connections_validate(
[
{
"actions": ["present", "up"],
"autoconnect": True,
"check_iface_exists": True,
"ethtool": ETHTOOL_DEFAULTS,
"force_state_change": None,
"ignore_errors": None,
"interface_name": "wireless1",
"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": "p@55w0rD",
"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,
},
"wireless": {
"ssid": "test wireless network",
"password": None,
"key_mgmt": "wpa-eap",
},
"mtu": None,
"name": "wireless1",
"parent": None,
"persistent_state": "present",
"slave_type": None,
"state": "up",
"type": "wireless",
"wait": None,
"zone": None,
}
],
[
{
"name": "wireless1",
"state": "up",
"type": "wireless",
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-eap",
},
"ieee802_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_invalid_cert_path(self):
"""
should fail if a relative path is used for 802.1x certs/keys
@ -2236,23 +2455,41 @@ class TestValidator(unittest.TestCase):
0,
)
def test_802_1x_non_ethernet(self):
def test_802_1x_unsupported_type(self):
"""
should fail if a non-ethernet interface has 802.1x settings defined
should fail if a non ethernet/wireless connection has 802.1x settings defined
"""
self.do_connections_check_invalid(
[
{
"name": "bond0",
"state": "up",
"type": "bond",
"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"],
"system_ca_certs": True,
},
}
]
)
def test_wireless_initscripts(self):
"""
should fail to create wireless connection with initscripts
"""
input_connections = [
{
"name": "bond0",
"name": "wireless1",
"state": "up",
"type": "bond",
"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"],
"system_ca_certs": True,
"type": "wireless",
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-psk",
"password": "p@55w0rD",
},
}
]
@ -2262,11 +2499,115 @@ class TestValidator(unittest.TestCase):
self.assertRaises(
n.ValidationError,
ARGS_CONNECTIONS.validate_connection_one,
VALIDATE_ONE_MODE_NM,
VALIDATE_ONE_MODE_INITSCRIPTS,
connections,
0,
)
def test_wireless_unsupported_type(self):
"""
should fail if a non wireless connection has wireless settings defined
"""
self.do_connections_check_invalid(
[
{
"name": "wireless-bond",
"state": "up",
"type": "bond",
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-psk",
"password": "p@55w0rD",
},
}
]
)
def test_wireless_ssid_too_long(self):
"""
should fail if ssid longer than 32 bytes
"""
self.do_connections_check_invalid(
[
{
"name": "wireless1",
"state": "up",
"type": "wireless",
"wireless": {
"ssid": "test wireless network with ssid too long",
"key_mgmt": "wpa-psk",
"password": "p@55w0rD",
},
}
]
)
def test_wireless_no_password(self):
"""
should fail if wpa-psk is selected and no password provided
"""
self.do_connections_check_invalid(
[
{
"name": "wireless1",
"state": "up",
"type": "wireless",
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-psk",
},
}
]
)
def test_wireless_password_too_long(self):
"""
should fail if wpa-psk is selected and no password provided
"""
self.do_connections_check_invalid(
[
{
"name": "wireless1",
"state": "up",
"type": "wireless",
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-psk",
"password": "This password is too long and should "
"not be able to validate properly",
},
}
]
)
def test_wireless_no_802_1x_for_wpa_eap(self):
"""
should fail if no 802.1x parameters are defined for a wireless
connection with key_mgmt=wpa-eap
"""
self.do_connections_check_invalid(
[
{
"name": "wireless1",
"state": "up",
"type": "wireless",
"wireless": {
"ssid": "test wireless network",
"key_mgmt": "wpa-eap",
},
}
]
)
def test_wireless_no_options_defined(self):
"""
should fail if a connection of type='wireless' does not
have any 'wireless' settings defined
"""
self.do_connections_check_invalid(
[{"name": "wireless1", "state": "up", "type": "wireless"}]
)
def test_invalid_mac(self):
self.maxDiff = None
self.do_connections_check_invalid(