conjur-cli 4.28.2 → 4.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +8 -0
  3. data/.gitignore +2 -0
  4. data/.overcommit.yml +10 -0
  5. data/.rubocop.yml +14 -0
  6. data/CHANGELOG.md +16 -0
  7. data/Dockerfile +10 -0
  8. data/Gemfile +2 -0
  9. data/Rakefile +1 -1
  10. data/acceptance-features/audit/audit_event_send.feature +46 -43
  11. data/acceptance-features/audit/send.feature +0 -19
  12. data/acceptance-features/authentication/login.feature +0 -2
  13. data/acceptance-features/authentication/logout.feature +0 -3
  14. data/acceptance-features/authorization/resource/check.feature +6 -4
  15. data/acceptance-features/authorization/resource/create.feature +4 -2
  16. data/acceptance-features/authorization/resource/exists.feature +8 -6
  17. data/acceptance-features/authorization/resource/give.feature +3 -1
  18. data/acceptance-features/authorization/resource/show.feature +3 -1
  19. data/acceptance-features/authorization/role/graph.feature +0 -1
  20. data/acceptance-features/conjurenv/check.feature +3 -10
  21. data/acceptance-features/conjurenv/run.feature +3 -3
  22. data/acceptance-features/conjurenv/template.feature +1 -1
  23. data/acceptance-features/directory/hostfactory/create.feature +13 -0
  24. data/acceptance-features/directory/hostfactory/tokens.feature +16 -0
  25. data/acceptance-features/directory/layer/retire.feature +43 -0
  26. data/acceptance-features/directory/user/update_password.feature +0 -1
  27. data/acceptance-features/directory/variable/value.feature +3 -2
  28. data/acceptance-features/dsl/policy_owner.feature +21 -7
  29. data/acceptance-features/dsl/resource_owner.feature +4 -4
  30. data/acceptance-features/pubkeys/add.feature +4 -2
  31. data/acceptance-features/pubkeys/names.feature +6 -3
  32. data/acceptance-features/pubkeys/show.feature +4 -2
  33. data/acceptance-features/step_definitions/{cli.rb → cli_steps.rb} +18 -4
  34. data/acceptance-features/step_definitions/user_steps.rb +13 -12
  35. data/acceptance-features/support/env.rb +0 -1
  36. data/acceptance-features/support/hooks.rb +11 -14
  37. data/acceptance-features/support/world.rb +16 -18
  38. data/build-deb.sh +19 -0
  39. data/ci/test.sh +19 -0
  40. data/conjur.gemspec +9 -12
  41. data/debify.sh +4 -0
  42. data/distrib/bin/_conjur +3 -0
  43. data/distrib/bin/conjur +3 -0
  44. data/distrib/bin/conjurize +3 -0
  45. data/distrib/bin/jsonfield +3 -0
  46. data/features/conjurize.feature +25 -25
  47. data/features/support/env.rb +5 -1
  48. data/features/support/hooks.rb +0 -1
  49. data/jenkins.sh +29 -1
  50. data/lib/conjur/cli.rb +27 -4
  51. data/lib/conjur/command.rb +36 -0
  52. data/lib/conjur/command/audit.rb +12 -0
  53. data/lib/conjur/command/bootstrap.rb +5 -9
  54. data/lib/conjur/command/host_factories.rb +187 -0
  55. data/lib/conjur/command/hosts.rb +82 -2
  56. data/lib/conjur/command/layers.rb +28 -0
  57. data/lib/conjur/command/resources.rb +1 -0
  58. data/lib/conjur/command/rspec/mock_services.rb +1 -1
  59. data/lib/conjur/command/server.rb +67 -0
  60. data/lib/conjur/command/users.rb +67 -12
  61. data/lib/conjur/command/variables.rb +101 -14
  62. data/lib/conjur/conjurize.rb +25 -69
  63. data/lib/conjur/conjurize/script.rb +133 -0
  64. data/lib/conjur/version.rb +1 -1
  65. data/publish.sh +6 -0
  66. data/spec/command/elevate_spec.rb +1 -1
  67. data/spec/command/host_factories_spec.rb +38 -0
  68. data/spec/command/hosts_spec.rb +86 -22
  69. data/spec/command/users_spec.rb +51 -3
  70. data/spec/command/variable_expiration_spec.rb +174 -0
  71. data/spec/command/variables_spec.rb +1 -1
  72. data/spec/conjurize_spec.rb +70 -0
  73. metadata +61 -64
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013 Conjur Inc
2
+ # Copyright (C) 2013-2015 Conjur Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
5
  # this software and associated documentation files (the "Software"), to deal in
