hiera-cfn-metadata 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 079405345d2b1e39e29713feafb802b754eece8f
4
+ data.tar.gz: 97a40eb65e8b7324f48ef1d419d8eb8f8c2adbbe
5
+ SHA512:
6
+ metadata.gz: de917f50c270aa0ae32fcc342e3697d55624afdb14536fe5fc88a7867ca62f0b37390cca3d1931f7b924d1534215c7b8d799f2cdd36e033202c19d7449e828d5
7
+ data.tar.gz: f1c9548b28d2c524d42a045f3b630b6eada42b7930e4354f608bde40432e096cc9f68599dbfc48c56b99e5ebdedf11c6b64d263d067335611d554684915196eb
@@ -0,0 +1,110 @@
1
+ require 'base64'
2
+ require 'net/http'
3
+ require 'time'
4
+
5
+ module Aws
6
+ class InstanceIdentityCredentials
7
+
8
+ include CredentialProvider
9
+ include RefreshingCredentials
10
+
11
+ # @api private
12
+ class Non200Response < RuntimeError; end
13
+
14
+ # These are the errors we trap when attempting to talk to the
15
+ # instance metadata service. Any of these imply the service
16
+ # is not present, no responding or some other non-recoverable
17
+ # error.
18
+ # @api private
19
+ FAILURES = [
20
+ Errno::EHOSTUNREACH,
21
+ Errno::ECONNREFUSED,
22
+ Errno::EHOSTDOWN,
23
+ Errno::ENETUNREACH,
24
+ SocketError,
25
+ Timeout::Error,
26
+ Non200Response,
27
+ ]
28
+
29
+ # @param [Hash] options
30
+ # @option options [Integer] :retries (5) Number of times to retry
31
+ # when retrieving credentials.
32
+ # @option options [String] :ip_address ('169.254.169.254')
33
+ # @option options [Integer] :port (80)
34
+ # @option options [Float] :http_open_timeout (5)
35
+ # @option options [Float] :http_read_timeout (5)
36
+ # @option options [Numeric, Proc] :delay By default, failures are retried
37
+ # with exponential back-off, i.e. `sleep(1.2 ** num_failures)`. You can
38
+ # pass a number of seconds to sleep between failed attempts, or
39
+ # a Proc that accepts the number of failures.
40
+ # @option options [IO] :http_debug_output (nil) HTTP wire
41
+ # traces are sent to this object. You can specify something
42
+ # like $stdout.
43
+ def initialize options = {}
44
+ @from_identity = true
45
+ @retries = options[:retries] || 5
46
+ @ip_address = options[:ip_address] || '169.254.169.254'
47
+ @port = options[:port] || 80
48
+ @http_open_timeout = options[:http_open_timeout] || 5
49
+ @http_read_timeout = options[:http_read_timeout] || 5
50
+ @http_debug_output = options[:http_debug_output]
51
+ super
52
+ end
53
+
54
+ # @return [Integer] The number of times to retry failed atttempts to
55
+ # fetch credentials from the instance metadata service. Defaults to 0.
56
+ attr_reader :retries
57
+
58
+ attr_reader :from_identity
59
+
60
+ private
61
+
62
+ def refresh
63
+ doc = get_instance_identity('document')
64
+ sig = get_instance_identity('signature')
65
+ @credentials = Credentials.new(
66
+ Base64.encode64(doc),
67
+ sig.delete("\n")
68
+ )
69
+ # Pretend that it expires in an hour
70
+ @expiration = Time.now + 60 * 60
71
+ end
72
+
73
+ def get_instance_identity(path)
74
+ failed_attempts = 0
75
+ begin
76
+ open_connection do |conn|
77
+ path = "/latest/dynamic/instance-identity/#{path}"
78
+ profile_name = http_get(conn, path).lines.first.strip
79
+ http_get(conn, path + profile_name)
80
+ end
81
+ rescue *FAILURES
82
+ if failed_attempts < @retries
83
+ failed_attempts += 1
84
+ retry
85
+ else
86
+ '{}'
87
+ end
88
+ end
89
+ end
90
+
91
+ def open_connection
92
+ http = Net::HTTP.new(@ip_address, @port, nil)
93
+ http.open_timeout = @http_open_timeout
94
+ http.read_timeout = @http_read_timeout
95
+ http.set_debug_output(@http_debug_output) if @http_debug_output
96
+ http.start
97
+ yield(http).tap { http.finish }
98
+ end
99
+
100
+ def http_get(connection, path)
101
+ response = connection.request(Net::HTTP::Get.new(path))
102
+ if response.code.to_i == 200
103
+ response.body
104
+ else
105
+ raise Non200Response
106
+ end
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,35 @@
1
+ module Aws
2
+ module Plugins
3
+ class CfnRequestSigner < Seahorse::Client::Plugin
4
+
5
+ option(:signature_version) do |cfg|
6
+ cfg.api.metadata['signatureVersion']
7
+ end
8
+
9
+ class Handler < Seahorse::Client::Handler
10
+ def call(context)
11
+ if cfn_request?(context) and using_instance_identity?(context)
12
+ context.config.signature_version = 'cfn_v1'
13
+ end
14
+ @handler.call(context)
15
+ end
16
+
17
+ private
18
+
19
+ def cfn_request?(context)
20
+ context.config.api.metadata['endpointPrefix'] == 'cloudformation'
21
+ end
22
+
23
+ def using_instance_identity?(context)
24
+ context.config.credentials.respond_to? :from_identity
25
+ end
26
+
27
+ end
28
+
29
+ def add_handlers(handlers, config)
30
+ handlers.add(Handler, step: :sign)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ require "time"
2
+ require "openssl"
3
+
4
+ module Aws
5
+ module Signers
6
+ class CfnV1 < Base
7
+ def sign(http_request)
8
+ http_request.headers["Authorization"] = authorization()
9
+ http_request
10
+ end
11
+
12
+ private
13
+
14
+ def authorization
15
+ return "CFN_V1 #{document}:#{signature}"
16
+ end
17
+
18
+ def document
19
+ @credentials.access_key_id
20
+ end
21
+
22
+ def signature
23
+ @credentials.secret_access_key
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,77 @@
1
+ # Vault backend for Hiera
2
+ class Hiera
3
+ module Backend
4
+ class Cfn_metadata_backend
5
+
6
+ def initialize()
7
+ require 'aws-sdk-core'
8
+ require 'hiera-cfn-metadata'
9
+
10
+ @config = Config[:cfn_metadata]
11
+ stack = @config[:stack] || ENV['CFN_STACK']
12
+ resource = @config[:resource] || ENV['CFN_RESOURCE']
13
+
14
+ begin
15
+ cfn = Aws::CloudFormation::Client.new(
16
+ region: @config[:region] || ENV['AWS_REGION'],
17
+ credentials: Aws::InstanceIdentityCredentials.new(),
18
+ )
19
+
20
+ resp = cfn.describe_stack_resource({
21
+ stack_name: stack,
22
+ logical_resource_id: resource
23
+ })
24
+ raise resp.error if !resp.successful?
25
+
26
+ metadata = resp.stack_resource_detail.metadata
27
+
28
+ @datasources = JSON.parse(metadata)
29
+ p @datasources
30
+
31
+ rescue Exception => e
32
+ @datasources = nil
33
+ Hiera.warn("[hiera-cfn-metadata] Skipping backend. Configuration error: #{e}")
34
+ end
35
+ end
36
+
37
+ def lookup(key, scope, order_override, resolution_type, context)
38
+ answer = nil
39
+ found = false
40
+
41
+ Hiera.debug("[hiera-cfn-metadata] Looking up #{key}")
42
+
43
+ Backend.datasources(scope, order_override) do |source|
44
+ Hiera.debug("[hiera-cfn-metadata] Looking for data source #{source}")
45
+
46
+ data = @datasources[source]
47
+ next if section.nil? or data.empty?
48
+ next unless data.include?(key)
49
+ found = true
50
+
51
+ # for array resolution we just append to the array whatever
52
+ # we find, we then goes onto the next file and keep adding to
53
+ # the array
54
+ #
55
+ # for priority searches we break after the first found data item
56
+ new_answer = Backend.parse_answer(data[key], scope, {}, context)
57
+ case resolution_type.is_a?(Hash) ? :hash : resolution_type
58
+ when :array
59
+ raise Exception, "[hiera-cfn-metadata] Hiera type mismatch for key '#{key}': expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String
60
+ answer ||= []
61
+ answer << new_answer
62
+ when :hash
63
+ raise Exception, "[hiera-cfn-metadata] Hiera type mismatch for key '#{key}': expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
64
+ answer ||= {}
65
+ answer = Backend.merge_answer(new_answer, answer, resolution_type)
66
+ else
67
+ answer = new_answer
68
+ break
69
+ end
70
+ end
71
+ throw :no_such_key unless found
72
+ return answer
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,15 @@
1
+ require "aws-sdk-core"
2
+ require_relative "aws-sdk-core/instance_identity_credentials"
3
+ require_relative "aws-sdk-core/signers/cfn_v1"
4
+ require_relative "aws-sdk-core/plugins/cfn_request_signer"
5
+
6
+ # Add the CFN_V1 signer to the built-in list
7
+ default_signers = Aws::Plugins::RequestSigner::Handler.const_get(:SIGNERS)
8
+ default_signers['cfn_v1'] = Aws::Signers::CfnV1
9
+ Aws::Plugins::RequestSigner::Handler.const_set(:SIGNERS, default_signers)
10
+
11
+ # Insert the CFN request signer plugin before the standard request signer
12
+ # so we can alter the signature version in time
13
+ plugins = Aws::CloudFormation::Client.plugins.dup
14
+ plugins.insert(plugins.index(Aws::Plugins::RequestSigner), Aws::Plugins::CfnRequestSigner)
15
+ Aws::CloudFormation::Client.set_plugins(plugins)
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hiera-cfn-metadata
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Sokolowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ description: Hiera backend for retrieving CloudFormation resource metadata and parsing
28
+ it as a JSON data source
29
+ email: jonathan.sokolowski@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/aws-sdk-core/instance_identity_credentials.rb
35
+ - lib/aws-sdk-core/plugins/cfn_request_signer.rb
36
+ - lib/aws-sdk-core/signers/cfn_v1.rb
37
+ - lib/hiera-cfn-metadata.rb
38
+ - lib/hiera/backend/cfn_metadata_backend.rb
39
+ homepage: http://github.com/jsok/hiera-cfn-metadata
40
+ licenses:
41
+ - Apache-2.0
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.4.5
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Module for using CloudFormation resource metadata as a hiera backend
63
+ test_files: []