/// <summary> /// Make a CredHubClient available to DI /// </summary> /// <remarks>Uses UAA user/password authentication if configured, otherwise mTLS</remarks> /// <param name="services">Service collection</param> /// <param name="config">App configuration</param> /// <param name="loggerFactory">Logger factory</param> /// <returns>Service collection with CredHubClient added in</returns> public static IServiceCollection AddCredHubClient(this IServiceCollection services, IConfiguration config, ILoggerFactory loggerFactory = null) { ILogger startupLogger = null; ILogger credhubLogger = null; if (loggerFactory != null) { startupLogger = loggerFactory.CreateLogger("Steeltoe.Security.DataProtection.CredHubCore"); credhubLogger = loggerFactory.CreateLogger <CredHubClient>(); } var credHubOptions = config.GetSection("CredHubClient").Get <CredHubOptions>(); credHubOptions.Validate(); CredHubClient credHubClient; try { startupLogger?.LogTrace("Using UAA auth for CredHub client with client id {ClientId}", credHubOptions.ClientId); credHubClient = CredHubClient.CreateUAAClientAsync(credHubOptions, credhubLogger).GetAwaiter().GetResult(); services.AddSingleton <ICredHubClient>(credHubClient); } catch (Exception e) { startupLogger?.LogCritical(e, "Failed to initialize CredHub client for ServiceCollection"); } return(services); }
/// <summary> /// Reach out to a CredHub server to interpolate credentials found in VCAP_SERVICES /// </summary> /// <param name="webHostBuilder">Your app's host builder</param> /// <param name="loggerFactory">To enable logging in the credhub client, pass in a loggerfactory</param> /// <returns>Your application's host builder with credentials interpolated</returns> public static IWebHostBuilder UseCredHubInterpolation(this IWebHostBuilder webHostBuilder, ILoggerFactory loggerFactory = null) { ILogger startupLogger = null; ILogger credhubLogger = null; if (loggerFactory != null) { startupLogger = loggerFactory.CreateLogger("Steeltoe.Security.DataProtection.CredHubCore"); credhubLogger = loggerFactory.CreateLogger <CredHubClient>(); } var vcapServices = Environment.GetEnvironmentVariable("VCAP_SERVICES"); // don't bother interpolating if there aren't any credhub references if (vcapServices != null && vcapServices.Contains("credhub-ref")) { webHostBuilder.ConfigureAppConfiguration((context, config) => { var builtConfig = config.Build(); CredHubClient credHubClient; var credHubOptions = builtConfig.GetSection("CredHubClient").Get <CredHubOptions>(); credHubOptions.Validate(); try { startupLogger?.LogTrace("Using UAA auth for CredHub client with client id {ClientId}", credHubOptions.ClientId); credHubClient = CredHubClient.CreateUAAClientAsync(credHubOptions, credhubLogger).GetAwaiter().GetResult(); } catch (Exception e) { startupLogger?.LogCritical(e, "Failed to initialize CredHub client"); // return early to prevent call we know will fail return; } try { // send the interpolate request to CredHub string interpolated = credHubClient.InterpolateServiceDataAsync(vcapServices).GetAwaiter().GetResult(); // update the environment variable for this process Environment.SetEnvironmentVariable("VCAP_SERVICES", interpolated); } catch (Exception e) { startupLogger?.LogCritical(e, "Failed to interpolate service data with CredHub"); } }); } else { startupLogger?.LogInformation("No CredHub references found in VCAP_SERVICES"); } return(webHostBuilder); }
/// <summary> /// Initialize a CredHub Client with user credentials for the appropriate UAA server /// </summary> /// <param name="credHubOptions">CredHub client configuration values</param> /// <param name="logger">Pass in a logger if you want logs</param> /// <param name="httpClient">Primarily for tests, optionally provide your own http client</param> /// <returns>An initialized CredHub client (using UAA OAuth)</returns> public static Task<CredHubClient> CreateUAAClientAsync(CredHubOptions credHubOptions, ILogger logger = null, HttpClient httpClient = null) { _logger = logger; _baseCredHubUrl = credHubOptions.CredHubUrl; var client = new CredHubClient(credHubOptions.ValidateCertificates); _httpClientHandler = new HttpClientHandler(); _httpClient = httpClient ?? client.InitializeHttpClient(_httpClientHandler); return client.InitializeAsync(credHubOptions); }
/// <summary> /// Expects CF_INSTANCE_CERT and CF_INSTANCE_KEY to be set in the environment (automatically set by DIEGO in cloud foundry) /// </summary> /// <param name="credHubOptions">CredHub client configuration values</param> /// <param name="logger">Pass in a logger if you want logs</param> /// <param name="httpClient">Optionally override the http client used to talk to credhub - added for tests only</param> /// <returns>An initialized CredHub client (using mTLS)</returns> public static async Task <CredHubClient> CreateMTLSClientAsync(CredHubOptions credHubOptions, ILogger logger = null, HttpClient httpClient = null) { _logger = logger; _baseCredHubUrl = credHubOptions.CredHubUrl; var cfInstanceCert = Environment.GetEnvironmentVariable("CF_INSTANCE_CERT") ?? string.Empty; var cfInstanceKey = Environment.GetEnvironmentVariable("CF_INSTANCE_KEY"); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && cfInstanceCert.StartsWith("/")) { _logger?.LogTrace("Detected Windows OS and root-relative paths for application credentials: converting to app-relative paths"); cfInstanceCert = ".." + cfInstanceCert; cfInstanceKey = ".." + cfInstanceKey; } if (string.IsNullOrEmpty(cfInstanceCert) || string.IsNullOrEmpty(cfInstanceKey)) { _logger?.LogCritical("Cloud Foundry application credentials not found in the environment"); throw new ArgumentException("Application Credentials not found (Missing ENV variable for Instance Cert and/or Key)"); } _logger?.LogTrace("Application certificate: " + cfInstanceCert); _logger?.LogTrace("Application key: " + cfInstanceKey); if (File.Exists(cfInstanceCert) && File.Exists(cfInstanceKey)) { var client = new CredHubClient(credHubOptions.ValidateCertificates); _httpClientHandler = new HttpClientHandler() { ClientCertificateOptions = ClientCertificateOption.Manual }; var certBytes = File.ReadAllBytes(cfInstanceCert); var keyBytes = File.ReadAllBytes(cfInstanceKey); var appCredentials = CertificateHelpers.GetX509FromBytes(certBytes, keyBytes); if (!appCredentials.HasPrivateKey) { throw new Exception("Private key is missing, mTLS won't work"); } _httpClientHandler.ClientCertificates.Add(appCredentials); _httpClient = httpClient ?? client.InitializeHttpClient(_httpClientHandler); return(await client.InitializeAsync()); } else { throw new Exception($"Application credentials not found (Failed to load Instance Cert [{cfInstanceCert}] and/or Key [{cfInstanceKey}])"); } }