Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7d41e66
Add local registry, auth, and push image
kvega005 Apr 6, 2026
638ebfa
Fix test
kvega005 Apr 6, 2026
7fd4026
delete cleanup registry storage
kvega005 Apr 6, 2026
1fdb1b2
Do not use volume
kvega005 Apr 6, 2026
15d30b6
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/…
kvega005 Apr 6, 2026
35e6322
Undo entry point fix (getting checked in seperately)
kvega005 Apr 6, 2026
b278c0a
undo entry point fix
kvega005 Apr 6, 2026
b988783
Fix formatting
kvega005 Apr 6, 2026
783ae7b
Address copilot comment
kvega005 Apr 6, 2026
ead90fc
Use packaged wslc-registry
kvega005 Apr 6, 2026
ce78d51
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 7, 2026
5156db5
Address copilot comments
kvega005 Apr 7, 2026
31b5ad5
Address more copilot comments
kvega005 Apr 7, 2026
4cfd98c
Merge branch 'user/kevinve/registry' of https://github.com/kvega005/W…
kvega005 Apr 7, 2026
6305c58
Fix push and Pulll tests
kvega005 Apr 7, 2026
22cb648
Fix formatting
kvega005 Apr 7, 2026
3a0ba44
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 7, 2026
83b39f2
Address suggestion
kvega005 Apr 7, 2026
a17819e
Address suggestions
kvega005 Apr 7, 2026
ba6c3de
Merge branch 'user/kevinve/registry' of https://github.com/kvega005/W…
kvega005 Apr 7, 2026
e329ac7
address suggestions
kvega005 Apr 7, 2026
868f69e
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 7, 2026
3256f5d
Address copilot comments
kvega005 Apr 7, 2026
2d4dce9
Merge branch 'user/kevinve/registry' of https://github.com/kvega005/W…
kvega005 Apr 7, 2026
9177765
Remove wslc local registry
kvega005 Apr 7, 2026
0fa5505
formatting fix
kvega005 Apr 7, 2026
7b41634
Add script and docker file to generate test images.
kvega005 Apr 7, 2026
aaed20e
Address base64 encode feedback
kvega005 Apr 7, 2026
65aed10
Add Auth to SDK
kvega005 Apr 7, 2026
adab0ce
remove unneeded code
kvega005 Apr 7, 2026
3d0bad5
Address custom headers suggestion
kvega005 Apr 7, 2026
10491ee
Make regitsry auth required
kvega005 Apr 7, 2026
404a05e
Nit update script
kvega005 Apr 7, 2026
965f832
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 7, 2026
2f35e77
Fix formatting
kvega005 Apr 8, 2026
71081bb
Update comment
kvega005 Apr 8, 2026
1734d51
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 8, 2026
a40b27d
Fix tests
kvega005 Apr 8, 2026
bc989e5
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/…
kvega005 Apr 8, 2026
7f59f8f
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 8, 2026
d1455e0
Merge branch 'user/kevinve/registry' of https://github.com/kvega005/W…
kvega005 Apr 8, 2026
ef6f192
Fix tests
kvega005 Apr 8, 2026
d14cfec
Added back removed test + cleanup
kvega005 Apr 9, 2026
f4c1f39
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/…
kvega005 Apr 9, 2026
c945517
Fix empty auth
kvega005 Apr 9, 2026
498588b
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 9, 2026
baabcd9
Fix clang-format violations in test files
Apr 9, 2026
f6ea418
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/…
kvega005 Apr 10, 2026
61dd8b4
Merge branch 'feature/wsl-for-apps' into user/kevinve/registry
kvega005 Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/windows/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ set(SOURCES
SubProcess.cpp
svccomm.cpp
WSLCContainerLauncher.cpp
WSLCLocalRegistry.cpp
WslcCredentialStore.cpp
VirtioNetworking.cpp
WSLCProcessLauncher.cpp
WslClient.cpp
Expand Down Expand Up @@ -116,6 +118,8 @@ set(HEADERS
SubProcess.h
svccomm.hpp
WSLCContainerLauncher.h
WSLCLocalRegistry.h
WslcCredentialStore.h
VirtioNetworking.h
WSLCProcessLauncher.h
WslClient.h
Expand All @@ -134,6 +138,7 @@ set(HEADERS

add_library(common STATIC ${SOURCES} ${HEADERS})
add_dependencies(common wslserviceidl wslcidl localization wslservicemc wslinstalleridl)
target_link_libraries(common PRIVATE Crypt32.lib)
Comment thread
kvega005 marked this conversation as resolved.
Outdated

target_precompile_headers(common PRIVATE precomp.h)
set_target_properties(common PROPERTIES FOLDER windows)
Expand Down
1 change: 1 addition & 0 deletions src/windows/common/WSLCContainerLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
void SetDnsSearchDomains(std::vector<std::string>&& DnsSearchDomains);
void SetDnsOptions(std::vector<std::string>&& DnsOptions);

using WSLCProcessLauncher::FormatResult;
using WSLCProcessLauncher::SetUser;
using WSLCProcessLauncher::SetWorkingDirectory;

Expand Down
121 changes: 121 additions & 0 deletions src/windows/common/WSLCLocalRegistry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*++

Comment thread
kvega005 marked this conversation as resolved.
Outdated
Copyright (c) Microsoft. All rights reserved.

Module Name:

WSLCLocalRegistry.cpp

Abstract:

Implementation of WSLCLocalRegistry.

--*/
Comment thread
kvega005 marked this conversation as resolved.
Outdated
#include "WSLCLocalRegistry.h"

using wsl::windows::common::RunningWSLCContainer;
using wsl::windows::common::WSLCContainerLauncher;
using wsl::windows::common::WSLCLocalRegistry;

namespace {

constexpr auto c_registryImage = "registry:3";
constexpr auto c_htpasswdImage = "httpd:2";

std::string GenerateHtpasswd(IWSLCSession& session, const std::string& username, const std::string& password)
{
THROW_IF_FAILED(session.PullImage(c_htpasswdImage, nullptr, nullptr));

Comment thread
kvega005 marked this conversation as resolved.
Outdated
const auto command = std::format("htpasswd -Bbn '{}' '{}'", username, password);

WSLCContainerLauncher launcher(c_htpasswdImage, {}, {"/bin/sh", "-c", command});
launcher.SetContainerFlags(WSLCContainerFlagsRm);

auto container = launcher.Launch(session);
auto result = container.GetInitProcess().WaitAndCaptureOutput();

THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "%hs", launcher.FormatResult(result).c_str());

auto output = result.Output[1];
output.erase(output.find_last_not_of("\n\r") + 1);

THROW_HR_IF_MSG(E_FAIL, output.empty(), "%hs", launcher.FormatResult(result).c_str());
return output;
}

std::vector<std::string> BuildRegistryEnv(IWSLCSession& session, const std::string& username, const std::string& password)
{
std::vector<std::string> env = {
"REGISTRY_HTTP_ADDR=0.0.0.0:5000",
};

if (!username.empty())
{
auto htpasswdEntry = GenerateHtpasswd(session, username, password);

env.push_back(std::format("HTPASSWD_CONTENT={}", htpasswdEntry));
env.push_back("REGISTRY_AUTH=htpasswd");
env.push_back("REGISTRY_AUTH_HTPASSWD_REALM=WSLC Test Registry");
env.push_back("REGISTRY_AUTH_HTPASSWD_PATH=/htpasswd");
}

return env;
}

} // namespace

WSLCLocalRegistry::WSLCLocalRegistry(
IWSLCSession& session,
RunningWSLCContainer&& container,
std::string&& username,
std::string&& password) :
m_session(wil::com_ptr<IWSLCSession>(&session)),
m_username(std::move(username)),
m_password(std::move(password)),
m_container(std::move(container))
{
}

WSLCLocalRegistry::~WSLCLocalRegistry()
{
// Delete the container first while the session is still active.
m_container.Reset();
Comment thread
kvega005 marked this conversation as resolved.
Outdated
}

WSLCLocalRegistry WSLCLocalRegistry::Start(
IWSLCSession& session, const std::string& username, const std::string& password)
{
THROW_IF_FAILED(session.PullImage(c_registryImage, nullptr, nullptr));

auto env = BuildRegistryEnv(session, username, password);

WSLCContainerLauncher launcher(c_registryImage, {}, {}, env);
launcher.AddPort(5000, 5000, AF_INET);

if (!username.empty())
{
launcher.SetEntrypoint({"/bin/sh", "-c", "echo \"$HTPASSWD_CONTENT\" > /htpasswd && registry serve /etc/distribution/config.yml"});
}
auto container = launcher.Launch(session, WSLCContainerStartFlagsNone);

return WSLCLocalRegistry(
session,
std::move(container),
std::string(username),
std::string(password));
}

const char* WSLCLocalRegistry::GetServerAddress() const
{
return "127.0.0.1:5000";
Comment thread
kvega005 marked this conversation as resolved.
Outdated
}

const std::string& WSLCLocalRegistry::GetUsername() const
{
return m_username;
}

const std::string& WSLCLocalRegistry::GetPassword() const
{
return m_password;
}
52 changes: 52 additions & 0 deletions src/windows/common/WSLCLocalRegistry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WSLCLocalRegistry.h
Abstract:
Helper class that starts a local Docker registry:3 container inside a WSLC
session, optionally configured with htpasswd basic authentication. Intended
for use in both unit tests and E2E tests that need a private registry without
an external dependency.
--*/

#pragma once
#include "WSLCContainerLauncher.h"

namespace wsl::windows::common {

class WSLCLocalRegistry
{
public:
NON_COPYABLE(WSLCLocalRegistry);
DEFAULT_MOVABLE(WSLCLocalRegistry);
~WSLCLocalRegistry();

static WSLCLocalRegistry Start(
IWSLCSession& Session,
const std::string& Username = {},
const std::string& Password = {});

const char* GetServerAddress() const;
const std::string& GetUsername() const;
const std::string& GetPassword() const;

private:
WSLCLocalRegistry(
IWSLCSession& session,
RunningWSLCContainer&& container,
std::string&& username,
std::string&& password);

wil::com_ptr<IWSLCSession> m_session;
std::string m_username;
std::string m_password;
RunningWSLCContainer m_container;
};

} // namespace wsl::windows::common
69 changes: 69 additions & 0 deletions src/windows/common/WslcCredentialStore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
WslcCredentialStore.cpp
Abstract:
Implementation of credential store helpers.
--*/

#include "WslcCredentialStore.h"
#include <format>
#include <wincrypt.h>

std::string wsl::windows::common::BuildRegistryAuthHeader(
const std::string& username, const std::string& password, const std::string& serverAddress)
{
auto authJson = std::format(
R"({{"username":"{}","password":"{}","serveraddress":"{}"}})", username, password, serverAddress);

DWORD base64Size = 0;
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringA(
reinterpret_cast<const BYTE*>(authJson.c_str()),
static_cast<DWORD>(authJson.size()),
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
nullptr,
&base64Size));

std::string result(base64Size, '\0');
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringA(
reinterpret_cast<const BYTE*>(authJson.c_str()),
static_cast<DWORD>(authJson.size()),
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
result.data(),
&base64Size));

result.resize(base64Size);
return result;
Comment thread
kvega005 marked this conversation as resolved.
Outdated
}

