/// <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;
            }));
        }
Пример #2
0
        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);
            }
        }
Пример #5
0
        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);
            }
        }
Пример #6
0
        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;
        }
Пример #7
0
        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));
        }
Пример #8
0
        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 ?? "/"));
            }
        }