@@ -32,16 +32,22 @@ class Conjur::Command::Hosts < Conjur::Command
32
32
  c.arg_name "password"
33
33
  c.flag [:p,:password]
34
34
 
35
+ c.desc "A comma-delimited list of CIDR addresses to restrict host to (optional)"
36
+ c.flag [:cidr]
37
+
35
38
  acting_as_option(c)
36
39
 
37
40
  c.action do |global_options,options,args|
38
41
  id = args.shift
39
- options[:id] = id if id
40
42
 
41
43
  unless id
42
44
  ActiveSupport::Deprecation.warn "id argument will be required in future releases"
43
45
  end
44
46
 
47
+ cidr = format_cidr(options.delete(:cidr))
48
+ options[:id] = id if id
49
+ options[:cidr] = cidr unless cidr.nil?
50
+
45
51
  display api.create_host(options), options
46
52
  end
47
53
  end
@@ -88,6 +94,65 @@ class Conjur::Command::Hosts < Conjur::Command
88
94
  end
89
95
  end
90
96
 
97
+ hosts.desc "Rotate a host's API key"
98
+ hosts.command :rotate_api_key do |c|
99
+ c.desc "Login of host whose API key we want to rotate (default: logged-in host)"
100
+ c.flag [:host, :h]
101
+ c.action do |_global, options, _args|
102
+ if options.include?(:host)
103
+ host = options[:host]
104
+
105
+ unless api.host(host).exists?
106
+ exit_now! "host '#{host}' not found"
107
+ end
108
+
109
+ # Prepend 'host/' if it wasn't passed in
110
+ unless is_host_login?(host)
111
+ host = 'host/' + host
112
+ end
113
+
114
+ # Make sure we're not trying to rotate our own key with the user flag.
115
+ if api.username == host
116
+ exit_now! 'To rotate your own API key, use this command without the --host flag'
117
+ end
118
+
119
+ puts api.user(host).rotate_api_key
120
+ else
121
+ username, password = Conjur::Authn.read_credentials
122
+ # Make sure the current identity is a host
123
+ unless is_host_login?(username)
124
+ exit_now! "'#{username}' is not a valid host login, specify a host with the --host flag"
125
+ end
126
+
127
+ new_api_key = Conjur::API.rotate_api_key username, password
128
+ # Show the new one before saving credentials so we don't lose it on failure.
129
+ puts new_api_key
130
+ Conjur::Authn.save_credentials username: username, password: new_api_key
131
+ end
132
+ end
133
+ end
134
+
135
+ hosts.desc "Update a hosts's attributes"
136
+ hosts.arg_name "HOST"
137
+ hosts.command :update do |c|
138
+ c.desc "A comma-delimited list of CIDR addresses to restrict host to (optional). Use 'all' to reset"
139
+ c.flag [:cidr]
140
+
141
+ c.action do |global_options, options, args|
142
+ id = require_arg(args, 'HOST')
143
+
144
+ host = api.host(id)
145
+
146
+ cidr = format_cidr(options[:cidr])
147
+
148
+ host_options = { }
149
+ host_options[:cidr] = cidr unless cidr.nil?
150
+
151
+ host.update(host_options)
152
+ puts "Host updated"
153
+ end
154
+ end
155
+
91
156
  hosts.desc "[Deprecated] Enroll a new host into conjur"
92
157
  hosts.arg_name "HOST"
93
158
  hosts.command :enroll do |c|
@@ -111,4 +176,19 @@ class Conjur::Command::Hosts < Conjur::Command
111
176
  end
112
177
  end
113
178
  end
179
+
180
+ def self.format_cidr(cidr)
181
+ case cidr
182
+ when 'all'
183
+ []
184
+ when nil
185
+ nil
186
+ else
187
+ cidr.split(',').each {|x| x.strip!}
188
+ end
189
+ end
190
+
191
+ def self.is_host_login?(username)
192
+ username.start_with?('host/')
193
+ end
114
194
  end
@@ -104,6 +104,34 @@ class Conjur::Command::Layers < Conjur::Command
104
104
  end
105
105
  end
106
106
 