std::string wsl::windows::common::BuildRegistryAuthHeader(
const std::string& identityToken, const std::string& serverAddress)
{
auto authJson = std::format(
R"({{"identitytoken":"{}","serveraddress":"{}"}})", identityToken, serverAddress);

DWORD base64Size = 0;
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringA(
reinterpret_cast<const BYTE*>(authJson.c_str()),
static_cast<DWORD>(authJson.size()),
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
nullptr,
&base64Size));

std::string result(base64Size, '\0');
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringA(
reinterpret_cast<const BYTE*>(authJson.c_str()),
static_cast<DWORD>(authJson.size()),
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
result.data(),
&base64Size));

result.resize(base64Size);
return result;
Comment thread
kvega005 marked this conversation as resolved.
Outdated
Comment thread
kvega005 marked this conversation as resolved.
Outdated
Comment thread
kvega005 marked this conversation as resolved.
Outdated
}
30 changes: 30 additions & 0 deletions src/windows/common/WslcCredentialStore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

WslcCredentialStore.h

Abstract:

Helpers for building Docker/OCI registry credential payloads.

--*/

#pragma once
#include <string>

namespace wsl::windows::common {

// Builds the base64-encoded X-Registry-Auth header value used by Docker APIs
// (PullImage, PushImage, etc.) from the given credentials.
std::string BuildRegistryAuthHeader(const std::string& username, const std::string& password, const std::string& serverAddress);

// Builds the base64-encoded X-Registry-Auth header value from an identity token
// returned by Authenticate().
std::string BuildRegistryAuthHeader(const std::string& identityToken, const std::string& serverAddress);

// TODO: Implement credential storage using WinCred

} // namespace wsl::windows::common
19 changes: 19 additions & 0 deletions src/windows/inc/docker_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ struct EmptyRequest
using TResponse = void;
};

