cloud-init fails with KeyError when nameserver config and matching interface are present

Bug #1979877 reported by Jonas Konrad
14
This bug affects 2 people
Affects Status Importance Assigned to Milestone
cloud-init
Fix Released
High
Brett Holman

Bug Description

cloud-init fails to parse network configuration with a KeyError if:

- The configuration has a `nameservers` configuration, and
- The configuration matches an existing interface on the machine, and
- The configuration name does not match that interfaces name.

Consider the following network config:

```
version: 2
ethernets:
  eth:
    match:
      macaddress: '00:11:22:33:44:55'
    addresses: [10.0.0.2/24]
    gateway4: 10.0.0.1
    nameservers:
      addresses: [10.0.0.1]
```

Now, if a device is present with the mac address 00:11:22:33:44:55, parsing will fail with the following stack trace:

```
2022-06-25 10:26:43,657 - util.py[WARNING]: failed stage init-local
2022-06-25 10:26:43,657 - util.py[DEBUG]: failed stage init-local
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/cloudinit/cmd/main.py", line 738, in status_wrapper
    ret = functor(name, args)
  File "/usr/lib/python3/dist-packages/cloudinit/cmd/main.py", line 410, in main_init
    init.apply_network_config(bring_up=bring_up_interfaces)
  File "/usr/lib/python3/dist-packages/cloudinit/stages.py", line 937, in apply_network_config
    return self.distro.apply_network_config(
  File "/usr/lib/python3/dist-packages/cloudinit/distros/__init__.py", line 231, in apply_network_config
    network_state = parse_net_config_data(netconfig)
  File "/usr/lib/python3/dist-packages/cloudinit/net/network_state.py", line 1056, in parse_net_config_data
    nsi.parse_config(skip_broken=skip_broken)
  File "/usr/lib/python3/dist-packages/cloudinit/net/network_state.py", line 278, in parse_config
    self.parse_config_v2(skip_broken=skip_broken)
  File "/usr/lib/python3/dist-packages/cloudinit/net/network_state.py", line 328, in parse_config_v2
    self._v2_common(command)
  File "/usr/lib/python3/dist-packages/cloudinit/net/network_state.py", line 782, in _v2_common
    self._handle_individual_nameserver(name_cmd, iface)
  File "/usr/lib/python3/dist-packages/cloudinit/net/network_state.py", line 110, in decorator
    return func(self, command, *args, **kwargs)
  File "/usr/lib/python3/dist-packages/cloudinit/net/network_state.py", line 570, in _handle_individual_nameserver
    _iface[iface]["dns"] = {"nameservers": nameservers, "search": search}
KeyError: 'eth'
```

I've investigated the issue myself and created a test case:

```
from unittest import mock

from cloudinit import safeyaml
from cloudinit.net import network_state

class TestNetDns:
    @mock.patch("cloudinit.net.network_state.get_interfaces_by_mac")
    def test_networkd_render_x(self, by_mac):
        by_mac.return_value = {"00:11:22:33:44:55": "foobar"}
        network_state.parse_net_config_data(safeyaml.load("""\
version: 2
ethernets:
  eth:
    match:
      macaddress: '00:11:22:33:44:55'
    addresses: [10.0.0.2/24]
    gateway4: 10.0.0.1
    nameservers:
      addresses: [10.0.0.1]
"""))
```

This test case will fail with the KeyError.

The reason for this bug is that `handle_ethernets` will take the interface name from the system because no set-name setting is present. It will then add the network state under that interface name, not the configured key 'eth'. Later, _handle_individual_nameserver will try to look up the state for 'eth' and fail.

A quick bisect shows that this test case starts failing with commit bf94945fb855c40c5188cef5fb00327c51c41fef (though the mock doesn't work very far in the past). This commit introduced the name handling logic.

As a workaround, you can give an explicit set-name statement, or change the key of the network config to match the physical device name.

Brett Holman (holmanb)
Changed in cloud-init:
status: New → Confirmed
Revision history for this message
Brett Holman (holmanb) wrote :

Wow, really appreciate the effort tracking this down and providing a workaround, bisected commit, and test case!

Are you interested in contributing this test case to the project on Github? I think this test would be a welcome addition.

I'm working on a fix now.

Revision history for this message
Jonas Konrad (yawkat) wrote :

Unfortunately I can't contribute the code. I found this issue in my free time, but the ubuntu CLA would require agreement from my employer, which could take weeks to approve if it'd be approved at all (I work for Oracle).

Thanks for working on this issue so promptly.

Revision history for this message
Brett Holman (holmanb) wrote :

Hi Jonas,

We (cloud-init team at Canonical) checked internally, and we already have an agreement in place with Oracle.

In March 2021, the CLA agreement was signed by both Canonical and Oracle that allows accepting Oracle contributions to cloud-init. You shouldn't need to sign the CLA, given that your employer already has.

Therefore we can add your contribution, assuming you want to.

Cheers,
Brett

James Falcon (falcojr)
Changed in cloud-init:
importance: Undecided → High
Brett Holman (holmanb)
Changed in cloud-init:
status: Confirmed → Fix Committed
assignee: nobody → Brett Holman (holmanb)
Revision history for this message
Brett Holman (holmanb) wrote : Fixed in cloud-init version 22.3.

This bug is believed to be fixed in cloud-init in version 22.3. If this is still a problem for you, please make a comment and set the state back to New

Thank you.

Changed in cloud-init:
status: Fix Committed → Fix Released
Revision history for this message
Danny Howard (dannyman) wrote :

Can anyone advise when the cloud images shipped at https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img and https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img will be equipped with cloud-init 22.3? I just tried pulling the jammy image, and I've got no network and see cloud-init 22.2-0ubuntu1~22.04.3 in the image.

Revision history for this message
James Falcon (falcojr) wrote :
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Duplicates of this bug

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.