Skip to content

Commit

Permalink
Add support for credhub binding of kerberos credentials on cloud foundry
Browse files Browse the repository at this point in the history
  • Loading branch information
macsux committed Feb 17, 2022
1 parent 4cddd18 commit d2d08f3
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.


# User-specific files
creds.json
**/temp/**
artifacts/**
*.suo
Expand Down
60 changes: 59 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ applications:

```


** Adjust URL of the Kerberos buildpack to latest version. You can get the full zip URL from [Releases](https://github.com/macsux/kerberos-buildpack/releases) page.

## How it works
Expand All @@ -43,6 +42,65 @@ Core libraries used by .NET and Java apps use MIT Kerberos to do Kerberos (aka I

A sidecar runs in background that will obtain tickets Kerberos .NET

## Secure credentials with CredHub integration

Instead of of injecting service credentials as environmental variables, this buildpack supports injecting it securely via CredHub integration. This will safely store creds associated with the app inside CredHub, and they will be injected as environmental variables when container starts. This will be only visible from the app - they will not be visible if the user tries to query environmental variables associated with the app.

#### Prerequisite: [CredHub service broker](https://network.tanzu.vmware.com/products/credhub-service-broker) installed on the platform

1. Omit KRB_SERVICE_ACCOUNT / KRB_PASSWORD from the manifest

2. Create a file creds.json that looks similar to this:

```json
{
"ServiceAccount": "[email protected]",
"Password": "P@ssw0rd"
}
```

3. Create a CredHub service instance that carries the above credentials as following:

```
cf create-service credhub default CREDS_SERVICE_INSTANCE_NAME -c .\creds.json -t kerberos-service-principal
```

4. Bind the credentials to the app

```
cf bind-service APP_NAME CREDS_SERVICE_INSTANCE_NAME
```

5. Push the app

## Embedding Kerberos configuration into buildpack

By default the buildpack will attempt to generate an MIT Kerberos configuration file (krb5.conf) out of combination of KRB5_KDC and the realm portion of the service account (everything after `@`). This may not be sufficient in more complex environments and require full control of the `krb5.conf` to properly work. It may also be desirable to not have to include location of the KDC in the push manifest. In order to support these scenarios, the buildpack allows embedding `krb5.conf` before being deployed on the platform. This has the advantage of being uniformly applied to all apps and move control over this file in the hands of central platform operator. In order to include environment specific `krb5.conf` in the buildpack, it must be placed into the buildpack zip file under `/deps/.krb5/krb5.conf`. This can be accomplished by creating a local directory structure that looks like this:

```
.
├── KerberosBuildpack-linux-x64-v1.0.0.zip
├── deps
│ └── .krb5
│ └── krb5.conf
```

After run the following command to patch the zip file with config file:

Powershell

```
Compress-Archive -Update -Path deps -DestinationPath KerberosBuildpack-linux-x64-v1.0.0.zip
```

Linux shell

```
zip -ur KerberosBuildpack-linux-x64-v1.0.0.zip deps
```



## Troubleshooting

Recommendation is to start with sample app included, which exposes the folowing endpoints:
Expand Down
14 changes: 14 additions & 0 deletions sample/KerberosDemo/manifest-WIP.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
applications:
- name: KerberosDemo
path: bin/Debug/net5.0/publish
memory: 512M
health-check-type: none
buildpacks:
- https://github.com/macsux/kerberos-buildpack/releases/download/WIP/KerberosBuildpack-linux-x64-WIP.zip
- dotnet_core_buildpack
env:
KRB5_KDC: dc1.macsux.com
KRB_SERVICE_ACCOUNT: ""
KRB_PASSWORD: ""
ConnectionStrings__SqlServer: Server=dc1.macsux.com;Database=master;Trusted_Connection=True;TrustServerCertificate=True
22 changes: 22 additions & 0 deletions src/KerberosSidecar/CloudFoundry/ConfigurationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace KerberosSidecar.CloudFoundry;

public static class ConfigurationExtensions
{
public static IEnumerable<ServiceBinding> GetServiceBindings(this IConfiguration config)
{
return config.GetSection("vcap:services").GetChildren().SelectMany(serviceTypeSection =>
{
var serviceType = serviceTypeSection.Key;

return serviceTypeSection.Get<List<ServiceBinding>>(c =>
{
c.BindNonPublicProperties = true;
})
.Select(x =>
{
x.Type = serviceType;
return x;
});
});
}
}
25 changes: 25 additions & 0 deletions src/KerberosSidecar/CloudFoundry/ServiceBinding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;

#nullable disable
namespace KerberosSidecar.CloudFoundry;

[PublicAPI]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class ServiceBinding
{
public string Name { get; set; }
public string Type {get;set;}
public string Plan { get; set; }
public IConfigurationSection Credentials { get; set; }
public T GetCredentials<T>() => Credentials.Get<T>();
public List<string> Tags {get;set;}
public string SyslogDrainUrl {get;set;}
private string Syslog_Drain_Url { set => SyslogDrainUrl = value; }
public string Provider {get;set;}
public string Label {get;set;}
public string InstanceName { get; set; }
private string Instance_Name { set => InstanceName = value; }
public string BindingName {get;set;}
private string Binding_Name { set => BindingName = value; }
}
11 changes: 11 additions & 0 deletions src/KerberosSidecar/CloudFoundry/ServiceCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#nullable disable
using JetBrains.Annotations;

namespace KerberosSidecar.CloudFoundry;

[PublicAPI]
public class ServiceCredentials
{
public string ServiceAccount { get; set; }
public string Password { get; set; }
}
2 changes: 1 addition & 1 deletion src/KerberosSidecar/KerberosSidecar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

<ItemGroup>
<PackageReference Include="Kerberos.NET" Version="4.5.155" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0" />
<PackageReference Include="Steeltoe.Extensions.Configuration.CloudFoundryCore" Version="3.1.3" />
</ItemGroup>

<ItemGroup>
Expand Down
26 changes: 22 additions & 4 deletions src/KerberosSidecar/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma warning disable IL2026
using System.Net;
using Kerberos.NET.Client;
using Kerberos.NET.Configuration;
using Kerberos.NET.Credentials;
using KerberosSidecar;
using KerberosSidecar.CloudFoundry;
using KerberosSidecar.HealthChecks;
using KerberosSidecar.Spn;
using Microsoft.AspNetCore.Builder;
Expand All @@ -13,13 +15,15 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Steeltoe.Extensions.Configuration.CloudFoundry;


var webHostBuilder = WebApplication.CreateBuilder(args);

webHostBuilder.Configuration.AddYamlFile("appsettings.yaml", optional: true, reloadOnChange: true);
webHostBuilder.Configuration.AddYamlFile($"appsettings.{webHostBuilder.Environment.EnvironmentName}.yaml", optional: true, reloadOnChange: true);

webHostBuilder.Configuration
.AddYamlFile("appsettings.yaml", optional: true, reloadOnChange: true)
.AddYamlFile($"appsettings.{webHostBuilder.Environment.EnvironmentName}.yaml", optional: true, reloadOnChange: true)
.AddCloudFoundry();
var services = webHostBuilder.Services;
services.AddOptions<KerberosOptions>()
.Configure(c =>
Expand All @@ -33,17 +37,31 @@
c.Kdc = config.GetValue<string>("KRB_KDC");
c.RunOnce = config.GetValue<bool>("KRB_RunOnce");
})
.Configure<IConfiguration>((options, config) =>
{
var serviceBindingCredentials = config.GetServiceBindings()
.Where(x => x.Tags.Contains("kerberos-service-principal"))
.Select(x => x.GetCredentials<ServiceCredentials>())
.FirstOrDefault();
if (serviceBindingCredentials != null)
{
options.ServiceAccount = serviceBindingCredentials.ServiceAccount;
options.Password = serviceBindingCredentials.Password;
}
})
.PostConfigure<ILoggerFactory>((options, loggerFactory) =>
{
var log = loggerFactory.CreateLogger<Program>();
var homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var userKerbDir = Path.Combine(homeDir, ".krb5");

// default files to user's ~/.krb/ folder if not set
// ReSharper disable ConstantNullCoalescingCondition
options.Kerb5ConfigFile ??= Path.Combine(userKerbDir, "krb5.conf");
options.KeytabFile ??= Path.Combine(userKerbDir, "krb5.keytab");
options.CacheFile ??= Path.Combine(userKerbDir, "krb5cc");
options.GenerateKrb5 = options.Kerb5ConfigFile != null! ? !File.Exists(options.Kerb5ConfigFile) : true;
// ReSharper restore ConstantNullCoalescingCondition
options.GenerateKrb5 = options.Kerb5ConfigFile == null! || !File.Exists(options.Kerb5ConfigFile);

Directory.CreateDirectory(Path.GetDirectoryName(options.Kerb5ConfigFile)!);
Directory.CreateDirectory(Path.GetDirectoryName(options.KeytabFile)!);
Expand Down

0 comments on commit d2d08f3

Please sign in to comment.