vagrant-zanzibar 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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