107
+ layer.desc "Decommission a layer"
108
+ layer.arg_name "LAYER"
109
+ layer.command :retire do |c|
110
+ retire_options c
111
+
112
+ c.action do |global_options,options,args|
113
+ id = require_arg(args, 'LAYER')
114
+
115
+ layer = api.layer(id)
116
+
117
+ validate_retire_privileges layer, options
118
+
119
+ retire_resource layer
120
+ retire_role layer
121
+ # retire internal roles for observe, use_host, admin_host
122
+ account = Conjur::Core::API.conjur_account
123
+ ['observe', 'use_host', 'admin_host'].each do |priv|
124
+ role_name = ['layer', id, priv].join('/')
125
+ role_id = [ account, '@', role_name].join(':')
126
+ role_obj = api.role(role_id)
127
+ retire_internal_role role_obj
128
+ end
129
+ give_away_resource layer, options
130
+
131
+ puts "Layer retired"
132
+ end
133
+ end
134
+
107
135
  layer.desc "Operations on hosts"
108
136
  layer.command :hosts do |hosts|
109
137
  hosts.desc "Permit a privilege on hosts in the layer"
@@ -68,6 +68,7 @@ class Conjur::Command::Resources < Conjur::Command
68
68
  id = full_resource_id( require_arg(args, "RESOURCE") )
69
69
  role = require_arg(args, "ROLE")
70
70
  privilege = require_arg(args, "PRIVILEGE")
71
+ $stderr.print "Granting #{role} permission to #{privilege} #{id}... "
71
72
  unless options[:g]
72
73
  api.resource(id).permit privilege, role
73
74
  else
@@ -1,7 +1,7 @@
1
1
  shared_context "with fake endpoints and test config" do
2
2
  let(:authn_host) { 'https://authn.example.com' }
3
3
  let(:authz_host) { 'https://authz.example.com' }
4
- let(:core_host) { 'https://core.example.com' }
4
+ let(:core_host) { 'https://core.example.com/api' }
5
5
  before do
6
6
  allow(Conjur::Authn::API).to receive(:host) { authn_host }
7
7
  allow(Conjur::Authz::API).to receive(:host) { authz_host }
@@ -0,0 +1,67 @@
1
+ #
2
+ # Copyright (C) 2016 Conjur Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ # this software and associated documentation files (the "Software"), to deal in
6
+ # the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ # the Software, and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+
21
+ class Conjur::Command::Server < Conjur::Command
22
+ desc 'Show Conjur client and server versions'
23
+ command :version do |v|
24
+ v.action do |*_|
25
+ puts "Conjur client version #{Conjur::VERSION}"
26
+ show_server_version
27
+ end
28
+ end
29
+
30
+ desc 'Server information'
31
+ command :server do |server|
32
+ server.desc 'Show service versions'
33
+ server.command :version do |c|
34
+ c.action do |*_|
35
+ show_server_version
36
+ end
37
+ end
38
+
39
+ server.desc 'Show general server information'
40
+ server.command :info do |c|
41
+ c.action do |*_|
42
+ display Conjur::API.appliance_info
43
+ end
44
+ end
45
+
46
+ server.desc 'Show server health information'
47
+ server.command :health do |c|
48
+ c.desc 'Show health information for a remote host, from the perspective of this server'
49
+ c.flag :h, :host
50
+ c.action do |_, options, _|
51
+ display Conjur::API.appliance_health(options[:host])
52
+ end
53
+ end
54
+ end
55
+
56
+ class << self
57
+ def show_server_version
58
+ services = Conjur::API.appliance_info['services']
59
+ appliance = services.delete 'appliance'
60
+ puts "Conjur appliance version: #{appliance['version']}"
61
+ puts 'Conjur service versions:'
62
+ services.each do |name,info|
63
+ puts " #{name}: #{info['version']}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013 Conjur Inc
2
+ # Copyright (C) 2013-2015 Conjur Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
5
  # this software and associated documentation files (the "Software"), to deal in
@@ -33,6 +33,9 @@ class Conjur::Command::Users < Conjur::Command
33
33
  c.desc "UID number to be associated with user (optional)"
34
34
  c.flag [:uidnumber]
35
35
 
36
+ c.desc "A comma-delimited list of CIDR addresses to restrict user to (optional)"
37
+ c.flag [:cidr]
38
+
36
39
  acting_as_option(c)
37
40
 
38
41
  interactive_option c
@@ -44,9 +47,11 @@ class Conjur::Command::Users < Conjur::Command
44
47
 
45
48
  groupid = options[:ownerid]
46
49
  uidnumber = options[:uidnumber]
50
+ cidr = format_cidr(options[:cidr])
47
51
  password = nil
48
- exit_now! "uidnumber should be integer" unless uidnumber.blank? || /\d+/ =~ uidnumber
49
-
52
+
53
+ validate_uidnumber(uidnumber)
54
+
50
55
  if interactive
