conjur-cli 4.8.0 → 4.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ *.policy
1
2
  .conjurrc
2
3
  *.cert
3
4
  *.credential
data/Gemfile CHANGED
@@ -3,15 +3,8 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in conjur.gemspec
4
4
  gemspec
5
5
 
6
- gem 'conjur-api', git: 'https://github.com/conjurinc/api-ruby.git', branch: 'master'
6
+ gem 'conjur-api', '>=4.8', git: 'https://github.com/conjurinc/api-ruby.git', branch: 'master'
7
7
 
8
8
  group :test, :development do
9
9
  gem 'pry'
10
10
  end
11
-
12
- group :development do
13
- gem 'conjur-asset-environment-api', git: 'git@github.com:inscitiv/conjur-asset-environment', branch: 'master'
14
- gem 'conjur-asset-key-pair-api', git: 'git@github.com:conjurinc/conjur-asset-key-pair', branch: 'master'
15
- gem 'conjur-asset-layer-api', git: 'git@github.com:conjurinc/conjur-asset-layer', branch: 'master'
16
- gem 'conjur-asset-ui-api', git: 'git@github.com:conjurinc/conjur-asset-ui', branch: 'master'
17
- end
data/bin/conjur CHANGED
@@ -20,6 +20,7 @@
20
20
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
  #
22
22
 
23
+ require 'active_support'
23
24
  require 'conjur/cli'
24
25
 
25
26
  exit Conjur::CLI.run(ARGV)
data/conjur.gemspec CHANGED
@@ -14,8 +14,10 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "conjur-cli"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Conjur::VERSION
17
-
18
- gem.add_dependency 'conjur-api', '>=4.7.2'
17
+
18
+
19
+ gem.add_dependency 'activesupport'
20
+ gem.add_dependency 'conjur-api', '>=4.8'
19
21
  gem.add_dependency 'gli', '>=2.8.0'
20
22
  gem.add_dependency 'highline'
21
23
  gem.add_dependency 'netrc'
data/lib/conjur/authn.rb CHANGED
@@ -19,6 +19,8 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  require 'highline'
22
+ require 'active_support'
23
+ require 'active_support/deprecation'
22
24
  require 'conjur/api'
23
25
  require 'netrc'
24
26
 
@@ -27,6 +27,7 @@ class Conjur::Command::Assets < Conjur::Command
27
27
  desc "Create an asset"
28
28
  arg_name "kind:id"
29
29
  command :create do |c|
30
+ def c.nodoc; true end
30
31
  acting_as_option(c)
31
32
 
32
33
  c.action do |global_options, options, args|
@@ -55,6 +56,7 @@ class Conjur::Command::Assets < Conjur::Command
55
56
  desc "Show an asset"
56
57
  arg_name "id"
57
58
  command :show do |c|
59
+ def c.nodoc; true end
58
60
  c.action do |global_options,options,args|
59
61
  kind, id = get_kind_and_id_from_args(args, 'id')
60
62
  display api.send(kind, id).attributes
@@ -64,6 +66,7 @@ class Conjur::Command::Assets < Conjur::Command
64
66
  desc "Checks for the existance of an asset"
65
67
  arg_name "id"
66
68
  command :exists do |c|
69
+ def c.nodoc; true end
67
70
  c.action do |global_options,options,args|
68
71
  kind, id = get_kind_and_id_from_args(args, 'id')
69
72
  puts api.send(kind, id).exists?
@@ -73,6 +76,7 @@ class Conjur::Command::Assets < Conjur::Command
73
76
  desc "List an asset"
74
77
  arg_name "kind"
75
78
  command :list do |c|
79
+ def c.nodoc; true end
76
80
  c.action do |global_options,options,args|
77
81
  kind = require_arg(args, "kind").gsub('-', '_')
78
82
  if api.respond_to?(kind.pluralize)
@@ -36,6 +36,7 @@ class Conjur::DSLCommand < Conjur::Command
36
36
 
37
37
  require 'conjur/dsl/runner'
38
38
  runner = Conjur::DSL::Runner.new(script, filename)
39
+ runner.owner = options[:ownerid] if options[:ownerid]
39
40
 
40
41
  if context = options[:context]
41
42
  runner.context = begin
