Skip to content

Commit

Permalink
subiquity.models.network: use cloud-init networking on root read-only
Browse files Browse the repository at this point in the history
When cloudinit.features.NETPLAN_CONFIG_ROOT_READ_ONLY is True,
cloud-init will write /etc/netplan/50-cloud-init.yaml as read-only
root.

This added security allows for subiquity to use cloud-init's
network renderer directly allowing both datasource and network
configuration passed in one place.

Any netplan wifis configuration can be specified in a single network
config file /etc/cloud/cloud.cfg.d/90-installer-network.cfg instead of
having a separate config file for wifi, which could contain credentials.

This simplifies golden image creation from images installed using
subiquity because image builders will not need to track down and
purge separate /etc/netplan/00-installer-config.yaml and
/etc/netplan/subiquity-disable-cloudinit-networking.cfg when preparing
a golden image.

Eventually, netplan config validation and cloudinit will both support
separation of sensitive configuration without needing to pre-categorize.
This will allow cloud-init to grow to ability to write separate
world-readable configuration from config which is security sensitive
with no change needed in subiquity.
  • Loading branch information
blackboxsw committed Jan 9, 2023
1 parent 9563b54 commit ee21ba2
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 26 deletions.
58 changes: 38 additions & 20 deletions subiquity/models/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import logging

from cloudinit import features
from subiquitycore.models.network import NetworkModel as CoreNetworkModel

log = logging.getLogger('subiquity.models.network')
Expand All @@ -39,32 +40,49 @@ def render(self):
# perfect solution because in principle there could be wired 802.1x
# stuff that has secrets too but the subiquity UI does not support any
# of that yet so this will do for now.
wifis = netplan['network'].pop('wifis', None)
r = {
'write_files': {
'etc_netplan_installer': {
'path': 'etc/netplan/00-installer-config.yaml',
'content': self.stringify_config(netplan),
use_cloudinit_net = getattr(
features, "NETPLAN_CONFIG_ROOT_READ_ONLY", False
)
if use_cloudinit_net:
r = {
'write_files': {
'etc_netplan_installer': {
'path': ('etc/cloud/cloud.cfg.d'
'/90-installer-network.cfg'),
'content': self.stringify_config(netplan),
},
}
}
else:
# Separate sensitive wifi config from world-readable config
wifis = netplan['network'].pop('wifis', None)
r = {
'write_files': {
# Disable cloud-init networking
'no_cloudinit_net': {
'path': ('etc/cloud/cloud.cfg.d/'
'subiquity-disable-cloudinit-networking.cfg'),
'content': 'network: {config: disabled}\n',
},
'nonet': {
'path': ('etc/cloud/cloud.cfg.d/'
'subiquity-disable-cloudinit-networking.cfg'),
'content': 'network: {config: disabled}\n',
# World-readable netplan without sensitive wifi config
'etc_netplan_installer': {
'path': 'etc/netplan/00-installer-config.yaml',
'content': self.stringify_config(netplan),
},
},
}
if wifis is not None:
netplan_wifi = {
'network': {
'version': 2,
'wifis': wifis,
if wifis is not None:
netplan_wifi = {
'network': {
'version': 2,
'wifis': wifis,
},
}
r['write_files']['etc_netplan_installer_wifi'] = {
'path': 'etc/netplan/00-installer-config-wifi.yaml',
'content': self.stringify_config(netplan_wifi),
'permissions': '0600',
}
r['write_files']['etc_netplan_installer_wifi'] = {
'path': 'etc/netplan/00-installer-config-wifi.yaml',
'content': self.stringify_config(netplan_wifi),
'permissions': '0600',
}
return r

async def target_packages(self):
Expand Down
29 changes: 23 additions & 6 deletions subiquity/models/tests/test_subiquity.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import yaml

from cloudinit.config.schema import SchemaValidationError
from cloudinit import features

try:
from cloudinit.config.schema import SchemaProblem
except ImportError:
Expand Down Expand Up @@ -192,7 +194,11 @@ def test_write_netplan(self):
for fspec in config['write_files'].values():
if fspec['path'].startswith('etc/netplan'):
if netplan_content is not None:
self.fail("writing two files to netplan?")
self.fail("writing two files for network config?")
netplan_content = fspec['content']
if fspec['path'].endswith('cloud.cfg.d/90-installer-network.cfg'):
if netplan_content is not None:
self.fail("writing two files for network config?")
netplan_content = fspec['content']
self.assertIsNot(netplan_content, None)
netplan = yaml.safe_load(netplan_content)
Expand Down Expand Up @@ -267,8 +273,6 @@ def test_cloud_init_files_emits_datasource_config_and_clean_script(
model.identity.add_user(main_user)
model.userdata = {}
expected_files = {
'etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg':
'network: {config: disabled}\n',
'etc/cloud/cloud.cfg.d/99-installer.cfg': re.compile('datasource:\n None:\n metadata:\n instance-id: .*\n userdata_raw: "#cloud-config\\\\ngrowpart:\\\\n mode: \\\'off\\\'\\\\npreserve_hostname: true\\\\n\\\\\n'), # noqa
'etc/hostname': 'somehost\n',
'etc/cloud/ds-identify.cfg': 'policy: enabled\n',
Expand All @@ -279,10 +283,23 @@ def test_cloud_init_files_emits_datasource_config_and_clean_script(
cfg_files = [
"/" + key for key in expected_files.keys() if "host" not in key
]
cfg_files.append(
# Obtained from NetworkModel.render
"/etc/netplan/00-installer-config.yaml",
use_cloudinit_networking = getattr(
features, "NETPLAN_CONFIG_ROOT_READ_ONLY", False
)
if use_cloudinit_networking:
cfg_files.append(
# Obtained from NetworkModel.render
"/etc/cloud/cloud.cfg.d/90-installer-network.cfg"
)
else:
expected_files[
'etc/cloud/cloud.cfg.d/'
'subiquity-disable-cloudinit-networking.cfg'
] = 'network: {config: disabled}\n'
cfg_files.append(
# Obtained from NetworkModel.render
"/etc/netplan/00-installer-config.yaml",
)
header = "# Autogenerated by Subiquity: 2004-03-05 ... UTC\n"
with self.subTest('Stable releases Jammy do not disable cloud-init'):
lsb_release.return_value = {"release": "22.04"}
Expand Down

0 comments on commit ee21ba2

Please sign in to comment.