diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index 462c7b36a..dffad7fe8 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2505,4 +2505,8 @@ On first run, creates the file with all settings commented out at their defaults
Requested load but no input provided.
+
+ Cannot use --no-dns with --dns, --dns-domain, --dns-option, or --dns-search
+ {Locked="--no-dns"}{Locked="--dns"}{Locked="--dns-domain"}{Locked="--dns-option"}{Locked="--dns-search"}Command line arguments should not be translated
+
diff --git a/src/windows/wslc/arguments/ArgumentDefinitions.h b/src/windows/wslc/arguments/ArgumentDefinitions.h
index 3748a95cd..ed4f064db 100644
--- a/src/windows/wslc/arguments/ArgumentDefinitions.h
+++ b/src/windows/wslc/arguments/ArgumentDefinitions.h
@@ -41,10 +41,10 @@ _(Command, "command", NO_ALIAS, Kind::Positional, L
_(ContainerId, "container-id", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_ContainerIdArgDescription()) \
_(Force, "force", L"f", Kind::Flag, Localization::WSLCCLI_ForceArgDescription()) \
_(Detach, "detach", L"d", Kind::Flag, Localization::WSLCCLI_DetachArgDescription()) \
-/*_(DNS, "dns", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSArgDescription())*/ \
-/*_(DNSDomain, "dns-domain", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSDomainArgDescription())*/ \
-/*_(DNSOption, "dns-option", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSOptionArgDescription())*/ \
-/*_(DNSSearch, "dns-search", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSSearchArgDescription())*/ \
+_(DNS, "dns", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSArgDescription()) \
+_(DNSDomain, "dns-domain", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSDomainArgDescription()) \
+_(DNSOption, "dns-option", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSOptionArgDescription()) \
+_(DNSSearch, "dns-search", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSSearchArgDescription()) \
_(Entrypoint, "entrypoint", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EntrypointArgDescription()) \
_(Env, "env", L"e", Kind::Value, Localization::WSLCCLI_EnvArgDescription()) \
_(EnvFile, "env-file", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EnvFileArgDescription()) \
@@ -59,7 +59,7 @@ _(ImageId, "image", NO_ALIAS, Kind::Positional, L
_(Input, "input", L"i", Kind::Value, Localization::WSLCCLI_InputArgDescription()) \
_(Interactive, "interactive", L"i", Kind::Flag, Localization::WSLCCLI_InteractiveArgDescription()) \
_(Name, "name", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NameArgDescription()) \
-/*_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription())*/ \
+_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription()) \
_(NoPrune, "no-prune", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoPruneArgDescription()) \
_(NoTrunc, "no-trunc", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoTruncArgDescription()) \
_(Output, "output", L"o", Kind::Value, Localization::WSLCCLI_OutputArgDescription()) \
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index ea47a5f55..7e5303a28 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -47,6 +47,16 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateVolumeMount(execArgs.GetAll());
break;
+ case ArgType::NoDNS:
+ {
+ if (execArgs.Contains(ArgType::DNS) || execArgs.Contains(ArgType::DNSDomain) ||
+ execArgs.Contains(ArgType::DNSOption) || execArgs.Contains(ArgType::DNSSearch))
+ {
+ throw ArgumentException(Localization::WSLCCLI_NoDNSConflictError());
+ }
+ break;
+ }
+
case ArgType::WorkDir:
{
const auto& value = execArgs.Get();
diff --git a/src/windows/wslc/commands/ContainerCreateCommand.cpp b/src/windows/wslc/commands/ContainerCreateCommand.cpp
index 4150d0af5..d1d7c574b 100644
--- a/src/windows/wslc/commands/ContainerCreateCommand.cpp
+++ b/src/windows/wslc/commands/ContainerCreateCommand.cpp
@@ -32,17 +32,17 @@ std::vector ContainerCreateCommand::GetArguments() const
Argument::Create(ArgType::Command),
Argument::Create(ArgType::ForwardArgs),
// Argument::Create(ArgType::CIDFile),
- // Argument::Create(ArgType::DNS),
- // Argument::Create(ArgType::DNSDomain),
- // Argument::Create(ArgType::DNSOption),
- // Argument::Create(ArgType::DNSSearch),
+ Argument::Create(ArgType::DNS, false, NO_LIMIT),
+ Argument::Create(ArgType::DNSDomain),
+ Argument::Create(ArgType::DNSOption, false, NO_LIMIT),
+ Argument::Create(ArgType::DNSSearch, false, NO_LIMIT),
Argument::Create(ArgType::Entrypoint),
Argument::Create(ArgType::Env, false, NO_LIMIT),
Argument::Create(ArgType::EnvFile, false, NO_LIMIT),
// Argument::Create(ArgType::GroupId),
Argument::Create(ArgType::Interactive),
Argument::Create(ArgType::Name),
- // Argument::Create(ArgType::NoDNS),
+ Argument::Create(ArgType::NoDNS),
// Argument::Create(ArgType::Progress),
Argument::Create(ArgType::Publish, false, NO_LIMIT),
Argument::Create(ArgType::Remove),
diff --git a/src/windows/wslc/commands/ContainerRunCommand.cpp b/src/windows/wslc/commands/ContainerRunCommand.cpp
index e2810cd5f..0c1a7c926 100644
--- a/src/windows/wslc/commands/ContainerRunCommand.cpp
+++ b/src/windows/wslc/commands/ContainerRunCommand.cpp
@@ -33,16 +33,16 @@ std::vector ContainerRunCommand::GetArguments() const
Argument::Create(ArgType::ForwardArgs),
// Argument::Create(ArgType::CIDFile),
Argument::Create(ArgType::Detach),
- // Argument::Create(ArgType::DNS),
- // Argument::Create(ArgType::DNSDomain),
- // Argument::Create(ArgType::DNSOption),
- // Argument::Create(ArgType::DNSSearch),
+ Argument::Create(ArgType::DNS, false, NO_LIMIT),
+ Argument::Create(ArgType::DNSDomain),
+ Argument::Create(ArgType::DNSOption, false, NO_LIMIT),
+ Argument::Create(ArgType::DNSSearch, false, NO_LIMIT),
Argument::Create(ArgType::Entrypoint),
Argument::Create(ArgType::Env, false, NO_LIMIT),
Argument::Create(ArgType::EnvFile, false, NO_LIMIT),
Argument::Create(ArgType::Interactive),
Argument::Create(ArgType::Name),
- // Argument::Create(ArgType::NoDNS),
+ Argument::Create(ArgType::NoDNS),
// Argument::Create(ArgType::Progress),
Argument::Create(ArgType::Publish, false, NO_LIMIT),
// Argument::Create(ArgType::Pull),
diff --git a/src/windows/wslc/services/ContainerModel.h b/src/windows/wslc/services/ContainerModel.h
index c4da81fab..9b459abe5 100644
--- a/src/windows/wslc/services/ContainerModel.h
+++ b/src/windows/wslc/services/ContainerModel.h
@@ -42,6 +42,11 @@ struct ContainerOptions
std::vector Entrypoint;
std::optional User{};
std::vector Tmpfs;
+ std::vector DnsServers;
+ std::string DnsDomain;
+ std::vector DnsOptions;
+ std::vector DnsSearchDomains;
+ bool NoDns = false;
};
struct CreateContainerResult
diff --git a/src/windows/wslc/services/ContainerService.cpp b/src/windows/wslc/services/ContainerService.cpp
index 05a5cc295..59de12430 100644
--- a/src/windows/wslc/services/ContainerService.cpp
+++ b/src/windows/wslc/services/ContainerService.cpp
@@ -107,6 +107,30 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(Session& sessio
containerLauncher.AddTmpfs(tmpfsMount.ContainerPath(), tmpfsMount.Options());
}
+ if (!options.DnsServers.empty())
+ {
+ auto dnsServers = options.DnsServers;
+ containerLauncher.SetDnsServers(std::move(dnsServers));
+ }
+
+ if (!options.DnsDomain.empty())
+ {
+ auto domainname = options.DnsDomain;
+ containerLauncher.SetDomainname(std::move(domainname));
+ }
+
+ if (!options.DnsOptions.empty())
+ {
+ auto dnsOptions = options.DnsOptions;
+ containerLauncher.SetDnsOptions(std::move(dnsOptions));
+ }
+
+ if (!options.DnsSearchDomains.empty())
+ {
+ auto dnsSearchDomains = options.DnsSearchDomains;
+ containerLauncher.SetDnsSearchDomains(std::move(dnsSearchDomains));
+ }
+
auto [result, runningContainer] = containerLauncher.CreateNoThrow(*session.Get());
if (result == WSLC_E_IMAGE_NOT_FOUND)
{
diff --git a/src/windows/wslc/tasks/ContainerTasks.cpp b/src/windows/wslc/tasks/ContainerTasks.cpp
index 55e19e478..72a20a1cc 100644
--- a/src/windows/wslc/tasks/ContainerTasks.cpp
+++ b/src/windows/wslc/tasks/ContainerTasks.cpp
@@ -293,6 +293,46 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
}
}
+ if (context.Args.Contains(ArgType::DNS))
+ {
+ auto dnsServers = context.Args.GetAll();
+ options.DnsServers.reserve(options.DnsServers.size() + dnsServers.size());
+ for (const auto& value : dnsServers)
+ {
+ options.DnsServers.emplace_back(WideToMultiByte(value));
+ }
+ }
+
+ if (context.Args.Contains(ArgType::DNSDomain))
+ {
+ options.DnsDomain = WideToMultiByte(context.Args.Get());
+ }
+
+ if (context.Args.Contains(ArgType::DNSOption))
+ {
+ auto dnsOptions = context.Args.GetAll();
+ options.DnsOptions.reserve(options.DnsOptions.size() + dnsOptions.size());
+ for (const auto& value : dnsOptions)
+ {
+ options.DnsOptions.emplace_back(WideToMultiByte(value));
+ }
+ }
+
+ if (context.Args.Contains(ArgType::DNSSearch))
+ {
+ auto dnsSearchDomains = context.Args.GetAll();
+ options.DnsSearchDomains.reserve(options.DnsSearchDomains.size() + dnsSearchDomains.size());
+ for (const auto& value : dnsSearchDomains)
+ {
+ options.DnsSearchDomains.emplace_back(WideToMultiByte(value));
+ }
+ }
+
+ if (context.Args.Contains(ArgType::NoDNS))
+ {
+ options.NoDns = true;
+ }
+
if (context.Args.Contains(ArgType::ForwardArgs))
{
auto const& forwardArgs = context.Args.Get();
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index cda1f24b1..4bad19f6c 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -1081,6 +1081,82 @@ class WSLCE2EContainerCreateTests
result.Verify({.Stderr = L"invalid mount path: '' mount path must be absolute\r\nError code: E_FAIL\r\n", .ExitCode = 1});
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_DNS_SingleServer)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container create --name {} --dns 8.8.8.8 {} cat /etc/resolv.conf", WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 8.8.8.8") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_DNS_MultipleServers)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container create --name {} --dns 8.8.8.8 --dns 8.8.4.4 {} cat /etc/resolv.conf",
+ WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 8.8.8.8") != std::wstring::npos);
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 8.8.4.4") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_DNS_Search)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container create --name {} --dns-search example.com {} cat /etc/resolv.conf",
+ WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"search example.com") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_DNS_Option)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container create --name {} --dns-option ndots:5 {} cat /etc/resolv.conf",
+ WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"options ndots:5") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_DNS_AllOptions)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container create --name {} --dns 1.1.1.1 --dns-search test.local --dns-option ndots:3 {} cat /etc/resolv.conf",
+ WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 1.1.1.1") != std::wstring::npos);
+ VERIFY_IS_TRUE(result.Stdout->find(L"search test.local") != std::wstring::npos);
+ VERIFY_IS_TRUE(result.Stdout->find(L"options ndots:3") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_NoDNS_ConflictWithDNS_Fails)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container create --name {} --no-dns --dns 8.8.8.8 {} echo test",
+ WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"Cannot use --no-dns with --dns, --dns-domain, --dns-option, or --dns-search\r\n", .ExitCode = 1});
+ }
+
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
@@ -1146,11 +1222,16 @@ class WSLCE2EContainerCreateTests
{
std::wstringstream options;
options << L"The following options are available:\r\n" //
+ << L" --dns IP address of the DNS nameserver in resolv.conf\r\n"
+ << L" --dns-domain Set the default DNS Domain\r\n"
+ << L" --dns-option Set DNS options\r\n"
+ << L" --dns-search Set DNS search domains\r\n"
<< L" --entrypoint Specifies the container init process executable\r\n"
<< L" -e,--env Key=Value pairs for environment variables\r\n"
<< L" --env-file File containing key=value pairs of env variables\r\n"
<< L" -i,--interactive Attach to stdin and keep it open\r\n"
<< L" --name Name of the container\r\n"
+ << L" --no-dns No configuration of DNS in the container\r\n"
<< L" -p,--publish Publish a port from a container to host\r\n"
<< L" --rm Remove the container after it stops\r\n"
<< L" --session Specify the session to use\r\n"
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
index 85f166890..f2201fda1 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
@@ -170,6 +170,63 @@ class WSLCE2EContainerRunTests
result.Verify({.Stderr = L"invalid mount path: '' mount path must be absolute\r\nError code: E_FAIL\r\n", .ExitCode = 1});
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_DNS_SingleServer)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container run --rm --dns 8.8.8.8 {} cat /etc/resolv.conf", DebianImage.NameAndTag()));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 8.8.8.8") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_DNS_MultipleServers)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container run --rm --dns 8.8.8.8 --dns 8.8.4.4 {} cat /etc/resolv.conf", DebianImage.NameAndTag()));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 8.8.8.8") != std::wstring::npos);
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 8.8.4.4") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_DNS_Search)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container run --rm --dns-search example.com {} cat /etc/resolv.conf", DebianImage.NameAndTag()));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"search example.com") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_DNS_Option)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container run --rm --dns-option ndots:5 {} cat /etc/resolv.conf", DebianImage.NameAndTag()));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"options ndots:5") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_DNS_AllOptions)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container run --rm --dns 1.1.1.1 --dns-search test.local --dns-option ndots:3 {} cat /etc/resolv.conf",
+ DebianImage.NameAndTag()));
+ result.Verify({.ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"nameserver 1.1.1.1") != std::wstring::npos);
+ VERIFY_IS_TRUE(result.Stdout->find(L"search test.local") != std::wstring::npos);
+ VERIFY_IS_TRUE(result.Stdout->find(L"options ndots:3") != std::wstring::npos);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_NoDNS_ConflictWithDNS_Fails)
+ {
+
+ auto result = RunWslc(std::format(
+ L"container run --rm --no-dns --dns 8.8.8.8 {} echo test", DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"Cannot use --no-dns with --dns, --dns-domain, --dns-option, or --dns-search\r\n", .ExitCode = 1});
+ }
+
private:
const std::wstring WslcContainerName = L"wslc-test-container";
const TestImage& DebianImage = DebianTestImage();
@@ -212,11 +269,16 @@ class WSLCE2EContainerRunTests
std::wstringstream options;
options << L"The following options are available:\r\n"
<< L" -d,--detach Run container in detached mode\r\n"
+ << L" --dns IP address of the DNS nameserver in resolv.conf\r\n"
+ << L" --dns-domain Set the default DNS Domain\r\n"
+ << L" --dns-option Set DNS options\r\n"
+ << L" --dns-search Set DNS search domains\r\n"
<< L" --entrypoint Specifies the container init process executable\r\n"
<< L" -e,--env Key=Value pairs for environment variables\r\n"
<< L" --env-file File containing key=value pairs of env variables\r\n"
<< L" -i,--interactive Attach to stdin and keep it open\r\n"
<< L" --name Name of the container\r\n"
+ << L" --no-dns No configuration of DNS in the container\r\n"
<< L" -p,--publish Publish a port from a container to host\r\n"
<< L" --rm Remove the container after it stops\r\n"
<< L" --session Specify the session to use\r\n"