51
56
  login ||= prompt_for_id :user, "login name"
52
57
 
@@ -57,7 +62,8 @@ class Conjur::Command::Users < Conjur::Command
57
62
  attributes = {
58
63
  "Login" => login,
59
64
  "Owner" => groupid,
60
- "UID Number" => uidnumber
65
+ "UID Number" => uidnumber,
66
+ "CIDR" => cidr
61
67
  }
62
68
  attributes["Password"] = "********" unless password.blank?
63
69
  prompt_to_confirm :user, attributes
@@ -70,6 +76,7 @@ class Conjur::Command::Users < Conjur::Command
70
76
  user_options = { }
71
77
  user_options[:ownerid] = groupid if groupid
72
78
  user_options[:uidnumber] = uidnumber.to_i if uidnumber
79
+ user_options[:cidr] = cidr unless cidr.nil?
73
80
  user_options[:password] = password if password
74
81
  user = api.create_user(login, user_options)
75
82
 
@@ -129,24 +136,57 @@ class Conjur::Command::Users < Conjur::Command
129
136
  c.flag [:p,:password]
130
137
 
131
138
  c.action do |global_options,options,args|
132
- username, password = Conjur::Authn.read_credentials
139
+ username, password = Conjur::Authn.get_credentials
133
140
  new_password = options[:password] || prompt_for_password
134
141
 
135
142
  Conjur::API.update_password username, password, new_password
136
143
  end
137
144
  end
138
145
 
139
- user.desc "Update user's attributes (only uidnumber supported now)"
140
- user.arg_name "USER"
146
+ user.desc "Rotate a user's API key"
147
+ user.command :rotate_api_key do |c|
148
+ c.desc "Login of user whose API key we want to rotate (default: logged-in user)"
149
+ c.flag [:user, :u]
150
+ c.action do |_global, options, _args|
151
+ if options.include?(:user)
152
+ # Make sure we're not trying to rotate our own key with the user flag.
153
+ if api.username == options[:user]
154
+ exit_now! 'To rotate your own API key, use this command without the --user flag'
155
+ end
156
+ puts api.user(options[:user]).rotate_api_key
157
+ else
158
+ username, password = Conjur::Authn.read_credentials
159
+ new_api_key = Conjur::API.rotate_api_key username, password
160
+ # Show the new one before saving credentials so we don't lose it on failure.
161
+ puts new_api_key
162
+ Conjur::Authn.save_credentials username: username, password: new_api_key
163
+ end
164
+ end
165
+ end
166
+
167
+ user.desc "Update a user's attributes"
168
+ user.arg_name "USER"
141
169
  user.command :update do |c|
142
- c.desc "UID number to be associated with user"
170
+ c.desc "UID number to be associated with user (optional)"
143
171
  c.flag [:uidnumber]
172
+
173
+ c.desc "A comma-delimited list of CIDR addresses to restrict user to (optional). Use 'all' to reset"
174
+ c.flag [:cidr]
175
+
144
176
  c.action do |global_options, options, args|
145
177
  login=require_arg(args,'USER')
146
- raise "Uidnumber should be integer" unless /\d+/ =~ options[:uidnumber]
147
- options[:uidnumber]=options[:uidnumber].to_i
148
- api.user(login).update(options)
149
- puts "UID set"
178
+
179
+ uidnumber = options[:uidnumber]
180
+ cidr = format_cidr(options[:cidr])
181
+
182
+ validate_uidnumber(uidnumber)
183
+
184
+ user_options = { }
185
+ user_options[:uidnumber] = uidnumber.to_i if uidnumber
186
+ user_options[:cidr] = cidr unless cidr.nil?
187
+
188
+ api.user(login).update(user_options)
189
+ puts "User updated"
150
190
  end
151
191
  end
152
192
 
@@ -165,4 +205,19 @@ class Conjur::Command::Users < Conjur::Command
165
205
  def self.prompt_for_uidnumber
166
206
  prompt_for_idnumber "uid number"
167
207
  end
208
+
209
+ def self.format_cidr(cidr)
210
+ case cidr
211
+ when 'all'
212
+ []
213
+ when nil
214
+ nil
215
+ else
216
+ cidr.split(',').each {|x| x.strip!}
217
+ end
218
+ end
219
+
220
+ def self.validate_uidnumber(uidnumber)
221
+ exit_now! 'uidnumber should be integer' unless uidnumber.blank? || /\d+/ =~ uidnumber
222
+ end
168
223
  end
@@ -153,24 +153,111 @@ class Conjur::Command::Variables < Conjur::Command
153
153
  $stdout.write api.variable(id).value(options[:version])
