hiera-housekeeper 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,152 @@
1
+ ### This file is almost exactly the hiera_gpg module by
2
+ ### Craig Dunn (craig@craigdunn.org) (Thanks, mate!)
3
+ ### Hopefully, our additions won't embarrass him.
4
+
5
+ require 'socket'
6
+
7
+ class Hiera
8
+ module Backend
9
+ class Housekeeper_backend
10
+
11
+ def initialize
12
+ require 'gpgme'
13
+ @data = Hash.new
14
+ @cache = Hash.new
15
+ debug("Loaded housekeeper_backend")
16
+ end
17
+
18
+ def debug (msg)
19
+ Hiera.debug("[housekeeper_backend]: #{msg}")
20
+ end
21
+
22
+ def warn (msg)
23
+ Hiera.warn("[housekeeper_backend]: #{msg}")
24
+ end
25
+
26
+
27
+ def lookup(key, scope, order_override, resolution_type)
28
+
29
+ debug("Lookup called, key #{key} resolution type is #{resolution_type}")
30
+ answer = nil
31
+
32
+ # This should compute ~ on both *nix and *doze
33
+ homes = ["HOME", "HOMEPATH"]
34
+ real_home = homes.detect { |h| ENV[h] != nil }
35
+
36
+ key_dir = Backend.parse_string(Config[:gpg][:key_dir], scope) || "#{ENV[real_home]}/.gnupg"
37
+ fqdn = Socket.gethostbyname(Socket.gethostname).first
38
+ housekeeper_server = Backend.parse_string(Config[:housekeeper][:url], scope)
39
+ housekeeper_keyname = Backend.parse_string(Config[:housekeeper][:keyname], scope) || "puppet"
40
+ housekeeper_branch = Backend.parse_string(Config[:housekeeper][:branch], scope) || "master"
41
+ housekeeper_identity = "#{housekeeper_keyname}@#{fqdn}"
42
+
43
+ seen_sources = []
44
+
45
+ Backend.datasources(scope, order_override) do |source|
46
+ begin
47
+ next if seen_sources.include? source
48
+ uri = URI.parse("http://#{housekeeper_server}:80/file/#{housekeeper_identity}/branches/#{housekeeper_branch}/hieradata/#{source}.gpg")
49
+ seen_sources.push(source)
50
+ gpgfile = open(uri).read()
51
+ rescue OpenURI::HTTPError
52
+ next
53
+ end
54
+
55
+ unless @data.has_key?(gpgfile) and !stale?(gpgfile)
56
+ plain = decrypt(gpgfile, key_dir)
57
+
58
+ next if !plain
59
+ next if !plain.kind_of?(String)
60
+ next if plain.empty?
61
+ debug("GPG decrypt returned valid data")
62
+
63
+ begin
64
+ @data[gpgfile] = YAML.load(plain)
65
+ rescue
66
+ warn("YAML decoding data in #{gpgfile} failed")
67
+ @data[gpgfile] = {}
68
+ end
69
+ end
70
+
71
+ data = @data[gpgfile]
72
+ next if !data
73
+ next if data.empty?
74
+ debug ("Data contains valid YAML")
75
+
76
+ next unless data.include?(key)
77
+ debug ("Key #{key} found in YAML document, Passing answer to hiera")
78
+
79
+ parsed_answer = Backend.parse_answer(data[key], scope)
80
+
81
+ begin
82
+ case resolution_type
83
+ when :array
84
+ debug("Appending answer array")
85
+ raise Exception, "Hiera type mismatch: expected Array and got #{parsed_answer.class}" unless parsed_answer.kind_of? Array or parsed_answer.kind_of? String
86
+ answer ||= []
87
+ answer << parsed_answer
88
+ when :hash
89
+ debug("Merging answer hash")
90
+ raise Exception, "Hiera type mismatch: expected Hash and got #{parsed_answer.class}" unless parsed_answer.kind_of? Hash
91
+ answer ||= {}
92
+ answer = parsed_answer.merge answer
93
+ else
94
+ debug("Assigning answer variable")
95
+ answer = parsed_answer
96
+ break
97
+ end
98
+ rescue NoMethodError
99
+ raise Exception, "Resolution type is #{resolution_type} but parsed_answer is a #{parsed_answer.class}"
100
+ end
101
+
102
+ end
103
+ return answer
104
+ end
105
+
106
+ def decrypt(cipher, gnupghome)
107
+
108
+ gnupghome_backup = ENV["GNUPGHOME"]
109
+ ENV["GNUPGHOME"] = gnupghome
110
+ debug("GNUPGHOME is #{ENV['GNUPGHOME']}")
111
+
112
+ ctx = GPGME::Ctx.new
113
+
114
+ if !ctx.keys.empty?
115
+ raw = GPGME::Data.new(cipher)
116
+ txt = GPGME::Data.new
117
+
118
+ begin
119
+ txt = ctx.decrypt(raw)
120
+ rescue GPGME::Error::DecryptFailed
121
+ warn("Warning: GPG Decryption failed, check your GPG settings")
122
+ rescue
123
+ warn("Warning: General exception decrypting GPG file")
124
+ ensure
125
+ ENV["GNUPGHOME"] = gnupghome_backup
126
+ end
127
+
128
+ txt.seek 0
129
+ result = txt.read
130
+
131
+ debug("result is a #{result.class} ctx #{ctx} txt #{txt}")
132
+ return result
133
+ else
134
+ warn("No usable keys found in #{gnupghome}. Check :key_dir value in hiera.yaml is correct")
135
+ nil
136
+ end
137
+ end
138
+
139
+ def stale?(gpgfile)
140
+ # NOTE: The mtime change in a file MUST be > 1 second before being
141
+ # recognized as stale. File mtime changes within 1 second will
142
+ # not be recognized.
143
+ stat = File.stat(gpgfile)
144
+ current = { 'inode' => stat.ino, 'mtime' => stat.mtime, 'size' => stat.size }
145
+ return false if @cache[gpgfile] == current
146
+
147
+ @cache[gpgfile] = current
148
+ return true
149
+ end
150
+ end
151
+ end
152
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hiera-housekeeper
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.5'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Neil Houston
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: hiera
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: hiera-gpg
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.1.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.1.0
46
+ description: Hiera backend for fetching encrypted data from housekeeper
47
+ email: neil.a.houston@gmail.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - lib/hiera/backend/housekeeper_backend.rb
53
+ homepage: http://github.com/ConnectedHomes/housekeeper
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.23
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Housekeeper backend for Hiera
77
+ test_files: []