@@ -0,0 +1,170 @@
1
+ #
2
+ # Copyright (C) 2014 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
+ require 'conjur/authn'
22
+ require 'conjur/command'
23
+ require 'conjur/conjurenv'
24
+ require 'tempfile'
25
+
26
+ class Conjur::Command::Env < Conjur::Command
27
+
28
+ self.prefix = :env
29
+
30
+ def self.common_parameters c
31
+ c.desc "Environment configuration file"
32
+ c.default_value ".conjurenv"
33
+ c.flag ["c"]
34
+
35
+ c.desc "Environment configuration as inline yaml"
36
+ c.flag ["yaml"]
37
+ end
38
+
39
+ def self.get_env_object options
40
+ if options[:yaml] and options[:c]!='.conjurenv'
41
+ exit_now! "Options -c and --yaml can not be provided together"
42
+ end
43
+
44
+ env = if options[:yaml]
45
+ Conjur::Env.new(yaml: options[:yaml])
46
+ else
47
+ Conjur::Env.new(file: (options[:c]||'.conjurenv'))
48
+ end
49
+ return env
50
+ end
51
+
52
+ desc "Execute external command with environment variables populated from Conjur"
53
+ long_desc <<'RUNLONGDESC'
54
+ Processes environment configuration (see env:help for details), and executes a command (with optional arguments) in the modified environment.
55
+ Local names are uppercased and used as names of environment variables.
56
+
57
+ Consider following environment configuration:
58
+
59
+ { key_pair_name: jenkins_key, ssh_keypair_path: !tmp jenkins/private_key, api_key: !var jenkins/api_key }
60
+
61
+ Values of the variables "jenkins/private_key" and "jenkins/api_key" will be obtained from Conjur. Value of "jenkins/api_key" will be associated with local name 'api_key'. Value of "jenkins/private_key" will be stored in the temporary file, which name will be associated with local name "ssh_keypair_path".
62
+
63
+ Than, external command with appropriate arguments will be launched in the environment which has following variables:
64
+
65
+ KEY_PAIR_NAME="jenkins_key",
66
+ SSH_KEYPAIR_PATH="/dev/shm/temp_file_with_key_obtained_from_Conjur",
67
+ API_KEY="api key obtained from Conjur"
68
+ RUNLONGDESC
69
+ arg_name "-- command [arg1, arg2 ...] "
70
+ command :run do |c|
71
+ common_parameters(c)
72
+
73
+ c.action do |global_options,options,args|
74
+ if args.empty?
75
+ exit_now! "External command with optional arguments should be provided"
76
+ end
77
+ env = get_env_object(options)
78
+ runtime_environment = Hash[ env.obtain(api).map {|k,v| [k.upcase, v] } ]
79
+ if Conjur.log
80
+ Conjur.log << "Running command in the prepared environment: #{args}"
81
+ end
82
+ Kernel.system(runtime_environment, *args) or exit($?.to_i) # keep original exit code in case of failure
83
+ end
84
+ end
85
+
86
+ desc "Check availability of Conjur variables"
87
+ long_desc "Checks availability of Conjur variables mentioned in an environment configuration (see env:help for details), and prints out each local name and appropriate status"
88
+
89
+ command :check do |c|
90
+ common_parameters(c)
91
+ c.action do |global_options,options,args|
92
+ env = get_env_object(options)
93
+ result = env.check(api)
94
+ result.each { |k,v| puts "#{k}: #{v}" }
95
+ raise "Some variables are not available" unless result.values.select {|v| v == :unavailable }.empty?
96
+ end
97
+ end # command
98
+
99
+ desc "Render ERB template with variables obtained from Conjur"
100
+ long_desc <<'TEMPLATEDESC'
101
+ Processes environment configuration (see env:help for details), and creates a temporary file, which contains result of ERB template rendering in appropriate context.
102
+ Template should refer to Conjur values by local name as "%<= conjurenv['local_name'] %>".
103
+
104
+ Consider following environment configuration:
105
+
106
+ { key_pair_name: jenkins_key, ssh_keypair_path: !tmp jenkins/private_key, api_key: !var jenkins/api_key }
107
+
108
+ Values of the variables "jenkins/private_key" and "jenkins/api_key" will be obtained from Conjur. Value of "jenkins/api_key" will be associated with local name 'api_key'. Value of "jenkins/private_key" will be stored in the temporary file, which name will be associated with local name "ssh_keypair_path".
109
+
110
+ Than, following template
111
+
112
+ key_pair=<%= conjurenv["key_pair_name"] %>, path_to_ssh_key= <%= conjurenv["ssh_keypair_path"] %>, api_key = '<%= conjurenv["api_key"] %>'
113
+
114
+ will be rendered to
115
+
116
+ key_pair=jenkins_key, path_to_ssh_key=/dev/shm/temp_file_with_key_obtained_from_Conjur, api_key='api key obtained from Conjur'
117
+
118
+ Result of the rendering will be stored in temporary file, which location is than printed to stdout
119
+ TEMPLATEDESC
120
+ arg_name "template.erb"
121
+ command :template do |c|
122
+ common_parameters(c)
123
+
124
+ c.action do |global_options,options,args|
125
+ template_file = args.first
126
+ exit_now! "Location of readable ERB template should be provided" unless template_file and File.readable?(template_file)
127
+ template = File.read(template_file)
128
+ env = get_env_object(options)
129
+ conjurenv = env.obtain(api) # needed for binding
130
+ rendered = ERB.new(template).result(binding)
131
+
132
+ #
133
+ tempfile = if File.directory?("/dev/shm") and File.writable?("/dev/shm")
134
+ Tempfile.new("conjur","/dev/shm")
135
+ else
136
+ Tempfile.new("conjur")
137
+ end
138
+ tempfile.write(rendered)
139
+ tempfile.close()
140
+ old_path = tempfile.path
141
+ new_path = old_path+".saved"
142
+ FileUtils.copy(old_path, new_path) # prevent garbage collection
143
+ puts new_path
144
+ end
145
+ end
146
+
147
+ desc "Print description of environment configuration format"
148
+ command :help do |c|
149
+ c.action do |global_options,options,args|
150
+ puts """
151
+ Environment configuration (either stored in file referred by -f option or provided inline with --yaml option) should be a YAML document describing one-level Hash.
152
+ Keys of the hash are 'local names', used to refer to variable values in convenient manner. (See help for env:run and env:template for more details about how they are interpreted).
153
+
154
+ Values of the hash may take one of the following forms: a) string b) string preceeded with !var tag c) string preceeded with !tmp tag.
155
+
156
+ a) Plain string is just associated with local name without any calls to Conjur.
157
+
158
+ b) String preceeded by !var tag is interpreted as an ID of the Conjur variable, which value should be obtained and associated with appropriate local name.
159
+
160
+ c) String preceeded by !tmp tag is interpreted as an ID of the Conjur variable, which value should be stored in temporary file, which location should in turn be associated with appropriate local name.
161
+
162
+ Example of environment configuration:
163
+
164
+ { local_variable_1: 'literal value', local_variable_2: !var id/of/Conjur/Variable , local_variable_3: !tmp id/of/another/Conjur/variable }
165
+
166
+ """
167
+ end
168
+ end
169
+
170
+ end
@@ -25,6 +25,8 @@ class Conjur::Command::Field < Conjur::Command
25
25
 
