diff --git a/subiquity/models/network.py b/subiquity/models/network.py index 504b9c462..5351656cb 100644 --- a/subiquity/models/network.py +++ b/subiquity/models/network.py @@ -62,7 +62,8 @@ def render(self): } } else: - # Separate sensitive wifi config from world-readable config + # Separate sensitive wifi config from potentially shared config + # e.g. via apport wifis = netplan["network"].pop("wifis", None) r = { "write_files": { @@ -73,11 +74,13 @@ def render(self): "subiquity-disable-cloudinit-networking.cfg" ), "content": "network: {config: disabled}\n", + "permissions": "0600", }, - # World-readable netplan without sensitive wifi config + # netplan without sensitive wifi config "etc_netplan_installer": { "path": "etc/netplan/00-installer-config.yaml", "content": self.stringify_config(netplan), + "permissions": "0600", }, }, } @@ -88,6 +91,7 @@ def render(self): "wifis": wifis, }, } + # sensitive wifi config r["write_files"]["etc_netplan_installer_wifi"] = { "path": "etc/netplan/00-installer-config-wifi.yaml", "content": self.stringify_config(netplan_wifi), diff --git a/subiquity/models/tests/test_network.py b/subiquity/models/tests/test_network.py index 636812c28..bd3a10b7c 100644 --- a/subiquity/models/tests/test_network.py +++ b/subiquity/models/tests/test_network.py @@ -42,3 +42,13 @@ async def test_is_nm_enabled(self): with mock.patch("subiquity.models.network.arun_command") as arun: arun.side_effect = subprocess.CalledProcessError(1, [], None, "error") self.assertFalse(await self.model.is_nm_enabled()) + + async def test_write_netplan_permissions(self): + """Assert correct netplan config permissions + + Since netplan 0.106.1, Netplan YAMLs should have file + permissions with mode 0o600 (root/owner RW only). + """ + config = self.model.render() + for file in config["write_files"].values(): + self.assertEqual(file["permissions"], "0600") diff --git a/subiquity/server/controllers/tests/test_network.py b/subiquity/server/controllers/tests/test_network.py new file mode 100644 index 000000000..c8cdc3cf7 --- /dev/null +++ b/subiquity/server/controllers/tests/test_network.py @@ -0,0 +1,57 @@ +# Copyright 2024 Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from unittest.mock import ANY, Mock, patch + +import jsonschema +from jsonschema.validators import validator_for + +from subiquity.server.controllers.network import NetworkController +from subiquitycore.tests import SubiTestCase +from subiquitycore.tests.mocks import make_app + + +class TestNetworkController(SubiTestCase): + def setUp(self): + app = make_app() + app.note_file_for_apport = Mock() + app.opts.output_base = self.tmp_dir() + app.opts.project = "subiquity" + self.controller = NetworkController(app) + self.controller.model.render_config = Mock(return_value=dict()) + self.controller.model.stringify_config = Mock(return_value="") + + def test_netplan_permissions(self): + """Assert correct netplan config permissions + + Since netplan 0.106.1, Netplan YAMLs should have file + permissions with mode 0o600 (root/owner RW only). + """ + + with patch("os.getuid", return_value=0): + with patch("os.chmod") as mock_chmod: + with patch("os.chown") as mock_chown: + self.controller._write_config() + mock_chmod.assert_called_with(ANY, 0o600) + mock_chown.assert_called_with(ANY, 0, 0) + + def test_valid_schema(self): + """Test that the expected autoinstall JSON schema is valid""" + + JsonValidator: jsonschema.protocols.Validator = validator_for( + NetworkController.autoinstall_schema + ) + + JsonValidator.check_schema(NetworkController.autoinstall_schema)