Skip to content

Commit fc269d0

Browse files
committed
fix(dns): validate nameserver addresses are valid IP addresses
Resolves #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. Signed-off-by: Maxime Grenu <maxime.grenu@gmail.com>
1 parent 7064cba commit fc269d0

3 files changed

Lines changed: 53 additions & 0 deletions

File tree

Sources/Containerization/DNSConfiguration.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
import ContainerizationExtras
18+
1719
/// DNS configuration for a container. The values will be used to
1820
/// construct /etc/resolv.conf for a given container.
1921
public struct DNS: Sendable {
@@ -41,6 +43,26 @@ public struct DNS: Sendable {
4143
self.searchDomains = searchDomains
4244
self.options = options
4345
}
46+
47+
/// Validates the DNS configuration.
48+
///
49+
/// Ensures that all nameserver entries are valid IPv4 or IPv6 addresses.
50+
/// Arbitrary hostnames are not permitted as nameservers.
51+
///
52+
/// - Throws: ``ContainerizationError`` with code `.invalidArgument` if
53+
/// any nameserver is not a valid IP address.
54+
public func validate() throws {
55+
for nameserver in nameservers {
56+
let isValidIPv4 = (try? IPv4Address(nameserver)) != nil
57+
let isValidIPv6 = (try? IPv6Address(nameserver)) != nil
58+
if !isValidIPv4 && !isValidIPv6 {
59+
throw ContainerizationError(
60+
.invalidArgument,
61+
message: "nameserver '\(nameserver)' is not a valid IPv4 or IPv6 address"
62+
)
63+
}
64+
}
65+
}
4466
}
4567

4668
extension DNS {

Sources/Containerization/Vminitd.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ extension Vminitd {
408408

409409
/// Configure DNS within the sandbox's environment.
410410
public func configureDNS(config: DNS, location: String) async throws {
411+
try config.validate()
411412
_ = try await client.configureDns(
412413
.with {
413414
$0.location = location

Tests/ContainerizationTests/DNSTests.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,34 @@ struct DNSTests {
5151
let expected = "nameserver 8.8.8.8\n"
5252
#expect(dns.resolvConf == expected)
5353
}
54+
55+
@Test func dnsValidateAcceptsValidIPv4Nameservers() throws {
56+
let dns = DNS(nameservers: ["8.8.8.8", "1.1.1.1"])
57+
#expect(throws: Never.self) { try dns.validate() }
58+
}
59+
60+
@Test func dnsValidateAcceptsValidIPv6Nameservers() throws {
61+
let dns = DNS(nameservers: ["2001:4860:4860::8888", "::1"])
62+
#expect(throws: Never.self) { try dns.validate() }
63+
}
64+
65+
@Test func dnsValidateAcceptsMixedIPv4AndIPv6Nameservers() throws {
66+
let dns = DNS(nameservers: ["8.8.8.8", "2001:4860:4860::8844"])
67+
#expect(throws: Never.self) { try dns.validate() }
68+
}
69+
70+
@Test func dnsValidateAcceptsEmptyNameservers() throws {
71+
let dns = DNS(nameservers: [])
72+
#expect(throws: Never.self) { try dns.validate() }
73+
}
74+
75+
@Test func dnsValidateRejectsHostname() {
76+
let dns = DNS(nameservers: ["dns.example.com"])
77+
#expect(throws: (any Error).self) { try dns.validate() }
78+
}
79+
80+
@Test func dnsValidateRejectsInvalidAddress() {
81+
let dns = DNS(nameservers: ["not-an-ip"])
82+
#expect(throws: (any Error).self) { try dns.validate() }
83+
}
5484
}

0 commit comments

Comments
 (0)