struct AuthRequest
{
using TResponse = struct AuthResponse;

std::string username;
std::string password;
std::string serveraddress;

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 CreateVolume
{
using TResponse = void;
Expand Down
2 changes: 2 additions & 0 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,9 @@ interface IWSLCSession : IUnknown
HRESULT ListImages([in, unique] const WSLCListImageOptions* Options, [out, size_is(, *Count)] WSLCImageInformation** Images, [out] ULONG* Count);
HRESULT DeleteImage([in] const WSLCDeleteImageOptions* Options, [out, size_is(, *Count)] WSLCDeletedImageInformation** DeletedImages, [out] ULONG* Count);
HRESULT TagImage([in] const WSLCTagImageOptions* Options);
HRESULT PushImage([in] LPCSTR Image, [in, unique] LPCSTR RegistryAuthenticationInformation, [in, unique] IProgressCallback* ProgressCallback);
HRESULT InspectImage([in] LPCSTR ImageNameOrId, [out] LPSTR* Output);
Comment thread
kvega005 marked this conversation as resolved.
HRESULT Authenticate([in] LPCSTR ServerAddress, [in] LPCSTR Username, [in] LPCSTR Password, [out] LPSTR* IdentityToken);
Comment thread
kvega005 marked this conversation as resolved.
Outdated

Comment thread
kvega005 marked this conversation as resolved.
// Container management.
HRESULT CreateContainer([in] const WSLCContainerOptions* Options, [out] IWSLCContainer** Container);
Expand Down
Loading
Loading