From bc302e39369533717786e36b1c16fa884f077ff8 Mon Sep 17 00:00:00 2001 From: Maxime Grenu Date: Wed, 18 Feb 2026 20:00:55 +0100 Subject: [PATCH] fix(dns): validate nameserver addresses are valid IP addresses Resolves apple/containerization#467. Previously, any arbitrary string could be passed as a nameserver in DNS configuration, which would silently result in an invalid /etc/resolv.conf inside the container. This change adds a DNS.validate() method that ensures every nameserver string is a valid IPv4 or IPv6 address (using the existing ContainerizationExtras parsers). The method is called from Vminitd.configureDNS() before applying the configuration. Tests added to DNSTests.swift covering valid IPv4, IPv6, mixed, empty nameserver lists, and invalid hostname/address rejection. --- .../Containerization/DNSConfiguration.swift | 22 ++++++++++++++ Sources/Containerization/Vminitd.swift | 1 + Tests/ContainerizationTests/DNSTests.swift | 30 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/Sources/Containerization/DNSConfiguration.swift b/Sources/Containerization/DNSConfiguration.swift index 8c9e601f..dc0abe47 100644 --- a/Sources/Containerization/DNSConfiguration.swift +++ b/Sources/Containerization/DNSConfiguration.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +import ContainerizationExtras + /// DNS configuration for a container. The values will be used to /// construct /etc/resolv.conf for a given container. public struct DNS: Sendable { @@ -41,6 +43,26 @@ public struct DNS: Sendable { self.searchDomains = searchDomains self.options = options } + + /// Validates the DNS configuration. + /// + /// Ensures that all nameserver entries are valid IPv4 or IPv6 addresses. + /// Arbitrary hostnames are not permitted as nameservers. + /// + /// - Throws: ``ContainerizationError`` with code `.invalidArgument` if + /// any nameserver is not a valid IP address. + public func validate() throws { + for nameserver in nameservers { + let isValidIPv4 = (try? IPv4Address(nameserver)) != nil + let isValidIPv6 = (try? IPv6Address(nameserver)) != nil + if !isValidIPv4 && !isValidIPv6 { + throw ContainerizationError( + .invalidArgument, + message: "nameserver '\(nameserver)' is not a valid IPv4 or IPv6 address" + ) + } + } + } } extension DNS { diff --git a/Sources/Containerization/Vminitd.swift b/Sources/Containerization/Vminitd.swift index f2caa15e..6edb63c9 100644 --- a/Sources/Containerization/Vminitd.swift +++ b/Sources/Containerization/Vminitd.swift @@ -406,6 +406,7 @@ extension Vminitd { /// Configure DNS within the sandbox's environment. public func configureDNS(config: DNS, location: String) async throws { + try config.validate() _ = try await client.configureDns( .with { $0.location = location diff --git a/Tests/ContainerizationTests/DNSTests.swift b/Tests/ContainerizationTests/DNSTests.swift index a003b919..fe866b97 100644 --- a/Tests/ContainerizationTests/DNSTests.swift +++ b/Tests/ContainerizationTests/DNSTests.swift @@ -51,4 +51,34 @@ struct DNSTests { let expected = "nameserver 8.8.8.8\n" #expect(dns.resolvConf == expected) } + + @Test func dnsValidateAcceptsValidIPv4Nameservers() throws { + let dns = DNS(nameservers: ["8.8.8.8", "1.1.1.1"]) + #expect(throws: Never.self) { try dns.validate() } + } + + @Test func dnsValidateAcceptsValidIPv6Nameservers() throws { + let dns = DNS(nameservers: ["2001:4860:4860::8888", "::1"]) + #expect(throws: Never.self) { try dns.validate() } + } + + @Test func dnsValidateAcceptsMixedIPv4AndIPv6Nameservers() throws { + let dns = DNS(nameservers: ["8.8.8.8", "2001:4860:4860::8844"]) + #expect(throws: Never.self) { try dns.validate() } + } + + @Test func dnsValidateAcceptsEmptyNameservers() throws { + let dns = DNS(nameservers: []) + #expect(throws: Never.self) { try dns.validate() } + } + + @Test func dnsValidateRejectsHostname() { + let dns = DNS(nameservers: ["dns.example.com"]) + #expect(throws: (any Error).self) { try dns.validate() } + } + + @Test func dnsValidateRejectsInvalidAddress() { + let dns = DNS(nameservers: ["not-an-ip"]) + #expect(throws: (any Error).self) { try dns.validate() } + } }