puppet-crypt 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.rvmrc +1 -0
- data/.travis.yml +21 -0
- data/ChangeLog.md +36 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +16 -0
- data/Modulefile +13 -0
- data/README.md +172 -0
- data/Rakefile +12 -0
- data/bundles/puppet_2_7.gemfile +6 -0
- data/bundles/puppet_3_0.gemfile +5 -0
- data/bundles/puppet_edge.gemfile +5 -0
- data/features/fixtures/data/overridden_secret_key.yaml +4 -0
- data/features/fixtures/data/simple.yaml +2 -0
- data/features/fixtures/hiera.yaml +8 -0
- data/features/fixtures/manifests/overridden_secret_key.pp.bak +5 -0
- data/features/fixtures/manifests/simple.pp +6 -0
- data/features/fixtures/other_secretkeys/secondary_key +11 -0
- data/features/fixtures/secretkeys/alt_key +1 -0
- data/features/fixtures/secretkeys/encryptor_secret_key +11 -0
- data/features/hiera.feature +56 -0
- data/features/step_definitions/puppet_steps.rb +31 -0
- data/features/support/env.rb +10 -0
- data/lib/puppet-decrypt.rb +13 -0
- data/lib/puppet-decrypt/decryptor.rb +99 -0
- data/lib/puppet-decrypt/key_loader.rb +28 -0
- data/lib/puppet-decrypt/version.rb +5 -0
- data/lib/puppet/application/crypt.rb +5 -0
- data/lib/puppet/face/crypt.rb +63 -0
- data/lib/puppet/functions/decrypt.rb +24 -0
- data/lib/puppet/functions/encrypt.rb +44 -0
- data/puppet-crypt.gemspec +36 -0
- data/spec/faces/crypt_spec.rb +67 -0
- data/spec/functions/decrypt_spec.rb +53 -0
- data/spec/functions/encrypt_spec.rb +63 -0
- data/spec/puppet-decrypt/fake_key_loader.rb +24 -0
- data/spec/spec_helper.rb +24 -0
- metadata +187 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0514f4e010774d12e7bdff47d66954b0c7f0211b814194788db5bf4bcbf73196
|
4
|
+
data.tar.gz: c8e4ce1fa1ad3de7e69545560e63a44318eb5fdb15b74a0ec17cd5e5b5efc186
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9b2cc0f2a8cef27949f15888b88a6a0ea25d29e35ce15fad0aad98942bb43704f2bd3f1607fc41d8121ac03729b73a94f1e0e01c1615472dd31bdd6e390dda4f
|
7
|
+
data.tar.gz: c9b1df191e25a392521f2f337894a6c0f2231655699d04c32a8ee77029632dd9b3d2fe26f8b3a8a235a1ded503de993b5d561cc3b1b54d2660c3b967ba376733
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use --create 1.9.3@puppet-decrypt
|
data/.travis.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
language: ruby
|
2
|
+
bundler_args: --without debugging
|
3
|
+
rvm:
|
4
|
+
- 1.8.7
|
5
|
+
- 1.9.3
|
6
|
+
- jruby-19mode
|
7
|
+
- 2.0.0
|
8
|
+
- 2.1.0
|
9
|
+
gemfile:
|
10
|
+
- Gemfile
|
11
|
+
- bundles/puppet_3_0.gemfile
|
12
|
+
- bundles/puppet_2_7.gemfile
|
13
|
+
- bundles/puppet_edge.gemfile
|
14
|
+
matrix:
|
15
|
+
exclude:
|
16
|
+
- rvm: 1.8.7
|
17
|
+
gemfile: bundles/puppet_edge.gemfile
|
18
|
+
- rvm: 2.1.0
|
19
|
+
gemfile: bundles/puppet_3_0.gemfile
|
20
|
+
- rvm: 2.1.0
|
21
|
+
gemfile: bundles/puppet_2_7.gemfile
|
data/ChangeLog.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
## 0.2.0 (Aug 07, 2014)
|
2
|
+
|
3
|
+
Enhancements:
|
4
|
+
- Added an encrypt function (thanks @tioteath!)
|
5
|
+
|
6
|
+
Bugfixes:
|
7
|
+
- Fixed handling of absolute paths for to the secret key file (thanks @tioteath!)
|
8
|
+
- Removed unnecessary puts (thanks @tioteath!)
|
9
|
+
- Improved error handling (thanks @tioteath!)
|
10
|
+
|
11
|
+
## 0.1.1 (Jan 24, 2014)
|
12
|
+
|
13
|
+
Enhancements:
|
14
|
+
- Salting to protect against dictionary and rainbow table attacks.
|
15
|
+
|
16
|
+
Bugfixes:
|
17
|
+
- Fix Ruby 1.8.7 support.
|
18
|
+
|
19
|
+
## 0.1.0 (July 10, 2013)
|
20
|
+
|
21
|
+
Features:
|
22
|
+
|
23
|
+
- support alternate secret keys
|
24
|
+
- override the key as part of the string in the format ENC:another_key[...]
|
25
|
+
- override the key by passing a hash containing value and secret_key
|
26
|
+
|
27
|
+
Bugfixes:
|
28
|
+
|
29
|
+
- Explicitly remove Ruby 1.8.7 support. Previously it would install but not work with Ruby 1.8.7.
|
30
|
+
|
31
|
+
## 0.0.4 (March 8, 2013)
|
32
|
+
|
33
|
+
Features:
|
34
|
+
|
35
|
+
- puppet face for encrypting/decrypting secrets
|
36
|
+
- basic puppet function for decrypting a secret, using a master key
|
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem "puppet-crypt", :git => "https://github.com/wgsateam/puppet-decrypt"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in puppet-crypt.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
# Not in the gemspec because we're testing multiple versions with appraisal.
|
9
|
+
gem 'puppet'
|
10
|
+
|
11
|
+
# Things we don't want on Travis
|
12
|
+
group :debugging do
|
13
|
+
# just for pushing documentation, requires ruby 1.9+
|
14
|
+
gem 'relish'
|
15
|
+
gem 'pry'
|
16
|
+
gem 'pry-nav'
|
17
|
+
end
|
18
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Puppet-Decrypt
|
2
|
+
|
3
|
+
Copyright (c) 2013 mlincoln
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
16
|
+
|
data/Modulefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
name 'devopsy/puppet_decrypt'
|
2
|
+
version '0.1.1'
|
3
|
+
source 'https://github.com/maxlinc/puppet-decrypt'
|
4
|
+
author 'Max Lincoln'
|
5
|
+
license 'Apache License, Version 2.0'
|
6
|
+
summary 'Simple encryption/decryption of secret data for Puppet'
|
7
|
+
description 'Simple encryption/decryption of secret data for Puppet'
|
8
|
+
project_page 'https://github.com/maxlinc/puppet-decrypt'
|
9
|
+
|
10
|
+
## Add dependencies, if any:
|
11
|
+
# don't think gem dependencies can be declared...
|
12
|
+
# dependency gem 'encryptor'
|
13
|
+
|
data/README.md
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*
|
2
|
+
|
3
|
+
- [Puppet-Decrypt](#puppet-decrypt)
|
4
|
+
- [Comparison with Hiera-GPG](#comparison-with-hiera-gpg)
|
5
|
+
- [Installation](#installation)
|
6
|
+
- [Via Gem](#via-gem)
|
7
|
+
- [Installing as a Module](#installing-as-a-module)
|
8
|
+
- [Usage](#usage)
|
9
|
+
- [Basic Usage](#basic-usage)
|
10
|
+
- [Overriding the secret key](#overriding-the-secret-key)
|
11
|
+
- [Contributing](#contributing)
|
12
|
+
|
13
|
+
[![Build Status](https://secure.travis-ci.org/maxlinc/puppet-decrypt.png?branch=master)](http://travis-ci.org/maxlinc/puppet-decrypt)
|
14
|
+
[![Dependency Status](https://gemnasium.com/maxlinc/puppet-decrypt.png?travis)](https://gemnasium.com/maxlinc/puppet-decrypt)
|
15
|
+
[![Code Climate](https://codeclimate.com/github/maxlinc/puppet-decrypt.png)](https://codeclimate.com/github/maxlinc/puppet-decrypt)
|
16
|
+
|
17
|
+
# Puppet-Decrypt
|
18
|
+
|
19
|
+
*Notice: The default secret key location is now /etc/puppet-decrypt/encryptor_secret_key*
|
20
|
+
|
21
|
+
Puppet Decrypt is a gem that gives puppet the ability to encrypt and decrypt strings. This is useful for making sure secret data - like database passwords - remains secret. It uses a model similar to [jasypt Encrypting Application Configuration Files](http://www.jasypt.org/encrypting-configuration.html) or [Maven Password Encryption](http://maven.apache.org/guides/mini/guide-encryption.html). It is a simple alternative to the [Secret variables in Puppet with Hiera and GPG](http://www.craigdunn.org/2011/10/secret-variables-in-puppet-with-hiera-and-gpg/) approach.
|
22
|
+
|
23
|
+
## Comparison with Hiera-GPG
|
24
|
+
|
25
|
+
Advantages:
|
26
|
+
|
27
|
+
* Store encrypted secret variables and related non-secret variables in the same file.
|
28
|
+
* Version control friendly - you can easily see when a variable was added, changed, or removed even if you don't know the exact value.
|
29
|
+
* Works with any data source. Combine it with Hiera, extlookup, external node classifiers, exported resources, or any other source of data.
|
30
|
+
* Simple administration - no keyring management.
|
31
|
+
|
32
|
+
Disadvantages:
|
33
|
+
|
34
|
+
* Uses a shared secret instead of asymetric keypairs. This means that the "master password" is shared by all admins. It is also shared by all machines that need to decrypt the same value (usually machines in the same environment).
|
35
|
+
* Less integrated with Hiera. You need to wrap calls as decrypt(hiera('my_db_password')).
|
36
|
+
|
37
|
+
The shared secret "master password" may seem more difficult to grant and revoke access than the asymetric keypair approach used by hiera-gpg. However both systems are protecting shared secret data! So if you want to fully revoke someone's access you need to change or revoke their decryption key (which is easier with hiera-gpg) *AND* to change any secrets that key protected (e.g.: your production database passwords).
|
38
|
+
|
39
|
+
## Installation
|
40
|
+
|
41
|
+
### Via Gem
|
42
|
+
|
43
|
+
It should be possible to install via a Gem in Puppet 3+. However, this method is easier, but does seem to have some quirks. If you have any issues, try installing as a module instead.
|
44
|
+
|
45
|
+
Add this line to your application's Gemfile:
|
46
|
+
|
47
|
+
gem 'puppet-decrypt'
|
48
|
+
|
49
|
+
And then execute:
|
50
|
+
|
51
|
+
$ bundle
|
52
|
+
|
53
|
+
Or install it yourself as:
|
54
|
+
|
55
|
+
$ gem install puppet-decrypt
|
56
|
+
|
57
|
+
### Installing as a Module
|
58
|
+
|
59
|
+
If installing as a module, you'll need to deal with the Gem prerequisites manually.
|
60
|
+
|
61
|
+
``` shell
|
62
|
+
$ gem install encryptor
|
63
|
+
```
|
64
|
+
|
65
|
+
Puppet Decrypt can be installed with the puppet module subcommand, which is included in Puppet 2.7.14 and later.
|
66
|
+
|
67
|
+
``` shell
|
68
|
+
$ sudo puppet module install devopsy-puppet_decrypt
|
69
|
+
```
|
70
|
+
The command will tell you where it is installing the module; take note:
|
71
|
+
|
72
|
+
``` shell
|
73
|
+
$ sudo puppet module install devopsy/puppet_decrypt
|
74
|
+
warning: iconv couldn't be loaded, which is required for UTF-8/UTF-16 conversions
|
75
|
+
Preparing to install into /etc/puppet/modules ...
|
76
|
+
Downloading from http://forge.puppetlabs.com ...
|
77
|
+
Installing -- do not interrupt ...
|
78
|
+
/etc/puppet/modules
|
79
|
+
└── devopsy-puppet_decrypt (v0.1.0)
|
80
|
+
```
|
81
|
+
|
82
|
+
After installing it, you must add the lib directory of the module to your $RUBYLIB. Add the following to your .profile file (replacing /etc/puppet/modules with the directory from the install command, if necessary), then run source ~/.profile to re-load it in the current shell:
|
83
|
+
|
84
|
+
``` shell
|
85
|
+
export RUBYLIB=/etc/puppet/modules/puppet_decrypt/lib:$RUBYLIB
|
86
|
+
```
|
87
|
+
|
88
|
+
You can verify that it is installed and usable by running:
|
89
|
+
|
90
|
+
``` shell
|
91
|
+
# puppet help crypt
|
92
|
+
```
|
93
|
+
|
94
|
+
## Usage
|
95
|
+
|
96
|
+
### Basic Usage
|
97
|
+
Put the secret key in /etc/puppet-decrypt/encryptor_secret_key on machines where puppet needs to decrypt the value. Make sure the file's read permissions are restricted!
|
98
|
+
|
99
|
+
Use the puppet face to encrypt a value
|
100
|
+
|
101
|
+
``` shell
|
102
|
+
$ puppet crypt encrypt my_secret_value
|
103
|
+
ENC[ANN3I3AWxXWmr5QAW3qgxw==]
|
104
|
+
```
|
105
|
+
|
106
|
+
Or to decrypt a value
|
107
|
+
``` shell
|
108
|
+
$ puppet crypt decrypt ENC[ANN3I3AWxXWmr5QAW3qgxw==]
|
109
|
+
my_secret_value
|
110
|
+
```
|
111
|
+
|
112
|
+
Put that value into hiera, extlookup, or any other data source you want. Hiera example:
|
113
|
+
``` yaml
|
114
|
+
database_password: ENC[ANN3I3AWxXWmr5QAW3qgxw==]
|
115
|
+
```
|
116
|
+
|
117
|
+
In your puppet code, load the value normally and then pass it to decrypt.
|
118
|
+
``` ruby
|
119
|
+
decrypt(hiera('database_password'))
|
120
|
+
```
|
121
|
+
|
122
|
+
Or encrypt a secret passing it to encrypt, if you need to save the secret to
|
123
|
+
external storage, for example.
|
124
|
+
``` ruby
|
125
|
+
$encrypted_secret_to_save = encrypt($data)
|
126
|
+
```
|
127
|
+
|
128
|
+
### Overriding the secret key
|
129
|
+
|
130
|
+
Puppet Decrypt now supports using more than just the default secret key location. You can easily use multiple secret keys for the same project.
|
131
|
+
|
132
|
+
For the Puppet face, you just use the --secretkey option to pass an alternate secret key location.
|
133
|
+
|
134
|
+
``` shell
|
135
|
+
$ echo 'example' > alt_key.txt
|
136
|
+
$ puppet crypt encrypt abc123 --secretkey alt_key
|
137
|
+
ENC[c4S4hMCDv1b7FkZgOBRTOA==]
|
138
|
+
$ puppet crypt decrypt ENC[c4S4hMCDv1b7FkZgOBRTOA==] --secretkey alt_key
|
139
|
+
abc123
|
140
|
+
```
|
141
|
+
|
142
|
+
There are two ways to use the alternate keys from the function. You can pass it it as part of the string in this format:
|
143
|
+
``` yaml
|
144
|
+
database_password: ENC:alt_key[c4S4hMCDv1b7FkZgOBRTOA==]
|
145
|
+
```
|
146
|
+
|
147
|
+
If you do that, instead of using the default secret key, it will look for alt_key in the same directory. So instead of
|
148
|
+
/etc/puppet-decrypt/encryptor_secret_key it will use /etc/puppet-decrypt/alt_key.
|
149
|
+
|
150
|
+
Alternately, you can pass a hash to the function, containing a value and a secret key, like this:
|
151
|
+
``` yaml
|
152
|
+
db_password:
|
153
|
+
value: 'ENC[G6MjBDDFcapYLaKBFJvPSg==]'
|
154
|
+
secretkey: '/any/path/you/another/key'
|
155
|
+
```
|
156
|
+
|
157
|
+
This has the advantage of letting you place keys in alternate directories, not just /etc/puppet-decrypt.
|
158
|
+
|
159
|
+
If you use this alongside hiera, you can just switch the lookup from hiera to hiera-hash:
|
160
|
+
``` ruby
|
161
|
+
decrypt(hiera_hash('db_password'))
|
162
|
+
```
|
163
|
+
|
164
|
+
See [features/hiera.feature](features/hiera.feature) for complete examples.
|
165
|
+
|
166
|
+
## Contributing
|
167
|
+
|
168
|
+
1. Fork it
|
169
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
170
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
171
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
172
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'cucumber'
|
4
|
+
require 'cucumber/rake/task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
task :default => [:spec, :integration]
|
9
|
+
|
10
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
11
|
+
t.cucumber_opts = "features --format pretty"
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
1ExtSQ4kg4a4rsp7J0RlquK5Y+h5b4TeEzHX24sDxURY4LLX6J5bwTZueg1PBhiI
|
2
|
+
EenV11LGhRvUDrw53dG7j+UuMxGJv/R2cdnGxq1ztjN1ozp8xSRUTcbzpd9uijLe
|
3
|
+
FhEZHVjAIzBNj9RLl13Dw5zVPvxRuPcQBsnRIyG8YMMCgZMgZ90yBMhJmp5M3hw/
|
4
|
+
q88nDZGlZeclkIg+9QTtjywz4crvHpnQrC1ViyaIVT+lZfHPMg4TipXlDXPrlFea
|
5
|
+
x/X7v/EYXvSvY6gSVs7/ZAfFnC1K30nCaPnAbIiwsdjh7t3dV7ymHY/TvXfZQHGO
|
6
|
+
BXMFu9jdJl+nptZZp/4x4xb569s0do1Y/Z6DHqeIi31pS4hwjw8j6zHWpPrnTgRw
|
7
|
+
vcv0Di3iCjFAbTbSadgFuuybUCeZQArRYO0J03IKHNqW9UWim7tN08LOmvGuoQKf
|
8
|
+
6IAhKT0Yc4y1Mk6jtDsPlkxJvdVzTRlv07FnWWi3R2kkLWqdeFz6YB8PDCSLMXL0
|
9
|
+
qNDDOfv1MuUfKlGgqigIkbMpbqd6KUU1qLQ0js1RTq9iO6XR8wHIXqKX5r0Mp0D6
|
10
|
+
aMO1a7gvjHJFqr3nkeZvDAbGeJMJtx99OrfuUH8FImvJZ22jOP4plcC1U1u2VZGp
|
11
|
+
Rx2AoLYyiWuzP948FmvruvXMn7WxvDrU7N/wF52L3/Q=
|
@@ -0,0 +1 @@
|
|
1
|
+
example
|
@@ -0,0 +1,11 @@
|
|
1
|
+
idZdWf3d7s97FVG0iytPj03c2LhrumdUqk4T+aa1VVzp8vFmtQb7Vyy7SbjV4v4y
|
2
|
+
CLEat8x8jFb7IBl2yjW3KmC0TW1q1cKaeP8eHuYeqm9ZIQoUtDH+q+8KM5xso358
|
3
|
+
PAkDtxJcFmcRvD2SL47WYTd+xFjYEW5VxWhGCcNi/FRlmRSLL29sBcRGfJNhPBit
|
4
|
+
j3PiXxPrNIu4E1Ikm94C911x4YD/PO5yNOuhDQW5rs1z8G8TGmzXV64Vxg7rYcXc
|
5
|
+
8XK0auzDm+qTdSpP6vaLhFROcGgFVzFu5WWHZ0dpRL1UfDdjlQbHojRCfb4jZB+I
|
6
|
+
bXTS5BQFzClttcPYSwf44am3zwYCuLeZ5oWif2sxPBwUIubzPIoBObcdj/uIjljx
|
7
|
+
BBPcoPDW+zIbghVWxjoHkLinRdqtXQzMCS0pp8znRsT7v5mp7FZvzMLDO6belz+Q
|
8
|
+
LEd3YefLkOFU6B7kJq7XDrVP18G691SwhY08rSq0LyNlwEZ4E9ZD6hrUduHd3Bs7
|
9
|
+
yGNhGjvAEItfDv7MBcavkLZSO/q1QElbz5B7/3aiu2HPRfUmS/hiywJ3S1cxZp8Y
|
10
|
+
wVr0kqIxRxMoNfl4IqCaRsvlwGBeTHmpXqkcviQsQOuddMStGXKKptrXKxvhn4ca
|
11
|
+
72SUKYg7mvyCWD44mAxJyTfTso93Pkn95klRtyBunu0=
|
@@ -0,0 +1,56 @@
|
|
1
|
+
Feature: Puppet works
|
2
|
+
|
3
|
+
Scenario: Unsalted (legacy) key
|
4
|
+
Given I have the following hiera data:
|
5
|
+
"""
|
6
|
+
---
|
7
|
+
db_password: ENC[wx0qTorBqPrTrgkbzYirlA==]
|
8
|
+
"""
|
9
|
+
When I execute this puppet manifest:
|
10
|
+
"""
|
11
|
+
$password = decrypt(hiera('db_password'))
|
12
|
+
notice("Decrypted: $password")
|
13
|
+
"""
|
14
|
+
Then the output should include "Decrypted: max"
|
15
|
+
|
16
|
+
Scenario: Default test
|
17
|
+
Given I have the following hiera data:
|
18
|
+
"""
|
19
|
+
---
|
20
|
+
db_password: ENC[HOz0/aHCjJTAUlEbM/pqMQ==:QZy2oTvQNhwFMmOARn+Jlw==:aUY1NjBqamp6RWs1UkYvVjVULzNvdz09]
|
21
|
+
"""
|
22
|
+
When I execute this puppet manifest:
|
23
|
+
"""
|
24
|
+
$password = decrypt(hiera('db_password'))
|
25
|
+
notice("Decrypted: $password")
|
26
|
+
"""
|
27
|
+
Then the output should include "Decrypted: max"
|
28
|
+
|
29
|
+
Scenario: Overriden key (string)
|
30
|
+
Given I have the following hiera data:
|
31
|
+
"""
|
32
|
+
---
|
33
|
+
db_password: ENC:alt_key[KgLJnDVF9VeTGGU/vG2KjQ==:NiLhgUn4JL07DI9trGSK8g==:YlVhZDhDSEZsSDV6RnBOdm1FMmVtQT09]
|
34
|
+
"""
|
35
|
+
When I execute this puppet manifest:
|
36
|
+
"""
|
37
|
+
$password = decrypt(hiera('db_password'))
|
38
|
+
notice("Decrypted: $password")
|
39
|
+
"""
|
40
|
+
Then the output should include "Decrypted: abc123"
|
41
|
+
|
42
|
+
Scenario: Overridden key (hash)
|
43
|
+
Given I have the following hiera data:
|
44
|
+
"""
|
45
|
+
---
|
46
|
+
db_password:
|
47
|
+
value: 'ENC[AVdi08NXUveKStMSAH4kMQ==:EAHeMe3TvK33gjnDDHV5rQ==:cndoVVBhMWdXQW5HVSsxWDN4OUtRZz09]'
|
48
|
+
secretkey: 'features/fixtures/other_secretkeys/secondary_key'
|
49
|
+
"""
|
50
|
+
When I execute this puppet manifest:
|
51
|
+
"""
|
52
|
+
notice(hiera_hash('db_password'))
|
53
|
+
$password = decrypt(hiera_hash('db_password'))
|
54
|
+
notice("Decrypted: $password")
|
55
|
+
"""
|
56
|
+
Then the output should include "Decrypted: overridden"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
Given /^I have the following hiera data:$/ do |hieradata|
|
4
|
+
hierafile = Thread.current[:hierafile]
|
5
|
+
hierafile.write(hieradata)
|
6
|
+
hierafile.close
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I execute this puppet manifest:$/ do |manifest|
|
10
|
+
hierafile = Thread.current[:hierafile]
|
11
|
+
file = Tempfile.new('test_manifest')
|
12
|
+
begin
|
13
|
+
file.write(manifest)
|
14
|
+
file.close
|
15
|
+
ENV['FACTER_HIERA_FILE'] = File.basename(hierafile.path, '.yaml')
|
16
|
+
ENV['PUPPET_DECRYPT_KEYDIR'] = 'features/fixtures/secretkeys'
|
17
|
+
puppet_version = `bundle exec puppet --version`
|
18
|
+
puppet_command = "bundle exec puppet apply --noop #{file.path}"
|
19
|
+
puppet_command = "#{puppet_command} --hiera_config=features/fixtures/hiera.yaml" if puppet_version.match /^3/
|
20
|
+
puppet_command = "#{puppet_command} --confdir=features/fixtures" if puppet_version.match /^2/
|
21
|
+
@output = `#{puppet_command}`
|
22
|
+
puts @output
|
23
|
+
ensure
|
24
|
+
file.unlink
|
25
|
+
end
|
26
|
+
$?.success?
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^the output should include "([^"]*)"$/ do |content|
|
30
|
+
@output.should include content
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'puppet-decrypt/version'
|
2
|
+
require 'puppet-decrypt/key_loader'
|
3
|
+
require 'puppet-decrypt/decryptor'
|
4
|
+
require 'encryptor'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
module Puppet
|
8
|
+
module Decrypt
|
9
|
+
def self.key_loader
|
10
|
+
@key_loader ||= Puppet::Decrypt::KeyLoader.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Puppet
|
2
|
+
module Decrypt
|
3
|
+
|
4
|
+
class Decryptor
|
5
|
+
ENCRYPTED_PATTERN = /^ENC:?(\w*)\[(.*)\]$/
|
6
|
+
KEY_DIR = ENV['PUPPET_DECRYPT_KEYDIR'] || '/etc/puppet-decrypt'
|
7
|
+
DEFAULT_KEY = 'encryptor_secret_key'
|
8
|
+
DEFAULT_FILE = File.join(KEY_DIR, DEFAULT_KEY)
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@raw = options[:raw] || false
|
12
|
+
end
|
13
|
+
|
14
|
+
def decrypt_hash(hash)
|
15
|
+
decrypt(hash['value'], hash['secretkey'] || hash['secret_key'])
|
16
|
+
end
|
17
|
+
|
18
|
+
def encrypt_hash(hash)
|
19
|
+
secret_key = hash['secretkey'] || hash['secret_key'] ||
|
20
|
+
File.join(KEY_DIR, DEFAULT_KEY)
|
21
|
+
salt = hash['salt'] || SecureRandom.base64
|
22
|
+
iv = hash['iv'] || OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv
|
23
|
+
|
24
|
+
encrypt(hash['value'], secret_key, salt, iv)
|
25
|
+
end
|
26
|
+
|
27
|
+
def decrypt(value, secret_key_file)
|
28
|
+
secret_key_file ||= secret_key_for value
|
29
|
+
secret_key_digest = digest_from secret_key_file
|
30
|
+
if @raw
|
31
|
+
match = true
|
32
|
+
else
|
33
|
+
match = value.match(ENCRYPTED_PATTERN)
|
34
|
+
if match
|
35
|
+
value = match[2]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
if match
|
39
|
+
value, iv, salt = value.split(':').map{|s| strict_decode64 s }
|
40
|
+
if iv && salt
|
41
|
+
value = value.decrypt(:key => secret_key_digest, :iv => iv, :salt => salt)
|
42
|
+
else
|
43
|
+
$stderr.puts "Warning: re-encrypt with puppet-crypt to use salted passwords"
|
44
|
+
value = value.decrypt(:key => secret_key_digest)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
value
|
48
|
+
end
|
49
|
+
|
50
|
+
def encrypt(value, secret_key_file, salt, iv)
|
51
|
+
secret_key_file ||= secret_key_for value
|
52
|
+
secret_key_digest = digest_from secret_key_file
|
53
|
+
result = value.encrypt(:key => secret_key_digest, :iv => iv, :salt => salt)
|
54
|
+
encrypted_value = [result, iv, salt].map{|v| strict_encode64(v).strip }.join ':'
|
55
|
+
encrypted_value = "ENC[#{encrypted_value}]" unless @raw
|
56
|
+
raise "Value can't be encrypted properly with salt #{salt}" unless decrypt(encrypted_value, secret_key_file) == value
|
57
|
+
encrypted_value
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def load_key(secret_key_file)
|
62
|
+
Puppet::Decrypt.key_loader.load_key secret_key_file
|
63
|
+
end
|
64
|
+
|
65
|
+
def secret_key_for(value)
|
66
|
+
match = value.match(ENCRYPTED_PATTERN)
|
67
|
+
if match
|
68
|
+
key = match[1]
|
69
|
+
key = DEFAULT_KEY if key.empty?
|
70
|
+
end
|
71
|
+
key ||= DEFAULT_KEY
|
72
|
+
File.join(KEY_DIR, key)
|
73
|
+
end
|
74
|
+
|
75
|
+
def digest_from(secret_key_file)
|
76
|
+
secret_key = load_key secret_key_file
|
77
|
+
Digest::SHA256.hexdigest(secret_key)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Backported for ruby 1.8.7
|
81
|
+
def strict_decode64(str)
|
82
|
+
return Base64.strict_decode64(str) if Base64.respond_to? :strict_decode64
|
83
|
+
|
84
|
+
unless str.include?("\n")
|
85
|
+
Base64.decode64(str)
|
86
|
+
else
|
87
|
+
raise(ArgumentError,"invalid base64")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Backported for ruby 1.8.7
|
92
|
+
def strict_encode64(bin)
|
93
|
+
return Base64.strict_encode64(bin) if Base64.respond_to? :strict_encode64
|
94
|
+
Base64.encode64(bin).tr("\n",'')
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
module Puppet
|
3
|
+
module Decrypt
|
4
|
+
class KeyLoader
|
5
|
+
def load_key(secret_key_file)
|
6
|
+
|
7
|
+
# Dot not add directory if absolute path provided
|
8
|
+
if Pathname.new(secret_key_file).absolute?
|
9
|
+
full_path = secret_key_file
|
10
|
+
else
|
11
|
+
full_path = File.join(ENV['PUPPET_DECRYPT_KEYDIR'] ||
|
12
|
+
Puppet::Decrypt::Decryptor::KEY_DIR, secret_key_file)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Assume key file is specified as basename
|
16
|
+
if File.readable? full_path
|
17
|
+
secret_key_file = full_path
|
18
|
+
else
|
19
|
+
# Assume key file is specifed as full path
|
20
|
+
raise "Secret key file: #{secret_key_file} is not readable!" unless
|
21
|
+
File.readable? secret_key_file
|
22
|
+
end
|
23
|
+
|
24
|
+
File.open(secret_key_file, &:readline).chomp
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'puppet-decrypt'
|
2
|
+
require 'puppet/face'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
Puppet::Face.define(:crypt, Puppet::Decrypt::VERSION) do
|
6
|
+
copyright "Max Lincoln", 2013
|
7
|
+
license "MIT; see LICENSE"
|
8
|
+
|
9
|
+
summary "Encrypt or decrypt secret values."
|
10
|
+
description <<-EOT
|
11
|
+
This subcommand provides a command line interface to encrypt or decrypt values
|
12
|
+
that are intended for use with the puppet decrypt function.
|
13
|
+
EOT
|
14
|
+
|
15
|
+
option "--raw" do
|
16
|
+
summary "Use raw parse/display"
|
17
|
+
description <<-EOT
|
18
|
+
Parse or display the value in raw format, instead of using ENC[...] block
|
19
|
+
EOT
|
20
|
+
end
|
21
|
+
|
22
|
+
option "--secretkey SECRET_KEY_PATH" do
|
23
|
+
summary "The path to the secret key file (default: #{Puppet::Decrypt::Decryptor::DEFAULT_FILE}"
|
24
|
+
end
|
25
|
+
|
26
|
+
option "--iv IV" do
|
27
|
+
summary "The initialization vector to use during encryption (default is random)"
|
28
|
+
end
|
29
|
+
|
30
|
+
option "--salt SALT" do
|
31
|
+
summary "The salt to use during encryption (default is random)"
|
32
|
+
end
|
33
|
+
|
34
|
+
action :encrypt do
|
35
|
+
summary 'Encrypt a secret value.'
|
36
|
+
arguments "<plaintext_secret>"
|
37
|
+
description <<-EOT
|
38
|
+
This action encrypts a value using the secret key.
|
39
|
+
EOT
|
40
|
+
when_invoked do |plaintext_secret, options|
|
41
|
+
iv = options.delete(:iv) || OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv
|
42
|
+
salt = options.delete(:salt) || SecureRandom.base64
|
43
|
+
secretkey = options[:secretkey]
|
44
|
+
unless secretkey.nil?
|
45
|
+
secretkey = File.expand_path(secretkey) if secretkey.start_with? '.'
|
46
|
+
end
|
47
|
+
|
48
|
+
Puppet::Decrypt::Decryptor.new(options).encrypt(plaintext_secret, secretkey, salt, iv)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
action :decrypt do
|
53
|
+
summary 'Decrypt a secret value.'
|
54
|
+
arguments "<encrypted_secret>"
|
55
|
+
description <<-EOT
|
56
|
+
This action decrypts a value using the secret key.
|
57
|
+
EOT
|
58
|
+
when_invoked do |encrypted_secret, options|
|
59
|
+
secretkey = options[:secretkey]
|
60
|
+
Puppet::Decrypt::Decryptor.new(options).decrypt(encrypted_secret, secretkey)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'puppet-decrypt'
|
2
|
+
|
3
|
+
Puppet::Functions.create_function(:'decrypt') do
|
4
|
+
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
5
|
+
|
6
|
+
dispatch :main do
|
7
|
+
required_param 'Variant[String, Hash]', :value
|
8
|
+
required_param 'String', :secret_key
|
9
|
+
end
|
10
|
+
|
11
|
+
def main(value, secret_key)
|
12
|
+
options = {}
|
13
|
+
decrypt_args = {}
|
14
|
+
|
15
|
+
if value.is_a? String
|
16
|
+
decrypt_args['value'] = value
|
17
|
+
decrypt_args['secret_key'] = secret_key
|
18
|
+
else
|
19
|
+
decrypt_args = value
|
20
|
+
end
|
21
|
+
|
22
|
+
Puppet::Decrypt::Decryptor.new(options).decrypt_hash(decrypt_args)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'puppet-decrypt'
|
2
|
+
|
3
|
+
Puppet::Functions.create_function(:'encrypt') do
|
4
|
+
# Encrypt data, using Decryptor.
|
5
|
+
#
|
6
|
+
# This function expects four arguments:
|
7
|
+
# - Data to encrypt.
|
8
|
+
# - Secret key file path,
|
9
|
+
# Puppet::Decrypt::Decryptor::DEFAULT_KEY by default.
|
10
|
+
# Can be specified as basename in Puppet::Decrypt::Decryptor::KEY_DIR.
|
11
|
+
# - Salt (optional), randomly generated by default.
|
12
|
+
# - Initialization vector (optional), randomly generated by default,
|
13
|
+
# mainly useful for tests.
|
14
|
+
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
15
|
+
|
16
|
+
dispatch :main do
|
17
|
+
required_param 'Variant[String,Hash]', :value
|
18
|
+
required_param 'String', :secret_key
|
19
|
+
optional_param 'String', :salt
|
20
|
+
optional_param 'String', :iv
|
21
|
+
end
|
22
|
+
|
23
|
+
def main(value, secret_key, salt, iv)
|
24
|
+
encrypt_args = {}
|
25
|
+
unless salt
|
26
|
+
salt = Puppet::Util::Execution.execute("pwgen -s -1 14")
|
27
|
+
end
|
28
|
+
|
29
|
+
unless iv
|
30
|
+
iv = Puppet::Util::Execution.execute("head -c 10 /dev/random | base64")
|
31
|
+
end
|
32
|
+
|
33
|
+
if value.is_a? String
|
34
|
+
encrypt_args['value'] = value
|
35
|
+
encrypt_args['secret_key'] = secret_key
|
36
|
+
encrypt_args['salt'] = salt
|
37
|
+
encrypt_args['iv'] = iv
|
38
|
+
else
|
39
|
+
encrypt_args = value
|
40
|
+
end
|
41
|
+
|
42
|
+
Puppet::Decrypt::Decryptor.new.encrypt_hash(encrypt_args)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'puppet-decrypt/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "puppet-crypt"
|
8
|
+
gem.version = "0.2.0"
|
9
|
+
gem.authors = ["mlincoln"]
|
10
|
+
gem.email = ["max@devopsy.com"]
|
11
|
+
gem.description = %q{A gem for encrypting/decrypting secret values for use with Puppet}
|
12
|
+
gem.summary = %q{A shared secret strategy that works with any data source}
|
13
|
+
gem.homepage = "https://github.com/wgsateam/puppet-decrypt"
|
14
|
+
gem.required_ruby_version = '>= 1.8.7'
|
15
|
+
notice = """
|
16
|
+
|
17
|
+
Notice: The default master key location is now /etc/puppet-decrypt/encryptor_secret_key
|
18
|
+
|
19
|
+
This was done to more easily support multiple keys. If you are upgrading from a version older than
|
20
|
+
0.1.0 you should move /etc/encryptor_secret_key to /etc/puppet-decrypt/encryptor_secret_key.
|
21
|
+
|
22
|
+
"""
|
23
|
+
gem.post_install_message = notice
|
24
|
+
|
25
|
+
gem.files = `git ls-files`.split("\n")
|
26
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
27
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
28
|
+
gem.require_paths = ["lib"]
|
29
|
+
|
30
|
+
gem.add_dependency('encryptor')
|
31
|
+
gem.add_development_dependency('rake')
|
32
|
+
gem.add_development_dependency('cucumber')
|
33
|
+
gem.add_development_dependency('rspec', '~> 2.14.1')
|
34
|
+
gem.add_development_dependency('rspec-puppet')
|
35
|
+
gem.add_development_dependency('puppetlabs_spec_helper')
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'puppet/face'
|
4
|
+
|
5
|
+
MINIMUM_IV_LENGTH = 20
|
6
|
+
describe Puppet::Face[:crypt, :current] do
|
7
|
+
let(:insecure_opts) do
|
8
|
+
{ :salt => '1234567890', :iv => '5'*20 }
|
9
|
+
end
|
10
|
+
# Values above, encoded
|
11
|
+
let(:base64_salt) { 'MTIzNDU2Nzg5MA==' }
|
12
|
+
let(:base64_iv) { 'NTU1NTU1NTU1NTU1NTU1NTU1NTU=' }
|
13
|
+
before :all do
|
14
|
+
mock_secret_key(Puppet::Decrypt::Decryptor::DEFAULT_FILE, 'masterkey')
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'encrypt' do
|
18
|
+
describe 'should encrypt a value' do
|
19
|
+
it 'is decryptable with minimum args' do
|
20
|
+
encrypted = subject.encrypt('flabberghaster')
|
21
|
+
subject.decrypt(encrypted).should == 'flabberghaster'
|
22
|
+
end
|
23
|
+
it 'is decryptable with minimum args with a salt' do
|
24
|
+
salt = SecureRandom.base64
|
25
|
+
encrypted = subject.encrypt('flabberghaster', {:salt => salt})
|
26
|
+
subject.decrypt(encrypted).should == 'flabberghaster'
|
27
|
+
end
|
28
|
+
it 'is decryptable with problematic salt (regexp chars)' do
|
29
|
+
salt = 'R8STny+9cq03ujQGiKDd9w=='
|
30
|
+
encrypted = subject.encrypt('flabberghaster', {:salt => salt})
|
31
|
+
subject.decrypt(encrypted).should == 'flabberghaster'
|
32
|
+
end
|
33
|
+
it 'with ENC[...]' do
|
34
|
+
expected_value = "ENC[7u523Z+PpqSm58+BeiN4qw==:#{base64_iv}:#{base64_salt}]"
|
35
|
+
subject.encrypt('flabberghaster', insecure_opts).should == expected_value
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'with --raw' do
|
39
|
+
expected_value = "7u523Z+PpqSm58+BeiN4qw==:#{base64_iv}:#{base64_salt}"
|
40
|
+
subject.encrypt('flabberghaster', {:raw => true}.merge(insecure_opts)).should == expected_value
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'with --secretkey' do
|
44
|
+
mock_secret_key('/etc/another_key', 'anotherkey')
|
45
|
+
expected_value = "ENC[81crlXmuzSnld3+4YUkQYg==:#{base64_iv}:#{base64_salt}]"
|
46
|
+
subject.encrypt('flabberghaster', {:secretkey => '/etc/another_key'}.merge(insecure_opts)).should == expected_value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'decrypt' do
|
52
|
+
describe 'should decrypt a value' do
|
53
|
+
it 'with ENC[...]' do
|
54
|
+
subject.decrypt('ENC[3xzy8fiXlaJqv3m+aXIJNA==]').should == 'flabberghaster'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'with --raw' do
|
58
|
+
subject.decrypt('3xzy8fiXlaJqv3m+aXIJNA==', {:raw => true}).should == 'flabberghaster'
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'with --secretkey' do
|
62
|
+
mock_secret_key('/etc/another_key', 'anotherkey')
|
63
|
+
subject.decrypt('ENC[8MaZYHPdj9IpnzcuBLlMdg==]', {:secretkey => '/etc/another_key'}).should == 'flabberghaster'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'decrypt' do
|
5
|
+
before(:all) do
|
6
|
+
mock_secret_key(Puppet::Decrypt::Decryptor::DEFAULT_FILE, 'masterkey')
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:node) { 'testhost.example.com' }
|
10
|
+
extdata_path = File.expand_path(File.join(File.dirname(__FILE__), '../../puppet/manifests/extdata'))
|
11
|
+
let(:pre_condition) { "$extlookup_datadir = '#{extdata_path}' $extlookup_precedence = ['common', 'env_vagrant']" }
|
12
|
+
|
13
|
+
context "unencrypted key" do
|
14
|
+
it { should run.with_params('blah').and_return("blah") }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "encrypted key" do
|
18
|
+
it "should decrypt exact matches" do
|
19
|
+
should run.with_params('ENC[3xzy8fiXlaJqv3m+aXIJNA==]').and_return("flabberghaster")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should not decrypt partial matches" do
|
23
|
+
should run.with_params('fooENC[3xzy8fiXlaJqv3m+aXIJNA==]bar').and_return('fooENC[3xzy8fiXlaJqv3m+aXIJNA==]bar')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with secret key in string" do
|
28
|
+
it "should decrypt exact matches" do
|
29
|
+
mock_secret_key('/etc/puppet-decrypt/another_key', 'anotherkey')
|
30
|
+
should run.with_params('ENC:another_key[8MaZYHPdj9IpnzcuBLlMdg==]').and_return('flabberghaster')
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not decrypt partial matches" do
|
34
|
+
should run.with_params('fooENC:max[3xzy8fiXlaJqv3m+aXIJNA==]bar').and_return('fooENC:max[3xzy8fiXlaJqv3m+aXIJNA==]bar')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with secret key file" do
|
39
|
+
it "should decrypt exact matches" do
|
40
|
+
mock_secret_key('/etc/another_key', 'anotherkey')
|
41
|
+
should run.with_params({ 'value' => 'ENC[8MaZYHPdj9IpnzcuBLlMdg==]', 'secretkey' => '/etc/another_key'}).and_return('flabberghaster')
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should override a key in the string" do
|
45
|
+
mock_secret_key('/etc/another_key', 'anotherkey')
|
46
|
+
should run.with_params({ 'value' => 'ENC:max[8MaZYHPdj9IpnzcuBLlMdg==]', 'secretkey' => '/etc/another_key'}).and_return('flabberghaster')
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should not decrypt partial matches" do
|
50
|
+
should run.with_params({ 'value' => 'fooENC[3xzy8fiXlaJqv3m+aXIJNA==]bar', 'secretkey' => '/etc/another_key'}).and_return('fooENC[3xzy8fiXlaJqv3m+aXIJNA==]bar')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'encrypt' do
|
5
|
+
before(:all) do
|
6
|
+
mock_secret_key(Puppet::Decrypt::Decryptor::DEFAULT_FILE, 'masterkey')
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:node) { 'testhost.example.com' }
|
10
|
+
extdata_path = File.expand_path(File.join(File.dirname(__FILE__),
|
11
|
+
'../../puppet/manifests/extdata'))
|
12
|
+
|
13
|
+
let :pre_condition do
|
14
|
+
"$extlookup_datadir = '#{extdata_path}' $extlookup_precedence = ['common', 'env_vagrant']"
|
15
|
+
end
|
16
|
+
|
17
|
+
let :salt do
|
18
|
+
'test_salt'
|
19
|
+
end
|
20
|
+
|
21
|
+
let :iv do
|
22
|
+
"YS7nvbPsLP+pIJQ0waxV0w=="
|
23
|
+
end
|
24
|
+
|
25
|
+
context "unencrypted key" do
|
26
|
+
it "should encrypt plain string" do
|
27
|
+
subject.call(['blah']).should =~
|
28
|
+
Puppet::Decrypt::Decryptor::ENCRYPTED_PATTERN
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "encrypted key" do
|
33
|
+
it "should encrypt exact matches" do
|
34
|
+
subject.call(["flabberghaster", nil, salt, iv]).should ==
|
35
|
+
'ENC[WnA7ezNPSZx2S01T67TCfA==:WVM3bnZiUHNMUCtwSUpRMHdheFYwdz09:dGVzdF9zYWx0]'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with secret key file" do
|
40
|
+
it "should encrypt exact matches" do
|
41
|
+
mock_secret_key('/etc/another_key', 'anotherkey')
|
42
|
+
subject.call(['value' => 'flabberghaster', 'secretkey' =>
|
43
|
+
'/etc/another_key', 'iv' => iv, 'salt' => salt]) =~
|
44
|
+
Puppet::Decrypt::Decryptor::ENCRYPTED_PATTERN
|
45
|
+
|
46
|
+
_, iv_hash, salt_hash = Regexp.last_match[2].split(':')
|
47
|
+
strict_decode64(iv_hash).should == iv
|
48
|
+
strict_decode64(salt_hash).should == salt
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Backported for ruby 1.8.7
|
55
|
+
def strict_decode64(str)
|
56
|
+
return Base64.strict_decode64(str) if Base64.respond_to? :strict_decode64
|
57
|
+
|
58
|
+
if str.include?("\n")
|
59
|
+
raise(ArgumentError,"invalid base64")
|
60
|
+
else
|
61
|
+
Base64.decode64(str)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Puppet
|
2
|
+
module Decrypt
|
3
|
+
def self.key_loader=(key_loader)
|
4
|
+
@key_loader = key_loader
|
5
|
+
end
|
6
|
+
|
7
|
+
class FakeKeyLoader
|
8
|
+
def initialize
|
9
|
+
@secrets = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_secret(secret_key_file, secret_key)
|
13
|
+
@secrets[secret_key_file] = secret_key
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_key(secret_key_file)
|
17
|
+
raise "Secret key file: #{secret_key_file} is not readable!" unless @secrets.has_key? secret_key_file
|
18
|
+
secret_key = @secrets[secret_key_file]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Puppet::Decrypt.key_loader = Puppet::Decrypt::FakeKeyLoader.new
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'rspec-puppet'
|
3
|
+
require 'puppet-decrypt/fake_key_loader'
|
4
|
+
require 'puppet-decrypt'
|
5
|
+
require 'rspec/mocks'
|
6
|
+
|
7
|
+
Puppet::Decrypt.key_loader = Puppet::Decrypt::FakeKeyLoader.new
|
8
|
+
|
9
|
+
module SecretKeyHelper
|
10
|
+
def mock_secret_key(filename, secret)
|
11
|
+
Puppet::Decrypt.key_loader.add_secret(filename, secret)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec::Mocks::setup(self)
|
16
|
+
|
17
|
+
RSpec.configure do |c|
|
18
|
+
c.include SecretKeyHelper
|
19
|
+
end
|
20
|
+
|
21
|
+
if ENV['PUPPET_DEBUG']
|
22
|
+
Puppet::Util::Log.level = :debug
|
23
|
+
Puppet::Util::Log.newdestination(:console)
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puppet-crypt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- mlincoln
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: encryptor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cucumber
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.14.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-puppet
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: puppetlabs_spec_helper
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: A gem for encrypting/decrypting secret values for use with Puppet
|
98
|
+
email:
|
99
|
+
- max@devopsy.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".rvmrc"
|
107
|
+
- ".travis.yml"
|
108
|
+
- ChangeLog.md
|
109
|
+
- Gemfile
|
110
|
+
- LICENSE.txt
|
111
|
+
- Modulefile
|
112
|
+
- README.md
|
113
|
+
- Rakefile
|
114
|
+
- bundles/puppet_2_7.gemfile
|
115
|
+
- bundles/puppet_3_0.gemfile
|
116
|
+
- bundles/puppet_edge.gemfile
|
117
|
+
- features/fixtures/data/overridden_secret_key.yaml
|
118
|
+
- features/fixtures/data/simple.yaml
|
119
|
+
- features/fixtures/hiera.yaml
|
120
|
+
- features/fixtures/manifests/overridden_secret_key.pp.bak
|
121
|
+
- features/fixtures/manifests/simple.pp
|
122
|
+
- features/fixtures/other_secretkeys/secondary_key
|
123
|
+
- features/fixtures/secretkeys/alt_key
|
124
|
+
- features/fixtures/secretkeys/encryptor_secret_key
|
125
|
+
- features/hiera.feature
|
126
|
+
- features/step_definitions/puppet_steps.rb
|
127
|
+
- features/support/env.rb
|
128
|
+
- lib/puppet-decrypt.rb
|
129
|
+
- lib/puppet-decrypt/decryptor.rb
|
130
|
+
- lib/puppet-decrypt/key_loader.rb
|
131
|
+
- lib/puppet-decrypt/version.rb
|
132
|
+
- lib/puppet/application/crypt.rb
|
133
|
+
- lib/puppet/face/crypt.rb
|
134
|
+
- lib/puppet/functions/decrypt.rb
|
135
|
+
- lib/puppet/functions/encrypt.rb
|
136
|
+
- puppet-crypt.gemspec
|
137
|
+
- spec/faces/crypt_spec.rb
|
138
|
+
- spec/functions/decrypt_spec.rb
|
139
|
+
- spec/functions/encrypt_spec.rb
|
140
|
+
- spec/puppet-decrypt/fake_key_loader.rb
|
141
|
+
- spec/spec_helper.rb
|
142
|
+
homepage: https://github.com/wgsateam/puppet-decrypt
|
143
|
+
licenses: []
|
144
|
+
metadata: {}
|
145
|
+
post_install_message: |2+
|
146
|
+
|
147
|
+
|
148
|
+
Notice: The default master key location is now /etc/puppet-decrypt/encryptor_secret_key
|
149
|
+
|
150
|
+
This was done to more easily support multiple keys. If you are upgrading from a version older than
|
151
|
+
0.1.0 you should move /etc/encryptor_secret_key to /etc/puppet-decrypt/encryptor_secret_key.
|
152
|
+
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: 1.8.7
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubygems_version: 3.0.3
|
168
|
+
signing_key:
|
169
|
+
specification_version: 4
|
170
|
+
summary: A shared secret strategy that works with any data source
|
171
|
+
test_files:
|
172
|
+
- features/fixtures/data/overridden_secret_key.yaml
|
173
|
+
- features/fixtures/data/simple.yaml
|
174
|
+
- features/fixtures/hiera.yaml
|
175
|
+
- features/fixtures/manifests/overridden_secret_key.pp.bak
|
176
|
+
- features/fixtures/manifests/simple.pp
|
177
|
+
- features/fixtures/other_secretkeys/secondary_key
|
178
|
+
- features/fixtures/secretkeys/alt_key
|
179
|
+
- features/fixtures/secretkeys/encryptor_secret_key
|
180
|
+
- features/hiera.feature
|
181
|
+
- features/step_definitions/puppet_steps.rb
|
182
|
+
- features/support/env.rb
|
183
|
+
- spec/faces/crypt_spec.rb
|
184
|
+
- spec/functions/decrypt_spec.rb
|
185
|
+
- spec/functions/encrypt_spec.rb
|
186
|
+
- spec/puppet-decrypt/fake_key_loader.rb
|
187
|
+
- spec/spec_helper.rb
|