vagrant-zanzibar 0.1.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.
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec/spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ deploy:
6
+ provider: rubygems
7
+ api_key:
8
+ secure: "CFXaOgtLypVc3/Nn3NFyeTYJ3rR/KNua2FPFHc02h5K/TPm8PMlHsYEB3e9OpithnC5cLqPUUlyZL4Gz7QC4zxX0G4luNVz+ZXdueMk1GBstK9QMsrjOQMKnQTCPaK/3x2/53kuPWMjYSyn5+ICkf/Omq1EVD4YEplhSvIRA9QQ="
9
+ gem: vagrant-zanzibar
10
+ on:
11
+ tags: true
12
+ all_branches: true
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'savon', '0.9.5'
4
+
5
+ group :test do
6
+ gem 'rake'
7
+ gem 'savon_spec'
8
+ gem 'rspec'
9
+ gem 'webmock'
10
+ gem 'codeclimate-test-reporter'
11
+ gem 'vagrant-zanzibar', path: '.'
12
+ gem 'rubocop'
13
+ end
14
+
15
+ # Specify your gem's dependencies in zanzibar.gemspec
16
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015 Cimpress
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Zanzibar
2
+ [![Gem Version](https://badge.fury.io/rb/zanzibar.svg)](http://badge.fury.io/rb/zanzibar)
3
+
4
+ Zanzibar is a utility to retrieve secrets from a Secret Server installation. It supports retrieval of a password, public/private key, or secret attachment.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'zanzibar'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install zanzibar
21
+
22
+ ## Usage
23
+
24
+ In your ruby project, rakefile, etc., create a new Zanzibar object. The constructor takes a hash of optional parameters for the WSDL location, the domain of the Secret Server, a hash of global variables to pass to savon (necessary for windows environments with self-signed certs) and a password for the current user (intended to be passed in through some encryption method, unless you really want a plaintext password there.). All of these parameters are optional and the user will be prompted to enter them if they are missing.
25
+
26
+ ```ruby
27
+ my_object = Zanzibar::Zanzibar.new(:domain => 'my.domain.net', :wsdl => 'my.scrt.srvr.com/webservices/sswebservice.asmx?wdsl', :pwd => get_encrypted_password_from_somewhere)
28
+ ```
29
+
30
+ Example:
31
+
32
+ ```ruby
33
+ require 'zanzibar'
34
+
35
+ ## Constructor takes hash as argument, all optional :domain, :wsdl, :pwd, :globals
36
+ secrets = Zanzibar::Zanzibar.new(:domain => 'mydomain.net', :wsdl => "https://my.scrt.server/webservices/sswebservice.asmx?wsdl")
37
+ # On windows with self-signed certs,
38
+ # Zanzibar::Zanzibar.new(:domain => 'mydomain.net', :wsdl => "https://my.scrt.server/webservices/sswebservice.asmx?wsdl", :globals => {:ssl_verify_mode => :none})
39
+
40
+ ## Simple password -> takes secret id as argument
41
+ secrets.get_password(1234)
42
+
43
+ ## Private Key -> takes hash as argument, requires :scrt_id, :type, optional :scrt_item_id, :path
44
+ secrets.download_secret_file(:scrt_id => 2345, :path => 'secrets/', :type => "Private Key")
45
+
46
+ ## Public Key -> takes hash as argument, requires :scrt_id, :type, optional :scrt_item_id, :path
47
+ secrets.download_secret_file(:scrt_id => 2345, :path => 'secrets/', :type => "Public Key")
48
+
49
+ ## Attachment; only supports secrets with single attachment -> takes hash as argument, requires :scrt_id, :path, optional :scrt_item_id, :path
50
+ secrets.download_secret_file(:scrt_id => 2345, :path => 'secrets/', :type => "Attachment")
51
+
52
+ ```
53
+
54
+ ### Command Line
55
+
56
+ Zanzibar comes bundled with the `zanzibar` command-line utility that can be used for fetching passwords and downloading keys from outside of Ruby.
57
+
58
+ `zanzibar` supports most actions provided by Zanzibar itself. Because it operates on the command-line, it can be used as part of a pipeline or within a bash script.
59
+
60
+ ```bash
61
+ # if you don't pipe in a password, you will be prompted to enter one.
62
+ # this will download the private key from secret 1984 to the current directory
63
+ cat ./local-password | zanzibar 1984 -s server.example.com -d example.com -t privatekey
64
+
65
+ ssh user@someremote -i ./private_key
66
+ ```
67
+
68
+ ## Contributing
69
+
70
+ 1. Fork it ( https://github.com/Cimpress-MCP/zanzibar/fork )
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
73
+ 4. Push to the branch (`git push origin my-new-feature`)
74
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'bundler/setup' # load up our gem environment (incl. local zanzibar)
3
+ require 'rspec/core/rake_task'
4
+ require 'vagrant-zanzibar/version'
5
+ require 'rubocop/rake_task'
6
+
7
+ task default: [:test]
8
+
9
+ RSpec::Core::RakeTask.new(:test)
10
+
11
+ RuboCop::RakeTask.new
data/bin/zamioculcas ADDED
@@ -0,0 +1,2 @@
1
+ #! ruby
2
+ system("zanzibar #{ARGV.join(" ")}")
data/bin/zanzibar ADDED
@@ -0,0 +1,72 @@
1
+ #! ruby
2
+
3
+ require 'vagrant-zanzibar'
4
+ require 'optparse'
5
+
6
+ options = {
7
+ :domain => 'local'
8
+ }
9
+
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: zamioculcas -d domain [-w wsdl] [-k] [-p] [secret_id]"
12
+
13
+ opts.on("-d", "--domain DOMAIN", "Specify domain") do |v|
14
+ options[:domain] = v
15
+ end
16
+
17
+ opts.on("-w", "--wsdl WSDL", "Specify WSDL location") do |v|
18
+ options[:wsdl] = v
19
+ end
20
+
21
+ opts.on("-s", "--server SERVER", "Secret server hostname or IP") do |v|
22
+ options[:server] = v
23
+ end
24
+
25
+ opts.on("-k", "--no-check-certificate", "Don't run SSL certificate checks") do |v|
26
+ options[:globals] = {:ssl_verify_mode => :none}
27
+ end
28
+
29
+ opts.on("-p", "--password PASSWORD", "Specify password") do |v|
30
+ options[:pwd] = v
31
+ end
32
+
33
+ opts.on("-t", "--type TYPE", "Specify the type of secret") do |v|
34
+ options[:type] = v
35
+ end
36
+
37
+ opts.on("-u", "--user USER", "Specify the username") do |v|
38
+ options[:username] = v
39
+ end
40
+
41
+ end.parse!
42
+
43
+ raise OptionParser::MissingArgument if options[:server].nil?
44
+ options[:type] = "password" if options[:type].nil?
45
+
46
+ unless STDIN.tty? || options[:pwd]
47
+ options[:pwd] = $stdin.read.strip
48
+ end
49
+
50
+ secret_id = Integer(ARGV.pop)
51
+ if(!secret_id)
52
+ fail "no secret!"
53
+ end
54
+
55
+ unless options[:wsdl] || options[:server].nil?
56
+ options[:wsdl] = "https://#{options[:server]}/webservices/sswebservice.asmx?wsdl"
57
+ end
58
+
59
+ scrt = Zanzibar::Zanzibar.new(options)
60
+
61
+ case options[:type]
62
+ when "password"
63
+ $stdout.write "#{scrt.get_password(secret_id)}\n"
64
+ when "privatekey"
65
+ scrt.download_private_key(:scrt_id=>secret_id)
66
+ when "publickey"
67
+ scrt.download_public_key(:scrt_id=>secret_id)
68
+ when "attachment"
69
+ scrt.download_attachment(:scrt_id=>secret_id)
70
+ else
71
+ $stderr.write "#{options[:type]} is not a known type."
72
+ end
@@ -0,0 +1,213 @@
1
+ require 'vagrant-zanzibar/version'
2
+ require 'savon'
3
+ require 'io/console'
4
+ require 'fileutils'
5
+
6
+ module Zanzibar
7
+ ##
8
+ # Class for interacting with Secret Server
9
+ class Zanzibar
10
+ ##
11
+ # @param args{:domain, :wsdl, :pwd, :username, :globals{}}
12
+
13
+ def initialize(args = {})
14
+
15
+ ## Array to store checked out secrets
16
+ @@secrets = []
17
+
18
+ if args[:username]
19
+ @@username = args[:username]
20
+ else
21
+ @@username = ENV['USER']
22
+ end
23
+
24
+ if args[:wsdl]
25
+ @@wsdl = args[:wsdl]
26
+ else
27
+ @@wsdl = get_wsdl_location
28
+ end
29
+ if args[:pwd]
30
+ @@password = args[:pwd]
31
+ else
32
+ @@password = prompt_for_password
33
+ end
34
+ if args[:domain]
35
+ @@domain = args[:domain]
36
+ else
37
+ @@domain = prompt_for_domain
38
+ end
39
+ args[:globals] = {} unless args[:globals]
40
+ init_client(args[:globals])
41
+ end
42
+
43
+ ## Initializes the Savon client class variable with the wdsl document location and optional global variables
44
+ # @param globals{}, optional
45
+
46
+ def init_client(globals = {})
47
+ globals = {} if globals.nil?
48
+ Savon.configure do |config|
49
+ config.log = false
50
+ end
51
+ @@client = Savon::Client.new(@@wsdl) do
52
+ #wsdl.endpoint = "https://scrt.vistaprint.net/webservices/sswebservice.asmx"
53
+ #wsdl.namespace = "thesecretserver.com"
54
+ http.auth.ssl.verify_mode = :none
55
+ HTTPI.log = false
56
+ end
57
+ #@@client.log = false
58
+ #@@client.http.auth.ssl.verify_mode = :none
59
+ end
60
+
61
+ ## Gets the user's password if none is provided in the constructor.
62
+ # @return [String] the password for the current user
63
+
64
+ def prompt_for_password
65
+ puts "Please enter password for #{@@username}:"
66
+ STDIN.noecho(&:gets).chomp
67
+ end
68
+
69
+ ## Gets the wsdl document location if none is provided in the constructor
70
+ # @return [String] the location of the WDSL document
71
+
72
+ def prompt_for_wsdl_location
73
+ puts 'Enter the URL of the Secret Server WSDL:'
74
+ STDIN.gets.chomp
75
+ end
76
+
77
+ ## Gets the domain of the Secret Server installation if none is provided in the constructor
78
+ # @return [String] the domain of the secret server installation
79
+
80
+ def prompt_for_domain
81
+ puts 'Enter the domain of your Secret Server:'
82
+ STDIN.gets.chomp
83
+ end
84
+
85
+ ## Check in the secrets that are currently checked out
86
+ def check_in_secrets
87
+ failures = []
88
+ token = get_token
89
+ @@secrets.each do |secret_id|
90
+ begin
91
+ response = @@client.request(:wsdl, :check_in) { soap.body = { token: token, secretId: secret_id} }
92
+ .hash[:envelope][:body][:check_in_response][:check_in_result][:errors]
93
+ if !response.nil? and response[:string] != "Secret is not CheckedOut to current user."
94
+ failures << "Error checking in secret #{secret_id} : #{response[:string]}"
95
+ end
96
+ rescue Savon::Error => err
97
+ raise "There was an error checking in secret #{secret_id}: #{err}"
98
+ end
99
+ end
100
+ if !failures.empty?
101
+ failures.each do |message|
102
+ puts message
103
+ end
104
+ abort()
105
+ end
106
+ end
107
+
108
+ ## Get an authentication token for interacting with Secret Server. These are only good for about 10 minutes so just get a new one each time.
109
+ # Will raise an error if there is an issue with the authentication.
110
+ # @return the authentication token for the current user.
111
+
112
+ def get_token
113
+ response = @@client.request( :wsdl, :authenticate) { soap.body = { username: @@username, password: @@password, organization: '', domain: @@domain }}
114
+ .hash[:envelope][:body][:authenticate_response][:authenticate_result]
115
+ abort("Error generating the authentication token for user #{@@username}: #{response[:errors][:string]}") if response[:errors]
116
+ response[:token]
117
+ rescue Savon::Error => err
118
+ raise "There was an error generating the authentiaton token for user #{@@username}: #{err}"
119
+ end
120
+
121
+ ## Get a secret returned as a hash
122
+ # Will raise an error if there was an issue getting the secret
123
+ # @param [Integer] the secret id
124
+ # @return [Hash] the secret hash retrieved from the wsdl
125
+
126
+ def get_secret(scrt_id, token = nil)
127
+ token = get_token if token.nil?
128
+ secret = @@client.request(:wsdl, :get_secret) { soap.body = { token: token, secretId: scrt_id } }.hash[:envelope][:body][:get_secret_response][:get_secret_result]
129
+ abort("There was an error getting secret #{scrt_id}: #{secret[:errors][:string]}") if secret[:errors]
130
+ @@secrets << scrt_id
131
+ return secret
132
+ rescue Savon::Error => err
133
+ raise "There was an error getting the secret with id #{scrt_id}: #{err}"
134
+ end
135
+
136
+ ## Retrieve a simple password from a secret
137
+ # Will raise an error if there are any issues
138
+ # @param [Integer] the secret id
139
+ # @return [String] the password for the given secret
140
+
141
+ def get_password(scrt_id)
142
+ secret = get_secret(scrt_id)
143
+ secret_items = secret[:secret][:items][:secret_item]
144
+ return get_secret_item_by_field_name(secret_items, 'Password')[:value]
145
+ rescue Savon::Error => err
146
+ raise "There was an error getting the password for secret #{scrt_id}: #{err}"
147
+ end
148
+
149
+ def write_secret_to_file(path, secret_response)
150
+ File.open(File.join(path, secret_response[:file_name]), 'wb') do |file|
151
+ file.puts Base64.decode64(secret_response[:file_attachment])
152
+ end
153
+ end
154
+
155
+ def get_secret_item_by_field_name(secret_items, field_name)
156
+ secret_items.each do |item|
157
+ return item if item[:field_name] == field_name
158
+ end
159
+ end
160
+
161
+ ## Get the secret item id that relates to a key file or attachment.
162
+ # Will raise on error
163
+ # @param [Integer] the secret id
164
+ # @param [String] the type of secret item to get, one of privatekey, publickey, attachment
165
+ # @return [Integer] the secret item id
166
+
167
+ def get_scrt_item_id(scrt_id, type, token)
168
+ secret = get_secret(scrt_id, token)
169
+ secret_items = secret[:secret][:items][:secret_item]
170
+ begin
171
+ return get_secret_item_by_field_name(secret_items, type)[:id]
172
+ rescue
173
+ raise "Unknown type, #{type}."
174
+ end
175
+ end
176
+
177
+ ## Downloads a file for a secret and places it where Zanzibar is running, or :path if specified
178
+ # Raise on error
179
+ # @param [Hash] args, :scrt_id, :type (one of "Private Key", "Public Key", "Attachment"), :scrt_item_id - optional, :path - optional
180
+
181
+ def download_secret_file(args = {})
182
+ token = get_token
183
+ scrt_item_id = get_scrt_item_id(args[:scrt_id], args[:type], token)
184
+ FileUtils.mkdir_p(args[:path]) if args[:path]
185
+ path = args[:path] ? args[:path] : '.' ## The File.join below doesn't handle nils well, so let's take that possibility away.
186
+ begin
187
+ response = @@client.request(:wsdl, :download_file_attachment_by_item_id){ soap.body =
188
+ { token: token, secretId: args[:scrt_id], secretItemId: scrt_item_id }}
189
+ .hash[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result]
190
+ abort("There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{response[:errors][:string]}") if response[:errors]
191
+ write_secret_to_file(path, response)
192
+ rescue Savon::Error => err
193
+ raise "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{err}"
194
+ end
195
+ end
196
+
197
+ ## Methods to maintain backwards compatibility
198
+ def download_private_key(args = {})
199
+ args[:type] = 'Private Key'
200
+ download_secret_file(args)
201
+ end
202
+
203
+ def download_public_key(args = {})
204
+ args[:type] = 'Public Key'
205
+ download_secret_file(args)
206
+ end
207
+
208
+ def download_attachment(args = {})
209
+ args[:type] = 'Attachment'
210
+ download_secret_file(args)
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,3 @@
1
+ module Zanzibar
2
+ VERSION = '0.1.1'
3
+ end