summaryrefslogtreecommitdiff
path: root/lib/chef/secret_fetcher/hashi_vault.rb
blob: 89dce01b67afffb77af111a522dba5f19429d201 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#
# Author:: Marc Paradise (<marc@chef.io>)
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require_relative "base"
require "aws-sdk-core" # Support for aws instance profile auth
require "vault"
class Chef
  class SecretFetcher
    # == Chef::SecretFetcher::HashiVault
    # A fetcher that fetches a secret from Hashi Vault.
    #
    # Does not yet support fetching with version when a versioned key store is in use.
    # In this initial iteration the only supported authentication is IAM role-based
    #
    # Required config:
    # :auth_method - one of :iam_role, :token.  default: :iam_role
    # :vault_addr - the address of a running Vault instance, eg https://vault.example.com:8200
    #
    # For `:token` auth: `:token` - a Vault token valid for authentication.
    #
    # For `:iam_role`:  `:role_name` - the name of the role in Vault that was created
    # to support authentication via IAM.  See the Vault documentation for details[1].
    # A Terraform example is also available[2]
    #
    #
    # [1] https://www.vaultproject.io/docs/auth/aws#recommended-vault-iam-policy
    # [2] https://registry.terraform.io/modules/hashicorp/vault/aws/latest/examples/vault-iam-auth
    #             an IAM principal ARN bound to it.
    #
    # Optional config
    # :namespace - the namespace under which secrets are kept.  Only supported in with Vault Enterprise
    #
    # @example
    #
    # fetcher = SecretFetcher.for_service(:hashi_vault, { role_name: "testing-role", vault_addr: https://localhost:8200}, run_context )
    # fetcher.fetch("secretkey1")
    #
    # @example
    #
    # fetcher = SecretFetcher.for_service(:hashi_vault, { auth_method: :token, token: "s.1234abcdef", vault_addr: https://localhost:8200}, run_context )
    # fetcher.fetch("secretkey1")
    SUPPORTED_AUTH_TYPES = %i{iam_role token}.freeze
    class HashiVault < Base

      # Validate and authenticate the current session using the configurated auth strategy and parameters
      def validate!
        if config[:vault_addr].nil?
          raise Chef::Exceptions::Secret::ConfigurationInvalid.new("You must provide the Vault address in the configuration as :vault_addr")
        end

        Vault.address = config[:vault_addr]
        Vault.namespace = config[:namespace] unless config[:namespace].nil?

        case config[:auth_method]
        when :token
          validate_token_auth!(config[:token])
        when :iam_role, nil
          validate_iam_role_auth!(config[:role_name])
        else
          raise Chef::Exceptions::Secret::ConfigurationInvalid.new("Invalid :auth_method provided.  You gave #{config[:auth_method]}, expected one of :#{SUPPORTED_AUTH_TYPES.join(", :")} ")
        end
      end

      # Validates IAM role auth configuration and obtains token via IAM auth
      #
      # @param role_name [String] the name of the Vault role associated with the IAM profile .
      def validate_iam_role_auth!(role_name)
        if role_name.nil?
          raise Chef::Exceptions::Secret::ConfigurationInvalid.new("You must provide the authenticating Vault role name in the configuration as :role_name")
        end

        Vault.auth.aws_iam(role_name, Aws::InstanceProfileCredentials.new)
      end

      # Validates that a token is provided and authenticates to Hashi Vault using the provided
      # token.
      # @param token [String] a value Vault token authorized to access the secrets needed. b
      def validate_token_auth!(token)
        if token.nil?
          raise Chef::Exceptions::Secret::ConfigurationInvalid.new("You must provide the token in the configuration as :token")
        end

        Vault.auth.token(token)
      end

      # @param identifier [String] Identifier of the secret to be fetched, which should
      # be the full path of that secret, eg 'secret/example'
      # @param _version [String] not used in this implementation
      # @return [Hash] containing key/value pairs stored at the location given in 'identifier'
      def do_fetch(identifier, _version)
        Vault.logical.read(identifier).data
      end
    end
  end
end