Skip to content
Open
15 changes: 13 additions & 2 deletions src/windows/WslcSDK/wslcsdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ template <>
struct FlagsTraits<WslcContainerFlags>
{
using WslcType = WSLCContainerFlags;
constexpr static WslcContainerFlags Mask = WSLC_CONTAINER_FLAG_AUTO_REMOVE | WSLC_CONTAINER_FLAG_ENABLE_GPU;
constexpr static WslcContainerFlags Mask = WSLC_CONTAINER_FLAG_AUTO_REMOVE | WSLC_CONTAINER_FLAG_ENABLE_GPU | WSLC_CONTAINER_FLAG_PRIVILEGED;
WSLC_FLAG_VALUE_ASSERT(WSLC_CONTAINER_FLAG_AUTO_REMOVE, WSLCContainerFlagsRm);
WSLC_FLAG_VALUE_ASSERT(WSLC_CONTAINER_FLAG_ENABLE_GPU, WSLCContainerFlagsGpu);
// TODO: WSLC_CONTAINER_FLAG_PRIVILEGED has no associated runtime value
};

template <>
Expand All @@ -80,6 +79,18 @@ typename FlagsTraits<Flags>::WslcType ConvertFlags(Flags flags)
return static_cast<typename traits::WslcType>(flags & traits::Mask);
}

template <>
WSLCContainerFlags ConvertFlags(WslcContainerFlags flags)
{
constexpr auto s_publicMask = FlagsTraits<WslcContainerFlags>::Mask & ~WSLC_CONTAINER_FLAG_PRIVILEGED;
auto result = static_cast<WSLCContainerFlags>(flags & s_publicMask);
if (WI_IsFlagSet(flags, WSLC_CONTAINER_FLAG_PRIVILEGED))
{
WI_SetFlag(result, WSLCContainerFlagsPrivileged);
}
return result;
}

WSLCSignal Convert(WslcSignal signal)
{
switch (signal)
Expand Down
7 changes: 7 additions & 0 deletions src/windows/common/WSLCContainerLauncher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ void WSLCContainerLauncher::AddUlimit(const std::string& Name, std::int64_t Soft
m_ulimits.push_back(ulimit);
}

void WSLCContainerLauncher::SetPrivileged(bool privileged)
{
m_privileged = privileged;
}


void wsl::windows::common::WSLCContainerLauncher::AddVolume(const std::wstring& HostPath, const std::string& ContainerPath, bool ReadOnly)
{
// Store a copy of the path strings to the launcher to ensure the pointers in WSLCVolume remain valid.
Expand Down Expand Up @@ -331,6 +337,7 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
options.PortsCount = static_cast<ULONG>(m_ports.size());
options.StopSignal = m_stopSignal;
options.Flags = m_containerFlags;
if (m_privileged) { WI_SetFlag(options.Flags, WSLCContainerFlagsPrivileged); }
if (m_stopTimeout.has_value())
{
Comment on lines 338 to 342
options.StopTimeout = m_stopTimeout.value();
Expand Down
2 changes: 2 additions & 0 deletions src/windows/common/WSLCContainerLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
void SetDnsOptions(std::vector<std::string>&& DnsOptions);
void SetMemoryLimit(std::int64_t Bytes);
void SetNanoCpus(std::int64_t NanoCpus);
void SetPrivileged(bool privileged);
void AddUlimit(const std::string& Name, std::int64_t Soft, std::int64_t Hard);

using WSLCProcessLauncher::FormatResult;
Expand Down Expand Up @@ -134,6 +135,7 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
std::int64_t m_memoryBytes = 0;
std::int64_t m_nanoCpus = 0;
std::vector<WSLCUlimit> m_ulimits;
bool m_privileged = false;
std::deque<std::string> m_ulimitNames;
};
} // namespace wsl::windows::common
26 changes: 14 additions & 12 deletions src/windows/inc/docker_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,17 @@ struct EmptyRequest
using TResponse = void;
};

struct AuthResponse
{
std::string Status;
std::optional<std::string> IdentityToken;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AuthResponse, Status, IdentityToken);
};

struct AuthRequest
{
using TResponse = struct AuthResponse;
using TResponse = AuthResponse;

std::string username;
std::string password;
Expand All @@ -62,14 +70,6 @@ struct AuthRequest
NLOHMANN_DEFINE_TYPE_INTRUSIVE(AuthRequest, username, password, serveraddress);
};

struct AuthResponse
{
std::string Status;
std::optional<std::string> IdentityToken;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AuthResponse, Status, IdentityToken);
};