26
26
  desc "(Deprecated. See standalone jsonfield command instead.)"
27
27
  command :select do |c|
28
+ def c.nodoc; true end
29
+
28
30
  c.action do |global_options,options,args|
29
31
  pattern = require_arg(args, 'pattern')
30
32
  value = args.shift || STDIN.read
@@ -62,6 +62,15 @@ class Conjur::Command::Hosts < Conjur::Command
62
62
  end
63
63
  end
64
64
 
65
+ desc "List the layers to which the host belongs"
66
+ arg_name "id"
67
+ command :layers do |c|
68
+ c.action do |global_options, options, args|
69
+ id = require_arg(args, 'id')
70
+ display api.host(id).role.all.select{|r| r.kind == "layer"}.map(&:identifier), options
71
+ end
72
+ end
73
+
65
74
  desc "Enroll a new host into conjur"
66
75
  arg_name "host"
67
76
  command :enroll do |c|
@@ -19,6 +19,8 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  require 'conjur/command'
22
+ require 'openssl'
23
+ require 'socket'
22
24
 
23
25
  class Conjur::Command::Init < Conjur::Command
24
26
  desc "Initialize the Conjur configuration"
@@ -40,14 +42,14 @@ class Conjur::Command::Init < Conjur::Command
40
42
  c.desc "Hostname of the Conjur endpoint (required for virtual appliance)"
41
43
  c.flag ["h", "hostname"]
42
44
 
43
- c.desc "Conjur account name (will be obtained from the host unless provided by this option)"
45
+ c.desc "Conjur organization account name (not required for appliance)"
44
46
  c.flag ["a", "account"]
45
47
 
46
48
  c.desc "Conjur SSL certificate (will be obtained from host unless provided by this option)"
47
49
  c.flag ["c", "certificate"]
48
50
 
49
51
  c.desc "File to write the configuration to"
50
- c.default_value File.join(ENV['HOME'], '.conjurrc')
52
+ c.default_value File.expand_path('~/.conjurrc')
51
53
  c.flag ["f","file"]
52
54
 
53
55
  c.desc "Force overwrite of existing files"
@@ -57,17 +59,18 @@ class Conjur::Command::Init < Conjur::Command
57
59
  hl = HighLine.new $stdin, $stderr
58
60
 
59
61
  hostname = options[:hostname] || hl.ask("Enter the hostname (and optional port) of your Conjur endpoint: ").to_s
