]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hostnamed: allow networkd to set the transient hostname
authorMartin Pitt <martin.pitt@ubuntu.com>
Fri, 18 Nov 2016 15:17:01 +0000 (16:17 +0100)
committerMartin Pitt <martin.pitt@ubuntu.com>
Sun, 20 Nov 2016 11:19:21 +0000 (12:19 +0100)
systemd-networkd runs as user "systemd-network" and thus is not privileged to
set the transient hostname:

  systemd-networkd[516]: ens3: Could not set hostname: Interactive authentication required.

Standard polkit *.policy files do not have a syntax for granting privileges to
a user, so ship a pklocalauthority (for polkit < 106) and a JavaScript rules
file (for polkit >= 106) that grants the "systemd-network" system user that
privilege.

Add DnsmasqClientTest.test_transient_hostname() test to networkd-test.py to
cover this. Make do_test() a bit more flexible by interpreting "coldplug==None"
as "test sets up the interface by itself". Change DnsmasqClientTest to set up
test_eth42 with a fixed MAC address so that we can configure dnsmasq to send a
special host name for that.

Fixes #4646

Makefile.am
src/hostname/systemd-networkd-hostname.pkla [new file with mode: 0644]
src/hostname/systemd-networkd-hostname.rules [new file with mode: 0644]
test/networkd-test.py

index 47c2ec8a8d4cc2f7b7ec606e93bd30b1c5445729..10ce36334799f924cd545076bfd30ff7fd100952 100644 (file)
@@ -55,6 +55,8 @@ pamconfdir=@pamconfdir@
 pkgconfigdatadir=$(datadir)/pkgconfig
 pkgconfiglibdir=$(libdir)/pkgconfig
 polkitpolicydir=$(datadir)/polkit-1/actions
+polkitrulesdir=$(datadir)/polkit-1/rules.d
+polkitpkladir=$(localstatedir)/lib/polkit-1/localauthority/10-vendor.d
 bashcompletiondir=@bashcompletiondir@
 zshcompletiondir=@zshcompletiondir@
 rpmmacrosdir=$(prefix)/lib/rpm/macros.d
@@ -116,6 +118,8 @@ pkgconfiglib_DATA =
 polkitpolicy_in_in_files =
 polkitpolicy_in_files =
 polkitpolicy_files =
+polkitrules_files =
+polkitpkla_files =
 dist_udevrules_DATA =
 nodist_udevrules_DATA =
 dist_pkgsysconf_DATA =
@@ -4836,8 +4840,16 @@ endif
 polkitpolicy_in_files += \
        src/hostname/org.freedesktop.hostname1.policy.in
 
+polkitrules_files += \
+       src/hostname/systemd-networkd-hostname.rules
+
+polkitpkla_files += \
+       src/hostname/systemd-networkd-hostname.pkla
+
 EXTRA_DIST += \
-       units/systemd-hostnamed.service.in
+       units/systemd-hostnamed.service.in \
+       src/hostname/systemd-networkd-hostname.rules \
+       src/hostname/systemd-networkd-hostname.pkla
 
 # ------------------------------------------------------------------------------
 dist_systemunit_DATA_busnames += \
@@ -6189,6 +6201,8 @@ if ENABLE_POLKIT
 nodist_polkitpolicy_DATA = \
        $(polkitpolicy_files) \
        $(polkitpolicy_in_in_files:.policy.in.in=.policy)
+polkitrules_DATA = $(polkitrules_files)
+polkitpkla_DATA = $(polkitpkla_files)
 endif
 
 EXTRA_DIST += \
diff --git a/src/hostname/systemd-networkd-hostname.pkla b/src/hostname/systemd-networkd-hostname.pkla
new file mode 100644 (file)
index 0000000..345ce61
--- /dev/null
@@ -0,0 +1,4 @@
+[Allow systemd-networkd to set transient hostname]
+Identity=unix-user:systemd-network
+Action=org.freedesktop.hostname1.set-hostname
+ResultAny=yes
diff --git a/src/hostname/systemd-networkd-hostname.rules b/src/hostname/systemd-networkd-hostname.rules
new file mode 100644 (file)
index 0000000..b7b780d
--- /dev/null
@@ -0,0 +1,5 @@
+polkit.addRule(function(action, subject) {
+    if (action.id == "org.freedesktop.hostname1.set-hostname" && subject.user == "systemd-network") {
+        return polkit.Result.YES;
+    }
+});
index 9767bd72f22f7ba08dbee96d1f6ee9a394a2de1c..e3007325fabfa95b869138034e680b4947eb8f3b 100755 (executable)
@@ -123,10 +123,13 @@ DHCP=%s
             # create interface first, then start networkd
             self.create_iface(ipv6=ipv6)
             subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
-        else:
+        elif coldplug is not None:
             # start networkd first, then create interface
             subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
             self.create_iface(ipv6=ipv6)
+        else:
+            # "None" means test sets up interface by itself
+            subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
 
         try:
             subprocess.check_call([self.networkd_wait_online, '--interface',
@@ -196,7 +199,7 @@ DHCP=%s
         else:
             self.fail('nameserver 192.168.5.1 not found in ' + RESOLV_CONF)
 
-        if not coldplug:
+        if coldplug is False:
             # check post-down.d hook
             self.shutdown_iface()
 
@@ -293,13 +296,15 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase):
     def setUp(self):
         super().setUp()
         self.dnsmasq = None
+        self.iface_mac = 'de:ad:be:ef:47:11'
 
     def create_iface(self, ipv6=False, dnsmasq_opts=None):
         '''Create test interface with DHCP server behind it'''
 
         # add veth pair
-        subprocess.check_call(['ip', 'link', 'add', 'name', self.iface, 'type',
-                               'veth', 'peer', 'name', self.if_router])
+        subprocess.check_call(['ip', 'link', 'add', 'name', self.iface,
+                               'address', self.iface_mac,
+                               'type', 'veth', 'peer', 'name', self.if_router])
 
         # give our router an IP
         subprocess.check_call(['ip', 'a', 'flush', 'dev', self.if_router])
@@ -415,6 +420,19 @@ Domains= ~company ~lab''')
         self.assertRegex(general_log, 'query.*megasearch.net')
         self.assertNotIn('megasearch.net', vpn_log)
 
+    def test_transient_hostname(self):
+        '''networkd sets transient hostname from DHCP'''
+
+        self.create_iface(dnsmasq_opts=['--dhcp-host=%s,192.168.5.210,testgreen' % self.iface_mac])
+        self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=False', dhcp_mode='ipv4')
+
+        # should have received the fixed IP above
+        out = subprocess.check_output(['ip', '-4', 'a', 'show', 'dev', self.iface])
+        self.assertRegex(out, b'inet 192.168.5.210/24 .* scope global dynamic')
+
+        # should have set transient hostname
+        self.assertIn(b'testgreen', subprocess.check_output(['hostnamectl']))
+
 
 class NetworkdClientTest(ClientTestBase, unittest.TestCase):
     '''Test networkd client against networkd server'''