struct VolumeUsageData
{
int64_t Size{-1};
Expand Down Expand Up @@ -294,19 +294,21 @@ struct HostConfig
std::optional<std::vector<std::string>> DnsOptions;
std::optional<std::vector<std::string>> Binds;
std::map<std::string, std::string> Tmpfs;
// Docker wire type is int64. 0 means "use daemon default" same as omitting
// the field so we don't bother with std::optional here.
// Docker wire type is int64. 0 means "use daemon default" - same as omitting
// the field - so we don't bother with std::optional here.
std::int64_t ShmSize{};
std::optional<std::vector<DeviceMapping>> Devices;
std::optional<std::vector<DeviceRequest>> DeviceRequests;
bool Privileged{};
std::vector<std::string> SecurityOpt;

// Per-container resource limits. 0 means "no limit" (Docker default).
std::int64_t Memory{};
std::int64_t NanoCpus{};
std::optional<std::vector<Ulimit>> Ulimits;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
HostConfig, Mounts, PortBindings, NetworkMode, Init, Dns, DnsSearch, DnsOptions, Binds, Tmpfs, Devices, DeviceRequests, ShmSize, Memory, NanoCpus, Ulimits);
HostConfig, Mounts, PortBindings, NetworkMode, Init, Privileged, SecurityOpt, Dns, DnsSearch, DnsOptions, Binds, Tmpfs, Devices, DeviceRequests, ShmSize, Memory, NanoCpus, Ulimits);
};

struct InspectEndpointIPAMConfig
Expand Down
3 changes: 2 additions & 1 deletion src/windows/service/inc/WSLCShared.idl
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,10 @@ typedef enum _WSLCContainerFlags
WSLCContainerFlagsStopTimeout = 16, // The StopTimeout field is set and should be honored (otherwise StopTimeout is ignored).
WSLCContainerFlagsHealthCheck = 32, // The Health* fields are set and should be honored (otherwise they are ignored).
WSLCContainerFlagsNoHealthCheck = 64, // Disable any container/image-specified health check.
WSLCContainerFlagsPrivileged = 128, // Run container in privileged mode, with full host access.
} WSLCContainerFlags;

cpp_quote("#define WSLCContainerFlagsValid (WSLCContainerFlagsRm | WSLCContainerFlagsGpu | WSLCContainerFlagsInit | WSLCContainerFlagsPublishAll | WSLCContainerFlagsStopTimeout | WSLCContainerFlagsHealthCheck | WSLCContainerFlagsNoHealthCheck)")
cpp_quote("#define WSLCContainerFlagsValid (WSLCContainerFlagsRm | WSLCContainerFlagsGpu | WSLCContainerFlagsInit | WSLCContainerFlagsPublishAll | WSLCContainerFlagsStopTimeout | WSLCContainerFlagsHealthCheck | WSLCContainerFlagsNoHealthCheck | WSLCContainerFlagsPrivileged)")

cpp_quote("DEFINE_ENUM_FLAG_OPERATORS(WSLCContainerFlags);")

Expand Down
1 change: 1 addition & 0 deletions src/windows/wslc/arguments/ArgumentDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ _(Path, "path", NO_ALIAS, Kind::Positional, L
/*_(Progress, "progress", NO_ALIAS, Kind::Value, Localization::WSLCCLI_ProgressArgDescription())*/ \
_(Publish, "publish", L"p", Kind::Value, Localization::WSLCCLI_PublishArgDescription()) \
_(PublishAll, "publish-all", L"P", Kind::Flag, Localization::WSLCCLI_PublishAllArgDescription()) \
_(Privileged, "privileged", NO_ALIAS, Kind::Flag, L"Run container in privileged mode") \
/*_(Pull, "pull", NO_ALIAS, Kind::Value, Localization::WSLCCLI_PullArgDescription())*/ \
_(Quiet, "quiet", L"q", Kind::Flag, Localization::WSLCCLI_QuietArgDescription()) \
_(Remove, "rm", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_RemoveArgDescription()) \
Expand Down
1 change: 1 addition & 0 deletions src/windows/wslc/commands/ContainerCreateCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ std::vector<Argument> ContainerCreateCommand::GetArguments() const
Argument::Create(ArgType::Publish, false, Limit::Unlimited),
Argument::Create(ArgType::PublishAll),
Argument::Create(ArgType::Remove),
Argument::Create(ArgType::Privileged),
// Argument::Create(ArgType::Scheme),
Argument::Create(ArgType::ShmSize),
Argument::Create(ArgType::StopSignal),
Expand Down
1 change: 1 addition & 0 deletions src/windows/wslc/commands/ContainerRunCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ std::vector<Argument> ContainerRunCommand::GetArguments() const
Argument::Create(ArgType::PublishAll),
// Argument::Create(ArgType::Pull),
Argument::Create(ArgType::Remove),
Argument::Create(ArgType::Privileged),
// Argument::Create(ArgType::Scheme),
Argument::Create(ArgType::ShmSize),
Argument::Create(ArgType::StopSignal),
Expand Down
1 change: 1 addition & 0 deletions src/windows/wslc/services/ContainerModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct ContainerOptions
std::optional<int64_t> MemoryBytes{};
std::optional<int64_t> NanoCpus{};
std::vector<std::tuple<std::string, int64_t, int64_t>> Ulimits;
bool Privileged = false;
};

