/// <summary> /// <para> /// Adds the secrets of a HashiCorp Vault KeyValue engine to the secret store. /// </para> /// <para> /// See more information on HashiCorp: <a href="https://www.vaultproject.io/docs" />. /// </para> /// </summary> /// <param name="builder">The builder to add the HashiCorp secrets from the KeyValue Vault to.</param> /// <param name="vaultServerUriWithPort">The URI that points to the running HashiCorp Vault.</param> /// <param name="username">The username of the UserPass authentication method.</param> /// <param name="password">The password of the UserPass authentication method.</param> /// <param name="secretPath">The secret path where the secret provider should look for secrets.</param> /// <param name="configureOptions">The optional function to set the additional options to configure the HashiCorp Vault KeyValue.</param> /// <param name="name">The unique name to register this HashiCorp provider in the secret store.</param> /// <param name="mutateSecretName">The optional function to mutate the secret name before looking it up.</param> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="builder"/> or <paramref name="secretPath"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException"> /// Thrown when the <paramref name="vaultServerUriWithPort"/> is blank or doesn't represent a valid URI, /// or the <paramref name="username"/> or <paramref name="password"/> is blank, /// or the <paramref name="secretPath"/> is blank. /// </exception> public static SecretStoreBuilder AddHashiCorpVaultWithUserPass( this SecretStoreBuilder builder, string vaultServerUriWithPort, string username, string password, string secretPath, Action <HashiCorpVaultUserPassOptions> configureOptions, string name, Func <string, string> mutateSecretName) { Guard.NotNull(builder, nameof(builder), "Requires a secret store builder to add the HashiCorp Vault secret provider"); Guard.NotNullOrWhitespace(vaultServerUriWithPort, nameof(vaultServerUriWithPort), "Requires a valid HashiCorp Vault URI with HTTP port to connect to the running HashiCorp Vault"); Guard.NotNullOrWhitespace(username, nameof(username), "Requires a username for the UserPass authentication during connecting with the HashiCorp Vault"); Guard.NotNullOrWhitespace(password, nameof(password), "Requires a password for the UserPass authentication during connecting with the HashiCorp Vault"); Guard.NotNullOrWhitespace(secretPath, nameof(secretPath), "Requires a path where the HashiCorp Vault secrets are stored"); Guard.For <ArgumentException>(() => !Uri.IsWellFormedUriString(vaultServerUriWithPort, UriKind.RelativeOrAbsolute), "Requires a HashiCorp Vault server URI with HTTP port"); var options = new HashiCorpVaultUserPassOptions(); configureOptions?.Invoke(options); IAuthMethodInfo authenticationMethod = new UserPassAuthMethodInfo(options.UserPassMountPoint, username, password); var settings = new VaultClientSettings(vaultServerUriWithPort, authenticationMethod); return(AddHashiCorpVault(builder, settings, secretPath, options, configureSecretProviderOptions: secretProviderOptions => { secretProviderOptions.Name = name; secretProviderOptions.MutateSecretName = mutateSecretName; })); }
public async Task AuthenticateWithUserPassKeyValueV2_GetNotFoundSecret_Fails() { // Arrange string secretPath = "mysecret"; string secretName = "my-value"; string expected = "s3cr3t"; string userName = "******"; string password = "******"; using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint : DefaultDevMountPoint, path : secretPath, data : new Dictionary <string, object> { ["unknown-prefix-" + secretName] = expected }); var authentication = new UserPassAuthMethodInfo(userName, password); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); var provider = new HashiCorpSecretProvider(settings, secretPath, new HashiCorpVaultOptions { KeyValueMountPoint = DefaultDevMountPoint, KeyValueVersion = VaultKeyValueSecretEngineVersion.V2 }, NullLogger <HashiCorpSecretProvider> .Instance); // Act string actual = await provider.GetRawSecretAsync(secretName); // Assert Assert.Null(actual); } }
public async Task AddHashiCorpVault_WithWrongMutation_Fails() { // Arrange string secretPath = "secretpath"; string secretKey = "my-value", expected = "s3cr3t"; string userName = "******", password = "******"; var builder = new HostBuilder(); using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint : DefaultDevMountPoint, path : secretPath, data : new Dictionary <string, object> { [secretKey] = expected }); var authentication = new UserPassAuthMethodInfo(userName, password); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); // Act builder.ConfigureSecretStore((config, stores) => { stores.AddHashiCorpVault(settings, secretPath, options => options.KeyValueMountPoint = DefaultDevMountPoint, mutateSecretName: secretName => "Test-" + secretName); }); // Assert IHost host = builder.Build(); var provider = host.Services.GetRequiredService <ISecretProvider>(); await Assert.ThrowsAsync <SecretNotFoundException>(() => provider.GetRawSecretAsync(secretKey)); } }
public async Task AddHashiCorpVault_WithMutationToRemovePrefix_Succeeds() { // Arrange string secretPath = "secretpath"; string secretKey = "my-value", expected = "s3cr3t"; string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; string password = _config["Arcus:HashiCorp:UserPass:Password"]; const string secretNamePrefix = "Test-"; var builder = new HostBuilder(); using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint : DefaultDevMountPoint, path : secretPath, data : new Dictionary <string, object> { [secretKey] = expected }); var authentication = new UserPassAuthMethodInfo(userName, password); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); // Act builder.ConfigureSecretStore((config, stores) => { stores.AddHashiCorpVault(settings, secretPath, options => options.KeyValueMountPoint = DefaultDevMountPoint, mutateSecretName: secretName => secretName.Remove(0, secretNamePrefix.Length), name: null); }); // Assert IHost host = builder.Build(); var provider = host.Services.GetRequiredService <ISecretProvider>(); string actual = await provider.GetRawSecretAsync(secretNamePrefix + secretKey); Assert.Equal(expected, actual); } }
public async Task AuthenticateWithUserPassKeyValueV1_GetSecret_Succeeds() { // Arrange string secretPath = "mysecret"; string secretName = "my-value"; string expected = "s3cr3t"; string userName = "******"; string password = "******"; const string mountPoint = "secret-v1"; const VaultKeyValueSecretEngineVersion keyValueVersion = VaultKeyValueSecretEngineVersion.V1; using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, mountPoint)) { await server.MountKeyValueAsync(mountPoint, keyValueVersion); await server.KeyValueV1.WriteSecretAsync( mountPoint : mountPoint, path : secretPath, values : new Dictionary <string, object> { [secretName] = expected }); var authentication = new UserPassAuthMethodInfo(userName, password); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); var provider = new HashiCorpSecretProvider(settings, secretPath, new HashiCorpVaultOptions { KeyValueMountPoint = mountPoint, KeyValueVersion = keyValueVersion }, NullLogger <HashiCorpSecretProvider> .Instance); // Act string actual = await provider.GetRawSecretAsync(secretName); // Assert Assert.Equal(expected, actual); } }
private async void ConfirmButton_Click(object sender, RoutedEventArgs e) { ConfirmButton.IsEnabled = false; CancelButton.IsEnabled = false; ConfirmButton.Content = "Confirming..."; try { var authMethod = new UserPassAuthMethodInfo(Username.Text, Password.Password); var vaultClientSettings = new VaultClientSettings(VaultServerURL.Text, authMethod); var vaultClient = new VaultClient(vaultClientSettings); var createTokenRequest = new CreateTokenRequest(); createTokenRequest.DisplayName = "TOTP Client token"; try { Secret <object> permanentToken = await vaultClient.V1.Auth.Token.CreateTokenAsync(createTokenRequest); Properties.Settings.Default.VaultToken = permanentToken.AuthInfo.ClientToken; Properties.Settings.Default.VaultURL = VaultServerURL.Text; Properties.Settings.Default.TotpMountPoint = TOTPMountPoint.Text; Properties.Settings.Default.Save(); this.DialogResult = true; this.Close(); } catch (VaultApiException exception) { MessageBox.Show(exception.ApiErrors.FirstOrDefault(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); } catch (HttpRequestException) { MessageBox.Show("Could not connect to Vault instance", "Error", MessageBoxButton.OK, MessageBoxImage.Error); } }catch (ArgumentException exception) { MessageBox.Show(exception.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } ConfirmButton.Content = "Confirm"; ConfirmButton.IsEnabled = true; CancelButton.IsEnabled = true; }
private IVaultClient CreateVaultClient() { IAuthMethodInfo authMethod; switch (_context.AuthenticationType) { case AuthenticationType.AppRole: authMethod = new AppRoleAuthMethodInfo(_context.RoleId, _context.SecretId); break; case AuthenticationType.UsernamePassword: authMethod = new UserPassAuthMethodInfo(_context.Username, _context.Password); break; case AuthenticationType.Ldap: authMethod = new LDAPAuthMethodInfo(_context.Username, _context.Password); break; case AuthenticationType.Token: authMethod = new TokenAuthMethodInfo(_context.Token); break; default: throw new NotSupportedException($"Authentication type '{_context.AuthenticationType}' is not supported."); } var vaultClientSettings = new VaultClientSettings(_context.VaultUri.ToString(), authMethod) { VaultServiceTimeout = TimeSpan.FromSeconds(30) }; if (!string.IsNullOrWhiteSpace(_context.Namespace)) { vaultClientSettings.Namespace = _context.Namespace; } return(new VaultClient(vaultClientSettings)); }
public async Task <IActionResult> Login(LoginViewModel model) { // We're using the supplied user name and password to authenticate with Vault // and retrieve a user specific client token. var authMethodInfo = new UserPassAuthMethodInfo(model.Username, model.Password); var vaultClientSettings = new VaultClientSettings(_vaultSettings.VaultEndpointUri, authMethodInfo); var vaultClient = new VaultClient(vaultClientSettings); bool?hasRabbitMqCapability = await CheckRabbitMqCapabilityAsync(vaultClient); // Since this is was our first call to the Vault API, // we know that invalid credentials are provided when // token capabilties could not be retrieved. if (hasRabbitMqCapability == null) { ModelState.Clear(); ModelState.AddModelError("", "Invalid username/password combination."); return(View()); } else { var claims = new List <string> { $"{ClaimTypes.Name}::{model.Username}", // Save the client token as a claim. It will get stored in the encrypted cookie. $"{VaultClaims.ClientToken}::{authMethodInfo.ReturnedLoginAuthInfo.ClientToken}" }; if (hasRabbitMqCapability == true) { claims.Add($"{VaultClaims.Capability}::rabbitmq"); } #region TOTP bool requiresTotp = _vaultSettings.TotpEnabled && await RequiresTotpAsync(model, vaultClient); if (requiresTotp) { claims.Add($"{VaultClaims.Capability}::2fa"); } if (requiresTotp) { TempData[nameof(VaultClaims)] = claims; return(RedirectToAction("LoginTotp", new { model.ReturnUrl })); } #endregion var claimsIdentity = new ClaimsIdentity( claims.Select(x => { var claim = x.Split("::"); return(new Claim(claim[0], claim[1])); }), CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties(); await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); return(LocalRedirect(model.ReturnUrl ?? "/")); } }