60
- exit_now! "Hostname should not include the protocol" if hostname =~ /^https?\:/
62
+ protocol, hostname = (hostname.scan %r(^(?:(.*)://)?(.*))).first
63
+ exit_now! "only https protocol supported" unless protocol.nil? || protocol == 'https'
61
64
  if hostname
62
65
  Conjur.configuration.core_url = "https://#{hostname}/api"
63
66
  end
64
67
 
65
68
  account = options[:account]
66
69
  account ||= if hostname
67
- account = Conjur::Core::API.info['account'] or raise "Exepcting 'account' in Core info"
70
+ account = Conjur::Core::API.info['account'] or raise "Expecting 'account' in Core info"
68
71
  else
69
72
  # using .to_s to overcome https://github.com/JEG2/highline/issues/69
70
- hl.ask("Enter your account name: ").to_s
73
+ hl.ask("Enter your organization account name: ").to_s
71
74
  end
72
75
 
73
76
  if (certificate = options[:certificate]).blank?
@@ -77,13 +80,7 @@ class Conjur::Command::Init < Conjur::Command
77
80
  else
78
81
  hostname + ':443'
79
82
  end
80
- certificate = \
81
- `echo | openssl s_client -connect #{connect_hostname} 2>/dev/null | openssl x509 -fingerprint`
82
- exit_now! "Unable to retrieve certificate from #{hostname}" if certificate.blank?
83
-
84
- lines = certificate.split("\n")
85
- fingerprint = lines[0]
86
- certificate = lines[1..-1].join("\n")
83
+ fingerprint, certificate = get_certificate connect_hostname
87
84
 
88
85
  puts
89
86
  puts fingerprint
@@ -118,4 +115,26 @@ class Conjur::Command::Init < Conjur::Command
118
115
  puts "Wrote configuration to #{options[:file]}"
119
116
  end
120
117
  end
118
+
119
+ def self.get_certificate connect_hostname
120
+ include OpenSSL::SSL
121
+ host, port = connect_hostname.split ':'
122
+ port ||= 443
123
+
124
+ sock = TCPSocket.new host, port.to_i
125
+ ssock = SSLSocket.new sock
126
+ ssock.connect
127
+ cert = ssock.peer_cert
128
+ fp = Digest::SHA1.digest cert.to_der
129
+
130
+ # convert to hex, then split into bytes with :
131
+ hexfp = (fp.unpack 'H*').first.upcase.scan(/../).join(':')
132
+
133
+ ["SHA1 Fingerprint=#{hexfp}", cert.to_pem]
134
+ rescue
135
+ exit_now! "Unable to retrieve certificate from #{connect_hostname}"
136
+ ensure
137
+ ssock.close if ssock
138
+ sock.close if sock
139
+ end
121
140
  end
@@ -20,16 +20,19 @@
20
20
  #
21
21
  require 'conjur/command/dsl_command'
22
22
 
23
+ require 'etc'
24
+ require 'socket'
25
+
23
26
  class Conjur::Command::Policy < Conjur::DSLCommand
24
27
  self.prefix = :policy
25
28
 
26
29
  class << self
27
30
  def default_collection_user
28
- ( ENV['USER'] ).strip
31
+ Etc.getlogin
29
32
  end
30
33
 
31
34
  def default_collection_hostname
32
- ( ENV['HOSTNAME'] || `hostname` ).strip
35
+ Socket.gethostname
33
36
  end
34
37
 
35
38
  def default_collection_name
@@ -80,4 +83,4 @@ owner of the policy role is the logged-in user (you), as always.
80
83
  end
81
84
  end
82
85
  end
83
- end
86
+ end
@@ -38,7 +38,7 @@ class Conjur::Command::Roles < Conjur::Command
38
38
  end
39
39
 
40
40
  role.create(options)
41
- display(role, options)
41
+ puts "Created role #{role.roleid}"
42
42
  end
43
43
  end
44
44
 
@@ -27,6 +27,8 @@ class Conjur::Command::Secrets < Conjur::Command
27
27
  desc "Create and store a secret"
28
28
  arg_name "secret"
29
29
  command :create do |c|
30
+ def c.nodoc; true end
31
+
30
32
  acting_as_option(c)
31
33
 
32
34
  c.action do |global_options,options,args|
@@ -38,6 +40,7 @@ class Conjur::Command::Secrets < Conjur::Command
38
40
  desc "Retrieve a secret"
39
41
  arg_name "id"
40
42
  command :value do |c|
43
+ def c.nodoc; true end
41
44
  c.action do |global_options,options,args|
42
45
  id = args.shift or raise "Missing parameter: id"
43
46
  puts api.secret(id).value
@@ -87,6 +87,7 @@ class Conjur::Command::Variables < Conjur::Command
87
87
  puts "Value added"
88
88
  end
89
89
  end
90
+
90
91
 
91
92
  desc "Get a value"
92
93
  arg_name "variable"