struct CreateContainerResult
Expand Down
6 changes: 6 additions & 0 deletions src/windows/wslc/services/ContainerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(Reporter& repor
containerLauncher.AddTmpfs(tmpfsMount.ContainerPath(), tmpfsMount.Options());
}


if (options.Privileged)
{
containerLauncher.SetPrivileged(true);
}

for (const auto& [key, value] : options.Labels)
{
containerLauncher.AddLabel(key, value);
Expand Down
6 changes: 6 additions & 0 deletions src/windows/wslc/tasks/ContainerTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,12 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
options.WorkingDirectory = WideToMultiByte(context.Args.Get<ArgType::WorkDir>());
}


if (context.Args.Contains(ArgType::Privileged))
{
options.Privileged = true;
}
Comment on lines +909 to +912

context.Data.Add<Data::ContainerOptions>(std::move(options));
}

Expand Down
23 changes: 22 additions & 1 deletion src/windows/wslcsession/WSLCContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,27 @@ std::shared_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
request.HostConfig.DeviceRequests = std::vector<common::docker_schema::DeviceRequest>{{"cdi", {LX_WSLC_GPU_CDI_DEVICE}}};
}

// Configure privileged mode if requested.
if (WI_IsFlagSet(containerOptions.Flags, WSLCContainerFlagsPrivileged))
{
request.HostConfig.Privileged = true;
request.HostConfig.SecurityOpt.push_back("seccomp=unconfined");

// Grant FUSE device access in privileged containers.
// Without this, /dev/fuse returns EPERM even with full capabilities
// because the device cgroup blocks access by default.
common::docker_schema::DeviceMapping fuseDevice;
fuseDevice.PathOnHost = "/dev/fuse";
fuseDevice.PathInContainer = "/dev/fuse";
fuseDevice.CgroupPermissions = "rwm";
if (!request.HostConfig.Devices.has_value())
{
request.HostConfig.Devices = std::vector<common::docker_schema::DeviceMapping>{};
}
request.HostConfig.Devices->push_back(std::move(fuseDevice));
}


// Prepare port mappings from container options.
std::vector<_WSLCPortMapping> ports;
for (ULONG i = 0; i < containerOptions.PortsCount; i++)
Expand Down Expand Up @@ -2743,7 +2764,7 @@ void WSLCContainerImpl::GetLabels(WSLCLabelInformation** Labels, ULONG* Count) c
wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(value.c_str()));
}

// All strings built successfully allocate output array and transfer ownership.
// All strings built successfully - allocate output array and transfer ownership.
auto labelsArray = wil::make_unique_cotaskmem<WSLCLabelInformation[]>(localLabels.size());
for (size_t i = 0; i < localLabels.size(); ++i)
{
Expand Down
30 changes: 30 additions & 0 deletions test/windows/wslc/WSLCCLIExecutionUnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,36 @@ class WSLCCLIExecutionUnitTests
VERIFY_IS_TRUE(options.Gpu);
}

TEST_METHOD(RunCommand_ParsePrivileged_SetsPrivilegedOption)
{
auto invocation = CreateInvocationFromCommandLine(L"wslc --privileged ubuntu sh");

ContainerRunCommand command{L""};
CLIExecutionContext context;
command.ParseArguments(invocation, context.Args);
command.ValidateArguments(context.Args);

wsl::windows::wslc::task::SetContainerOptionsFromArgs(context);

const auto& options = context.Data.Get<Data::ContainerOptions>();
VERIFY_IS_TRUE(options.Privileged);
}

TEST_METHOD(CreateCommand_ParsePrivileged_SetsPrivilegedOption)
{
auto invocation = CreateInvocationFromCommandLine(L"wslc --privileged ubuntu sh");

ContainerCreateCommand command{L""};
CLIExecutionContext context;
command.ParseArguments(invocation, context.Args);
command.ValidateArguments(context.Args);

wsl::windows::wslc::task::SetContainerOptionsFromArgs(context);

const auto& options = context.Data.Get<Data::ContainerOptions>();
VERIFY_IS_TRUE(options.Privileged);
}

TEST_METHOD(RunCommand_ParseGpusInvalid_ThrowsArgumentException)
{
auto invocation = CreateInvocationFromCommandLine(L"wslc --gpus invalid ubuntu sh");
Expand Down