Skip to content

Commit e1dd748

Browse files
committed
CCG plugin notes
1 parent 83dbddb commit e1dd748

File tree

15 files changed

+466
-51
lines changed

15 files changed

+466
-51
lines changed

sample/KerberosDemo/Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# syntax=docker/dockerfile:1
2+
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build-env
3+
WORKDIR /app
4+
5+
# Copy csproj and restore as distinct layers
6+
COPY *.csproj ./
7+
RUN dotnet restore
8+
9+
# Copy everything else and build
10+
COPY ./ ./
11+
RUN dotnet publish -c Release -o out
12+
13+
# Build runtime image
14+
FROM mcr.microsoft.com/dotnet/aspnet:5.0
15+
WORKDIR /app
16+
COPY --from=build-env /app/out .
17+
USER "NT AUTHORITY\NETWORK SERVICE"
18+
ENTRYPOINT ["dotnet", "KerberosDemo.dll"]

sample/KerberosDemo/Startup.cs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,30 @@ public Startup(IConfiguration configuration)
2929
// This method gets called by the runtime. Use this method to add services to the container.
3030
public void ConfigureServices(IServiceCollection services)
3131
{
32-
var serviceAccount = Environment.GetEnvironmentVariable("KRB_SERVICE_ACCOUNT")!;
33-
var password = Environment.GetEnvironmentVariable("KRB_PASSWORD")!;
34-
var kdcStr = Environment.GetEnvironmentVariable("KRB5_KDC");
35-
var domain = serviceAccount.Split("@").Last();
36-
var ldapAddress = kdcStr != null ? kdcStr.Split(";").First() : domain;
32+
// var serviceAccount = Environment.GetEnvironmentVariable("KRB_SERVICE_ACCOUNT");
33+
// if (services == null)
34+
// {
35+
// throw new Exception("KRB_SERVICE_ACCOUNT must be set");
36+
// }
37+
// var password = Environment.GetEnvironmentVariable("KRB_PASSWORD")!;
38+
// if (password == null)
39+
// {
40+
// throw new Exception("KRB_PASSWORD must be set");
41+
// }
42+
// var kdcStr = Environment.GetEnvironmentVariable("KRB5_KDC");
43+
// var domain = serviceAccount.Split("@").Last();
44+
// var ldapAddress = kdcStr != null ? kdcStr.Split(";").First() : domain;
3745
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
38-
.AddNegotiate(c => c
39-
.EnableLdap(ldap =>
40-
{
41-
ldap.LdapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapAddress, true, false), new NetworkCredential(serviceAccount, password), AuthType.Basic);
42-
ldap.Domain = domain;
43-
ldap.LdapConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
44-
ldap.LdapConnection.SessionOptions.ProtocolVersion = 3; //Setting LDAP Protocol to latest version
45-
ldap.LdapConnection.AutoBind = true;
46-
}));
46+
.AddNegotiate();
47+
// .AddNegotiate(c => c
48+
// .EnableLdap(ldap =>
49+
// {
50+
// ldap.LdapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapAddress, true, false), new NetworkCredential(serviceAccount, password), AuthType.Basic);
51+
// ldap.Domain = domain;
52+
// ldap.LdapConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
53+
// ldap.LdapConnection.SessionOptions.ProtocolVersion = 3; //Setting LDAP Protocol to latest version
54+
// ldap.LdapConnection.AutoBind = true;
55+
// }));
4756
services.AddControllers();
4857
services.AddSwaggerGen(c =>
4958
{

sample/KerberosDemo/appsettings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"Logging": {
33
"LogLevel": {
4-
"Default": "Information",
5-
"Microsoft": "Warning",
4+
"Default": "Debug",
5+
"Microsoft": "Debug",
66
"Microsoft.Hosting.Lifetime": "Information"
77
}
88
},

src/CcgPlugin/CcgCredProvider.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.IO;
3+
using System.Runtime.CompilerServices;
4+
using System.Runtime.InteropServices;
5+
6+
namespace CcgPlugin
7+
{
8+
9+
[Guid("6ECDA518-2010-4437-8BC3-46E752B7B172")]
10+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
11+
[ComImport]
12+
public interface ICcgDomainAuthCredentials
13+
{
14+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
15+
void GetPasswordCredentials(
16+
[MarshalAs(UnmanagedType.LPWStr), In] string pluginInput,
17+
[MarshalAs(UnmanagedType.LPWStr)] out string domainName,
18+
[MarshalAs(UnmanagedType.LPWStr)] out string username,
19+
[MarshalAs(UnmanagedType.LPWStr)] out string password);
20+
}
21+
22+
// [Guid("defff03c-3245-465f-8391-cc586a2d1f31")]
23+
[Guid("defff03c-3245-465f-8391-cc586a2d1f32")]
24+
[ClassInterface(ClassInterfaceType.None)]
25+
[ProgId("CcgCredProvider")]
26+
public class CcgCredProvider : ICcgDomainAuthCredentials
27+
{
28+
public CcgCredProvider()
29+
{
30+
File.WriteAllText(@"c:\temp\ccglog2.txt", "test");
31+
}
32+
public void GetPasswordCredentials(
33+
[MarshalAs(UnmanagedType.LPWStr), In] string pluginInput,
34+
[MarshalAs(UnmanagedType.LPWStr)] out string domainName,
35+
[MarshalAs(UnmanagedType.LPWStr)] out string username,
36+
[MarshalAs(UnmanagedType.LPWStr)] out string password)
37+
{
38+
File.WriteAllText(@"c:\temp\ccglog.txt", pluginInput);
39+
domainName = "mydomain.com";
40+
username = "myser";
41+
password = "mypassword";
42+
}
43+
44+
}
45+
}

src/CcgPlugin/CcgPlugin.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<AssemblyOriginatorKeyFile>CcgPlugin.snk</AssemblyOriginatorKeyFile>
1616
</PropertyGroup>
1717
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18-
<PlatformTarget>AnyCPU</PlatformTarget>
18+
<PlatformTarget>x64</PlatformTarget>
1919
<DebugSymbols>true</DebugSymbols>
2020
<DebugType>full</DebugType>
2121
<Optimize>false</Optimize>
@@ -38,12 +38,15 @@
3838
<Reference Include="System.Core" />
3939
</ItemGroup>
4040
<ItemGroup>
41-
<Compile Include="Class1.cs" />
41+
<Compile Include="CcgCredProvider.cs" />
4242
<Compile Include="Properties\AssemblyInfo.cs" />
4343
</ItemGroup>
4444
<ItemGroup>
4545
<None Include="CcgPlugin.snk" />
4646
</ItemGroup>
47+
<ItemGroup>
48+
<Content Include="ccg.json" />
49+
</ItemGroup>
4750
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
4851
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
4952
Other similar extension points exist, see Microsoft.Common.targets.
@@ -52,5 +55,4 @@
5255
<Target Name="AfterBuild">
5356
</Target>
5457
-->
55-
56-
</Project>
58+
</Project>

src/CcgPlugin/Class1.cs

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/CcgPlugin/NOTES.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
```
2+
bin\Debug> tlbexp CcgPlugin.dll
3+
regasm CcgPlugin.dll /tlb:CcgPlugin.tlb
4+
az aks update -g myResourceGroup -n myAKSCluster --windows-admin-password $WINDOWS_ADMIN_PASSWORD
5+
```
6+
7+
8+
9+
```
10+
MANAGED_ID=$(az aks show -g playaks2 -n MyAKS --query "identityProfile.kubeletidentity.objectId" -o tsv)
11+
az keyvault secret set --vault-name macsux-vault --name "GMSADomainUserCred" --value "ALMIREX\\gmsa1:P@ssw0rd"
12+
13+
```
14+
15+
16+
17+
18+
19+
Azure Plugin registrations
20+
21+
```
22+
PS C:\ProgramData\docker\credentialspecs> reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CCG\COMClasses" /s
23+
24+
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CCG\COMClasses\{CCC2A336-D7F3-4818-A213-272B7924213E}
25+
(Default) REG_SZ
26+
27+
reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CCC2A336-D7F3-4818-A213-272B7924213E}" /s
28+
29+
reg query "HKEY_CLASSES_ROOT" /f "CCC2A336-D7F3-4818-A213-272B7924213E" /s
30+
31+
reg query HKEY_LOCAL_MACHINE\SOFTWARE /f "{CCC2A336-D7F3-4818-A213-272B7924213E}" /s
32+
33+
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CCC2A336-D7F3-4818-A213-272B7924213E}
34+
AppID REG_SZ {557110E1-88BC-4583-8281-6AAC6F708584}
35+
36+
reg export "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{557110E1-88BC-4583-8281-6AAC6F708584}" 1.reg
37+
reg export "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WOW6432Node\AppID\{557110E1-88BC-4583-8281-6AAC6F708584}" 2.reg
38+
reg export "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\AppID\{557110E1-88BC-4583-8281-6AAC6F708584}" 3.reg
39+
40+
41+
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CCC2A336-D7F3-4818-A213-272B7924213E}\InprocServer32
42+
(Default) REG_SZ C:\Windows\System32\CCGAKVPlugin.dll
43+
ThreadingModel REG_SZ Both
44+
```
45+
46+
```
47+
kubectl exec -it node-debugger-aks-nodepool1-39078785-vmss000000-669sp -- /bin/bash
48+
ssh -l macsux 10.240.0.97
49+
```
50+
51+
```powershell
52+
docker run --security-opt "credentialspec=file://vault.json" --hostname webapp01 -it mcr.microsoft.com/windows/servercore:ltsc2019 powershell
53+
54+
docker run --security-opt "credentialspec=file://ccg.json" --hostname almirex.dc -it kerberos-demo
55+
56+
docker run --security-opt "credentialspec=file://ccg.json" --hostname webapp01 -it mcr.microsoft.com/windows/servercore:ltsc2019 powershell
57+
58+
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe CcgPlugin.dll /codebase /tlb
59+
```
60+
61+
62+
63+
1. TBS
64+
65+
2. Observability
66+
67+
3. Accelerator
68+
69+
70+
71+
4. App Catalog
72+
73+
5. Introduction to TAP

src/CcgPlugin/Properties/AssemblyInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
// General Information about an assembly is controlled through the following
55
// set of attributes. Change these attribute values to modify the information
66
// associated with an assembly.
7-
[assembly: AssemblyTitle("CcgPlugin")]
7+
[assembly: AssemblyTitle("CcgPlugin2")]
88
[assembly: AssemblyDescription("")]
99
[assembly: AssemblyConfiguration("")]
1010
[assembly: AssemblyCompany("")]
11-
[assembly: AssemblyProduct("CcgPlugin")]
11+
[assembly: AssemblyProduct("CcgPlugin2")]
1212
[assembly: AssemblyCopyright("Copyright © 2021")]
1313
[assembly: AssemblyTrademark("")]
1414
[assembly: AssemblyCulture("")]
@@ -19,7 +19,7 @@
1919
[assembly: ComVisible(true)]
2020

2121
// The following GUID is for the ID of the typelib if this project is exposed to COM
22-
[assembly: Guid("8A8F0F10-04A9-4726-9592-22ED3797761B")]
22+
[assembly: Guid("8A8F0F10-04A9-4726-9592-22ED3797761C")]
2323

2424
// Version information for an assembly consists of the following four values:
2525
//

src/CcgPlugin/README.md

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,107 @@
1+
# Experiments to get gMSA Container Credentials Manager working.
2+
3+
### Goal:
4+
5+
Implement a C# plugin that is invoked by CCM to provide credentials for container launch on non-domain joined machine. The process is documented in the following articles:
6+
7+
https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts
8+
9+
https://docs.microsoft.com/en-us/windows/win32/api/ccgplugins/nf-ccgplugins-iccgdomainauthcredentials-getpasswordcredentials
10+
11+
### Current problem:
12+
13+
Cannot get CCM to activate COM component
14+
15+
### What has been tried:
16+
17+
1. Implemented COM interface as C# project
18+
19+
2. Compile in x64 mode (Debug config)
20+
21+
3. Register to COM from `bin\Debug` folder via `C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe CcgPlugin.dll /tlb /codebase`
22+
23+
4. Add COM registration to CCG
24+
25+
```
26+
Windows Registry Editor Version 5.00
27+
28+
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\CCG\COMClasses\{DEFFF03C-3245-465F-8391-CC586A2D1F32}]
29+
```
30+
31+
32+
33+
5. Place `ccg.json` into `C:\ProgramData\Docker\credentialspecs`
34+
35+
6. Try to start container with `docker run --security-opt "credentialspec=file://ccg.json" --hostname webapp01 -it mcr.microsoft.com/windows/servercore:ltsc2019 powershell`
36+
37+
### Expected result:
38+
39+
1. Some kinda logs created in `c:\temp` (first on component activation, and method invocation)
40+
41+
### Actual result:
42+
43+
1. No logs in `c:\temp`
44+
45+
## Additional info
46+
47+
### Validate COM registration
48+
49+
COM registration seems to be successful, as it can be activated like this:
50+
51+
```c#
52+
var otherType = Type.GetTypeFromCLSID(new Guid("DEFFF03C-3245-465F-8391-CC586A2D1F32"));
53+
var otherInstance = Activator.CreateInstance(otherType).Dump();
54+
object[] args = new object[] { "test","","","" };
55+
ParameterModifier pMod = new ParameterModifier(4);
56+
pMod[1] = true;
57+
pMod[2] = true;
58+
pMod[3] = true;
59+
ParameterModifier[] mods = { pMod };
60+
61+
otherType.InvokeMember("GetPasswordCredentials", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public , null, otherInstance, args, mods, null, null);
62+
163
```
2-
bin\Debug> tlbexp CcgPlugin.dll
3-
regasm CcgPlugin.dll /tlb:CcgPlugin.tlb
4-
```
564

65+
### Confirm correct COM signature
66+
67+
1. Windows SDK installed to get original `IDL` for `ccgplugins`. Necessary files are in `C:\Program Files (x86)\Windows Kits\10\Include\10.0.20348.0\um`
68+
69+
2. IDL converted to `TLB` as following:
70+
71+
```
72+
midl ccgplugins-lib.idl /tlb ccgplugins.tlb
73+
74+
```
75+
76+
3. `TLB` converted to `.NET` assembly DLL via `tlbimp ccgplugins.tlb /out:ccgplugins-net.dll`
77+
78+
```
79+
tlbimp ccgplugins.tlb /out:ccgplugins-net.dll
80+
```
81+
82+
4. Reverse decompilation of the resulting DLL via Jetbrains DotPeek confirms that the correct signature for the interface as:
83+
84+
```c#
85+
namespace ccgplugins\u002Dnet
86+
{
87+
[Guid("6ECDA518-2010-4437-8BC3-46E752B7B172")]
88+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
89+
[ComImport]
90+
public interface ICcgDomainAuthCredentials
91+
{
92+
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
93+
void GetPasswordCredentials(
94+
[MarshalAs(UnmanagedType.LPWStr), In] string pluginInput,
95+
[MarshalAs(UnmanagedType.LPWStr)] out string domainName,
96+
[MarshalAs(UnmanagedType.LPWStr)] out string username,
97+
[MarshalAs(UnmanagedType.LPWStr)] out string password);
98+
}
99+
}
100+
101+
```
102+
103+
6104

105+
### AKS plugin
7106

107+
It was confirmed that AKS plugin that implements the above interface does work and is able to be spun up and start container with GMSA creds as per this article. https://docs.microsoft.com/en-us/azure/aks/use-group-managed-service-accounts

0 commit comments

Comments
 (0)