154
154
  end
155
155
  end
156
- end
157
-
158
- def self.prompt_for_kind
159
- highline.ask('Enter the kind: ') {|q| q.default = @default_kind }
160
- end
161
156
 
162
- def self.prompt_for_mime_type
163
- highline.choose do |menu|
164
- menu.prompt = 'Enter the MIME type: '
165
- menu.choice @default_mime_type
166
- menu.choices *%w(application/json application/xml application/x-yaml application/x-pem-file)
167
- menu.choice "other", nil do |c|
168
- @highline.ask('Enter a custom mime type: ')
157
+ var.desc 'Set the expiration for a variable'
158
+ var.command :expire do |c|
159
+ c.arg_name "NOW"
160
+ c.desc 'Set variable to expire immediately'
161
+ min_version c, '4.6.0'
162
+ c.switch [:n, :'now'], :negatable => false
163
+
164
+ c.arg_name "DAYS"
165
+ c.desc 'Set variable to expire after the given number of days'
166
+ c.flag [:d, :'days']
167
+
168
+ c.arg_name "MONTHS"
169
+ c.desc 'Set variable to expire after the given number of months'
170
+ c.flag [:m, :'months']
171
+
172
+ c.arg_name "DURATION"
173
+ c.desc 'Set variable to expire after the given ISO8601 duration'
174
+ c.flag [:i, :'in']
175
+
176
+ c.action do |global_options, options, args|
177
+ id = require_arg(args, 'VARIABLE')
178
+
179
+ exit_now! 'Specify only one duration' if durations(options) > 1
180
+ exit_now! 'Specify at least one duration' if durations(options) == 0
181
+
182
+ now = options[:n]
183
+ days = options[:d]
184
+ months = options[:m]
185
+
186
+ case
187
+ when now.present?
188
+ duration = 'P0Y'
189
+ when days.present?
190
+ duration = "P#{days.to_i}D"
191
+ when months.present?
192
+ duration = "P#{months.to_i}M"
193
+ else
194
+ duration = options[:i]
195
+ end
196
+
197
+ display api.variable(id).expires_in(duration)
198
+ end
199
+ end
200
+
201
+ var.desc 'Display expiring variables'
202
+ var.long_desc 'Only variables that expire within the given duration are displayed. If no duration is provided, show all visible variables that are set to expire.'
203
+ var.command :expirations do |c|
204
+ c.arg_name 'DAYS'
205
+ c.desc 'Display variables that expire within the given number of days'
206
+ min_version c, '4.6.0'
207
+ c.flag [:d, :'days']
208
+
209
+ c.arg_name 'MONTHS'
210
+ c.desc 'Display variables that expire within the given number of months'
211
+ c.flag [:m, :'months']
212
+
213
+ c.arg_name 'IN'
214
+ c.desc 'Display variables that expire within the given ISO8601 interval'
215
+ c.flag [:i, :'in']
216
+
217
+ c.action do | global_options, options, args|
218
+
219
+ days = options[:d]
220
+ months = options[:m]
221
+ duration = options[:i]
222
+
223
+ exit_now! 'Specify only one duration' if durations(options) > 1
224
+
225
+ case
226
+ when days.present?
227
+ duration = "P#{days.to_i}D"
228
+ when months.present?
229
+ duration = "P#{months.to_i}M"
230
+ end
231
+
232
+ display api.variable_expirations(duration)
169
233
  end
170
234
  end
235
+
171
236
  end
172
237
 
173
- def self.prompt_for_value
174
- read_till_eof('Enter the secret value (^D on its own line to finish):')
238
+ class << self
239
+ def prompt_for_kind
240
+ highline.ask('Enter the kind: ') {|q| q.default = @default_kind }
241
+ end
242
+
243
+ def prompt_for_mime_type
244
+ highline.choose do |menu|
245
+ menu.prompt = 'Enter the MIME type: '
246
+ menu.choice @default_mime_type
247
+ menu.choices *%w(application/json application/xml application/x-yaml application/x-pem-file)
248
+ menu.choice "other", nil do |c|
249
+ @highline.ask('Enter a custom mime type: ')
250
+ end
251
+ end
252
+ end
253
+
254
+ def prompt_for_value
255
+ read_till_eof('Enter the secret value (^D on its own line to finish):')
256
+ end
257
+
258
+ def durations(options)
259
+ [options[:n],options[:d],options[:m],options[:i]].count {|o| o.present?}
260
+ end
175
261
  end
262
+
176
263
  end