hiera-housekeeper 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/hiera/backend/housekeeper_backend.rb +152 -0
- metadata +77 -0
@@ -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: []
|