leap_cli 1.2.5
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/bin/leap +81 -0
- data/lib/core_ext/boolean.rb +14 -0
- data/lib/core_ext/hash.rb +35 -0
- data/lib/core_ext/json.rb +42 -0
- data/lib/core_ext/nil.rb +5 -0
- data/lib/core_ext/string.rb +14 -0
- data/lib/leap/platform.rb +52 -0
- data/lib/leap_cli/commands/ca.rb +430 -0
- data/lib/leap_cli/commands/clean.rb +16 -0
- data/lib/leap_cli/commands/compile.rb +134 -0
- data/lib/leap_cli/commands/deploy.rb +172 -0
- data/lib/leap_cli/commands/facts.rb +93 -0
- data/lib/leap_cli/commands/inspect.rb +140 -0
- data/lib/leap_cli/commands/list.rb +122 -0
- data/lib/leap_cli/commands/new.rb +126 -0
- data/lib/leap_cli/commands/node.rb +272 -0
- data/lib/leap_cli/commands/pre.rb +99 -0
- data/lib/leap_cli/commands/shell.rb +67 -0
- data/lib/leap_cli/commands/test.rb +55 -0
- data/lib/leap_cli/commands/user.rb +140 -0
- data/lib/leap_cli/commands/util.rb +50 -0
- data/lib/leap_cli/commands/vagrant.rb +201 -0
- data/lib/leap_cli/config/macros.rb +369 -0
- data/lib/leap_cli/config/manager.rb +369 -0
- data/lib/leap_cli/config/node.rb +37 -0
- data/lib/leap_cli/config/object.rb +336 -0
- data/lib/leap_cli/config/object_list.rb +174 -0
- data/lib/leap_cli/config/secrets.rb +43 -0
- data/lib/leap_cli/config/tag.rb +18 -0
- data/lib/leap_cli/constants.rb +7 -0
- data/lib/leap_cli/leapfile.rb +97 -0
- data/lib/leap_cli/load_paths.rb +15 -0
- data/lib/leap_cli/log.rb +166 -0
- data/lib/leap_cli/logger.rb +216 -0
- data/lib/leap_cli/markdown_document_listener.rb +134 -0
- data/lib/leap_cli/path.rb +84 -0
- data/lib/leap_cli/remote/leap_plugin.rb +204 -0
- data/lib/leap_cli/remote/puppet_plugin.rb +66 -0
- data/lib/leap_cli/remote/rsync_plugin.rb +35 -0
- data/lib/leap_cli/remote/tasks.rb +36 -0
- data/lib/leap_cli/requirements.rb +19 -0
- data/lib/leap_cli/ssh_key.rb +130 -0
- data/lib/leap_cli/util/remote_command.rb +110 -0
- data/lib/leap_cli/util/secret.rb +54 -0
- data/lib/leap_cli/util/x509.rb +32 -0
- data/lib/leap_cli/util.rb +431 -0
- data/lib/leap_cli/version.rb +9 -0
- data/lib/leap_cli.rb +46 -0
- data/lib/lib_ext/capistrano_connections.rb +16 -0
- data/lib/lib_ext/gli.rb +52 -0
- data/lib/lib_ext/markdown_document_listener.rb +122 -0
- data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +200 -0
- data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +77 -0
- data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +97 -0
- data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +266 -0
- data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +148 -0
- data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +144 -0
- data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +65 -0
- data/vendor/certificate_authority/lib/certificate_authority/revocable.rb +14 -0
- data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +10 -0
- data/vendor/certificate_authority/lib/certificate_authority/signing_entity.rb +16 -0
- data/vendor/certificate_authority/lib/certificate_authority/signing_request.rb +56 -0
- data/vendor/certificate_authority/lib/certificate_authority.rb +21 -0
- data/vendor/rsync_command/lib/rsync_command/ssh_options.rb +159 -0
- data/vendor/rsync_command/lib/rsync_command/thread_pool.rb +36 -0
- data/vendor/rsync_command/lib/rsync_command/version.rb +3 -0
- data/vendor/rsync_command/lib/rsync_command.rb +96 -0
- data/vendor/rsync_command/test/rsync_test.rb +74 -0
- data/vendor/rsync_command/test/ssh_options_test.rb +61 -0
- data/vendor/vagrant_ssh_keys/vagrant.key +27 -0
- data/vendor/vagrant_ssh_keys/vagrant.pub +1 -0
- metadata +345 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
module KeyMaterial
|
3
|
+
def public_key
|
4
|
+
raise "Required implementation"
|
5
|
+
end
|
6
|
+
|
7
|
+
def private_key
|
8
|
+
raise "Required implementation"
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_in_hardware?
|
12
|
+
raise "Required implementation"
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_in_memory?
|
16
|
+
raise "Required implementation"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_x509_key_pair(pair,password=nil)
|
20
|
+
if password.nil?
|
21
|
+
key = OpenSSL::PKey::RSA.new(pair)
|
22
|
+
else
|
23
|
+
key = OpenSSL::PKey::RSA.new(pair,password)
|
24
|
+
end
|
25
|
+
mem_key = MemoryKeyMaterial.new
|
26
|
+
mem_key.public_key = key.public_key
|
27
|
+
mem_key.private_key = key
|
28
|
+
mem_key
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.from_x509_public_key(public_key_pem)
|
32
|
+
key = OpenSSL::PKey::RSA.new(public_key_pem)
|
33
|
+
signing_request_key = SigningRequestKeyMaterial.new
|
34
|
+
signing_request_key.public_key = key.public_key
|
35
|
+
signing_request_key
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class MemoryKeyMaterial
|
40
|
+
include KeyMaterial
|
41
|
+
include ActiveModel::Validations
|
42
|
+
|
43
|
+
attr_accessor :keypair
|
44
|
+
attr_accessor :private_key
|
45
|
+
attr_accessor :public_key
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
end
|
49
|
+
|
50
|
+
validates_each :private_key do |record, attr, value|
|
51
|
+
record.errors.add :private_key, "cannot be blank" if record.private_key.nil?
|
52
|
+
end
|
53
|
+
validates_each :public_key do |record, attr, value|
|
54
|
+
record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
|
55
|
+
end
|
56
|
+
|
57
|
+
def is_in_hardware?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
def is_in_memory?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def generate_key(modulus_bits=2048)
|
66
|
+
self.keypair = OpenSSL::PKey::RSA.new(modulus_bits)
|
67
|
+
self.private_key = keypair
|
68
|
+
self.public_key = keypair.public_key
|
69
|
+
self.keypair
|
70
|
+
end
|
71
|
+
|
72
|
+
def private_key
|
73
|
+
@private_key
|
74
|
+
end
|
75
|
+
|
76
|
+
def public_key
|
77
|
+
@public_key
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class SigningRequestKeyMaterial
|
82
|
+
include KeyMaterial
|
83
|
+
include ActiveModel::Validations
|
84
|
+
|
85
|
+
validates_each :public_key do |record, attr, value|
|
86
|
+
record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
attr_accessor :public_key
|
90
|
+
|
91
|
+
def initialize(request=nil)
|
92
|
+
if request.is_a? OpenSSL::X509::Request
|
93
|
+
raise "Invalid certificate signing request" unless request.verify request.public_key
|
94
|
+
self.public_key = request.public_key
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def is_in_hardware?
|
99
|
+
false
|
100
|
+
end
|
101
|
+
|
102
|
+
def is_in_memory?
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
def private_key
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def public_key
|
111
|
+
@public_key
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class SigningRequestKeyMaterial
|
116
|
+
include KeyMaterial
|
117
|
+
include ActiveModel::Validations
|
118
|
+
|
119
|
+
validates_each :public_key do |record, attr, value|
|
120
|
+
record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
|
121
|
+
end
|
122
|
+
|
123
|
+
attr_accessor :public_key
|
124
|
+
|
125
|
+
def initialize(request=nil)
|
126
|
+
if request.is_a? OpenSSL::X509::Request
|
127
|
+
raise "Invalid certificate signing request" unless request.verify request.public_key
|
128
|
+
self.public_key = request.public_key
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def is_in_hardware?
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
def is_in_memory?
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
def private_key
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
|
144
|
+
def public_key
|
145
|
+
@public_key
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class OCSPResponseBuilder
|
3
|
+
attr_accessor :ocsp_response
|
4
|
+
attr_accessor :verification_mechanism
|
5
|
+
attr_accessor :ocsp_request_reader
|
6
|
+
attr_accessor :parent
|
7
|
+
attr_accessor :next_update
|
8
|
+
|
9
|
+
GOOD = OpenSSL::OCSP::V_CERTSTATUS_GOOD
|
10
|
+
REVOKED = OpenSSL::OCSP::V_CERTSTATUS_REVOKED
|
11
|
+
|
12
|
+
NO_REASON=0
|
13
|
+
KEY_COMPROMISED=OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE
|
14
|
+
UNSPECIFIED=OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED
|
15
|
+
|
16
|
+
def build_response()
|
17
|
+
raise "Requires a parent for signing" if @parent.nil?
|
18
|
+
if @verification_mechanism.nil?
|
19
|
+
## If no verification callback is provided we're marking it GOOD
|
20
|
+
@verification_mechanism = lambda {|cert_id| [GOOD,NO_REASON] }
|
21
|
+
end
|
22
|
+
|
23
|
+
@ocsp_request_reader.ocsp_request.certid.each do |cert_id|
|
24
|
+
result,reason = verification_mechanism.call(cert_id.serial)
|
25
|
+
|
26
|
+
## cert_id, status, reason, rev_time, this update, next update, ext
|
27
|
+
## - unit of time is seconds
|
28
|
+
## - rev_time is currently set to "now"
|
29
|
+
@ocsp_response.add_status(cert_id,
|
30
|
+
result, reason,
|
31
|
+
0, 0, @next_update, nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
@ocsp_response.sign(OpenSSL::X509::Certificate.new(@parent.to_pem), @parent.key_material.private_key, nil, nil)
|
35
|
+
OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, @ocsp_response)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.from_request_reader(request_reader,verification_mechanism=nil)
|
39
|
+
response_builder = OCSPResponseBuilder.new
|
40
|
+
response_builder.ocsp_request_reader = request_reader
|
41
|
+
|
42
|
+
ocsp_response = OpenSSL::OCSP::BasicResponse.new
|
43
|
+
ocsp_response.copy_nonce(request_reader.ocsp_request)
|
44
|
+
response_builder.ocsp_response = ocsp_response
|
45
|
+
response_builder.next_update = 60*15 #Default of 15 minutes
|
46
|
+
response_builder
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class OCSPRequestReader
|
51
|
+
attr_accessor :raw_ocsp_request
|
52
|
+
attr_accessor :ocsp_request
|
53
|
+
|
54
|
+
def serial_numbers
|
55
|
+
@ocsp_request.certid.collect do |cert_id|
|
56
|
+
cert_id.serial
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.from_der(request_body)
|
61
|
+
reader = OCSPRequestReader.new
|
62
|
+
reader.raw_ocsp_request = request_body
|
63
|
+
reader.ocsp_request = OpenSSL::OCSP::Request.new(request_body)
|
64
|
+
|
65
|
+
reader
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
## DEPRECATED
|
70
|
+
class OCSPHandler
|
71
|
+
include ActiveModel::Validations
|
72
|
+
|
73
|
+
attr_accessor :ocsp_request
|
74
|
+
attr_accessor :certificate_ids
|
75
|
+
|
76
|
+
attr_accessor :certificates
|
77
|
+
attr_accessor :parent
|
78
|
+
|
79
|
+
attr_accessor :ocsp_response_body
|
80
|
+
|
81
|
+
validate do |crl|
|
82
|
+
errors.add :parent, "A parent entity must be set" if parent.nil?
|
83
|
+
end
|
84
|
+
validate :all_certificates_available
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
self.certificates = {}
|
88
|
+
end
|
89
|
+
|
90
|
+
def <<(cert)
|
91
|
+
self.certificates[cert.serial_number.number.to_s] = cert
|
92
|
+
end
|
93
|
+
|
94
|
+
def extract_certificate_serials
|
95
|
+
openssl_request = OpenSSL::OCSP::Request.new(@ocsp_request)
|
96
|
+
|
97
|
+
self.certificate_ids = openssl_request.certid.collect do |cert_id|
|
98
|
+
cert_id.serial
|
99
|
+
end
|
100
|
+
|
101
|
+
self.certificate_ids
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def response
|
106
|
+
raise "Invalid response" unless valid?
|
107
|
+
|
108
|
+
openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new
|
109
|
+
openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
|
110
|
+
openssl_ocsp_response.copy_nonce(openssl_ocsp_request)
|
111
|
+
|
112
|
+
openssl_ocsp_request.certid.each do |cert_id|
|
113
|
+
certificate = self.certificates[cert_id.serial.to_s]
|
114
|
+
|
115
|
+
openssl_ocsp_response.add_status(cert_id,
|
116
|
+
OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
|
117
|
+
0, 0, 30, nil)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil)
|
122
|
+
final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response)
|
123
|
+
self.ocsp_response_body = final_response
|
124
|
+
self.ocsp_response_body
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_der
|
128
|
+
raise "No signed OCSP response body available" if self.ocsp_response_body.nil?
|
129
|
+
self.ocsp_response_body.to_der
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def all_certificates_available
|
135
|
+
openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
|
136
|
+
|
137
|
+
openssl_ocsp_request.certid.each do |cert_id|
|
138
|
+
certificate = self.certificates[cert_id.serial.to_s]
|
139
|
+
errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil?
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class Pkcs11KeyMaterial
|
3
|
+
include KeyMaterial
|
4
|
+
include ActiveModel::Validations
|
5
|
+
include ActiveModel::Serialization
|
6
|
+
|
7
|
+
attr_accessor :engine
|
8
|
+
attr_accessor :token_id
|
9
|
+
attr_accessor :pkcs11_lib
|
10
|
+
attr_accessor :openssl_pkcs11_engine_lib
|
11
|
+
attr_accessor :pin
|
12
|
+
|
13
|
+
def initialize(attributes = {})
|
14
|
+
@attributes = attributes
|
15
|
+
initialize_engine
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_in_hardware?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_in_memory?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_key(modulus_bits=1024)
|
27
|
+
puts "Key generation is not currently supported in hardware"
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def private_key
|
32
|
+
initialize_engine
|
33
|
+
self.engine.load_private_key(self.token_id)
|
34
|
+
end
|
35
|
+
|
36
|
+
def public_key
|
37
|
+
initialize_engine
|
38
|
+
self.engine.load_public_key(self.token_id)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def initialize_engine
|
44
|
+
## We're going to return early and try again later if params weren't passed in
|
45
|
+
## at initialization. Any attempt at getting a public/private key will try
|
46
|
+
## again.
|
47
|
+
return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil?
|
48
|
+
return self.engine unless self.engine.nil?
|
49
|
+
OpenSSL::Engine.load
|
50
|
+
|
51
|
+
pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e|
|
52
|
+
e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib)
|
53
|
+
e.ctrl_cmd("ID","pkcs11")
|
54
|
+
e.ctrl_cmd("LIST_ADD","1")
|
55
|
+
e.ctrl_cmd("LOAD")
|
56
|
+
e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == ""
|
57
|
+
e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib)
|
58
|
+
end
|
59
|
+
|
60
|
+
self.engine = pkcs11
|
61
|
+
pkcs11
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
module SigningEntity
|
3
|
+
|
4
|
+
def self.included(mod)
|
5
|
+
mod.class_eval do
|
6
|
+
attr_accessor :signing_entity
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def signing_entity=(val)
|
11
|
+
raise "invalid param" unless [true,false].include?(val)
|
12
|
+
@signing_entity = val
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class SigningRequest
|
3
|
+
attr_accessor :distinguished_name
|
4
|
+
attr_accessor :key_material
|
5
|
+
attr_accessor :raw_body
|
6
|
+
attr_accessor :openssl_csr
|
7
|
+
attr_accessor :digest
|
8
|
+
|
9
|
+
def to_cert
|
10
|
+
cert = Certificate.new
|
11
|
+
if !@distinguished_name.nil?
|
12
|
+
cert.distinguished_name = @distinguished_name
|
13
|
+
end
|
14
|
+
cert.key_material = @key_material
|
15
|
+
cert
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_pem
|
19
|
+
to_x509_csr.to_pem
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_x509_csr
|
23
|
+
raise "Must specify a DN/subject on csr" if @distinguished_name.nil?
|
24
|
+
raise "Invalid DN in request" unless @distinguished_name.valid?
|
25
|
+
raise "CSR must have key material" if @key_material.nil?
|
26
|
+
raise "CSR must include a public key on key material" if @key_material.public_key.nil?
|
27
|
+
|
28
|
+
opensslcsr = OpenSSL::X509::Request.new
|
29
|
+
opensslcsr.subject = @distinguished_name.to_x509_name
|
30
|
+
opensslcsr.public_key = @key_material.public_key
|
31
|
+
opensslcsr.sign @key_material.private_key, OpenSSL::Digest::Digest.new(@digest || "SHA512")
|
32
|
+
opensslcsr
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.from_x509_csr(raw_csr)
|
36
|
+
csr = SigningRequest.new
|
37
|
+
openssl_csr = OpenSSL::X509::Request.new(raw_csr)
|
38
|
+
csr.distinguished_name = DistinguishedName.from_openssl openssl_csr.subject
|
39
|
+
csr.raw_body = raw_csr
|
40
|
+
csr.openssl_csr = openssl_csr
|
41
|
+
key_material = SigningRequestKeyMaterial.new
|
42
|
+
key_material.public_key = openssl_csr.public_key
|
43
|
+
csr.key_material = key_material
|
44
|
+
csr
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.from_netscape_spkac(raw_spkac)
|
48
|
+
openssl_spkac = OpenSSL::Netscape::SPKI.new raw_spkac
|
49
|
+
csr = SigningRequest.new
|
50
|
+
csr.raw_body = raw_spkac
|
51
|
+
key_material = SigningRequestKeyMaterial.new
|
52
|
+
key_material.public_key = openssl_spkac.public_key
|
53
|
+
csr
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
#Exterior requirements
|
4
|
+
require 'openssl'
|
5
|
+
require 'active_model'
|
6
|
+
|
7
|
+
#Internal modules
|
8
|
+
require 'certificate_authority/signing_entity'
|
9
|
+
require 'certificate_authority/revocable'
|
10
|
+
require 'certificate_authority/distinguished_name'
|
11
|
+
require 'certificate_authority/serial_number'
|
12
|
+
require 'certificate_authority/key_material'
|
13
|
+
require 'certificate_authority/pkcs11_key_material'
|
14
|
+
require 'certificate_authority/extensions'
|
15
|
+
require 'certificate_authority/certificate'
|
16
|
+
require 'certificate_authority/certificate_revocation_list'
|
17
|
+
require 'certificate_authority/ocsp_handler'
|
18
|
+
require 'certificate_authority/signing_request'
|
19
|
+
|
20
|
+
module CertificateAuthority
|
21
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
#
|
2
|
+
# Converts capistrano-style ssh configuration (which uses Net::SSH) into a OpenSSH command line flags suitable for rsync.
|
3
|
+
#
|
4
|
+
# For a list of the options normally support by Net::SSH (and thus Capistrano), see
|
5
|
+
# http://net-ssh.github.com/net-ssh/classes/Net/SSH.html#method-c-start
|
6
|
+
#
|
7
|
+
# Also, to see how Net::SSH does the opposite of the conversion we are doing here, check out:
|
8
|
+
# https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/config.rb
|
9
|
+
#
|
10
|
+
# API mismatch:
|
11
|
+
#
|
12
|
+
# * many OpenSSH options not supported
|
13
|
+
# * some options only make sense for Net::SSH
|
14
|
+
# * compression: for Net::SSH, this option is supposed to accept true, false, or algorithm. OpenSSH accepts 'yes' or 'no'
|
15
|
+
#
|
16
|
+
class RsyncCommand
|
17
|
+
class SshOptions
|
18
|
+
|
19
|
+
def initialize(options={})
|
20
|
+
@options = parse_options(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_flags
|
24
|
+
if @options.empty?
|
25
|
+
nil
|
26
|
+
else
|
27
|
+
%[-e "ssh #{@options.join(' ')}"]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parse_options(options)
|
34
|
+
options.map do |key, value|
|
35
|
+
next if value.nil?
|
36
|
+
# Convert Net::SSH options into OpenSSH options.
|
37
|
+
case key
|
38
|
+
when :auth_methods then opt_auth_methods(value)
|
39
|
+
when :bind_address then opt('BindAddress', value)
|
40
|
+
when :compression then opt('Compression', value ? 'yes' : 'no')
|
41
|
+
when :compression_level then opt('CompressionLevel', value.to_i)
|
42
|
+
when :config then value ? "-F '#{value}'" : nil
|
43
|
+
when :encryption then opt('Ciphers', [value].flatten.join(','))
|
44
|
+
when :forward_agent then opt('ForwardAgent', value)
|
45
|
+
when :global_known_hosts_file then opt('GlobalKnownHostsFile', value)
|
46
|
+
when :hmac then opt('MACs', [value].flatten.join(','))
|
47
|
+
when :host_key then opt('HostKeyAlgorithms', [value].flatten.join(','))
|
48
|
+
when :host_key_alias then opt('HostKeyAlias', value)
|
49
|
+
when :host_name then opt('HostName', value)
|
50
|
+
when :kex then opt('KexAlgorithms', [value].flatten.join(','))
|
51
|
+
when :key_data then nil # not supported
|
52
|
+
when :keys then [value].flatten.select { |k| File.exist?(k) }.map { |k| "-i '#{k}'" }
|
53
|
+
when :keys_only then opt('IdentitiesOnly', value ? 'yes' : 'no')
|
54
|
+
when :languages then nil # not applicable
|
55
|
+
when :logger then nil # not applicable
|
56
|
+
when :paranoid then opt('StrictHostKeyChecking', value ? 'yes' : 'no')
|
57
|
+
when :passphrase then nil # not supported
|
58
|
+
when :password then nil # not supported
|
59
|
+
when :port then "-p #{value.to_i}"
|
60
|
+
when :properties then nil # not applicable
|
61
|
+
when :proxy then nil # not applicable
|
62
|
+
when :rekey_blocks_limit then nil # not supported
|
63
|
+
when :rekey_limit then opt('RekeyLimit', reverse_interpret_size(value))
|
64
|
+
when :rekey_packet_limit then nil # not supported
|
65
|
+
when :timeout then opt('ConnectTimeout', value.to_i)
|
66
|
+
when :user then "-l #{value}"
|
67
|
+
when :user_known_hosts_file then multi_opt('UserKnownHostsFile', value)
|
68
|
+
when :verbose then opt('LogLevel', interpret_log_level(value))
|
69
|
+
end
|
70
|
+
end.compact
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def opt(option_name, option_value)
|
76
|
+
"-o #{option_name}='#{option_value}'"
|
77
|
+
end
|
78
|
+
|
79
|
+
def multi_opt(option_name, option_values)
|
80
|
+
[option_values].flatten.map do |value|
|
81
|
+
opt(option_name, value)
|
82
|
+
end.join(' ')
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# In OpenSSH, password and pubkey default to 'yes', hostbased defaults to 'no'.
|
87
|
+
# Regardless, if :auth_method is configured, then we explicitly set the auth method.
|
88
|
+
#
|
89
|
+
def opt_auth_methods(value)
|
90
|
+
value = [value].flatten
|
91
|
+
opts = []
|
92
|
+
if value.any?
|
93
|
+
if value.include? 'password'
|
94
|
+
opts << opt('PasswordAuthentication', 'yes')
|
95
|
+
else
|
96
|
+
opts << opt('PasswordAuthentication', 'no')
|
97
|
+
end
|
98
|
+
if value.include? 'publickey'
|
99
|
+
opts << opt('PubkeyAuthentication', 'yes')
|
100
|
+
else
|
101
|
+
opts << opt('PubkeyAuthentication', 'no')
|
102
|
+
end
|
103
|
+
if value.include? 'hostbased'
|
104
|
+
opts << opt('HostbasedAuthentication', 'yes')
|
105
|
+
else
|
106
|
+
opts << opt('HostbasedAuthentication', 'no')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
if opts.any?
|
110
|
+
return opts.join(' ')
|
111
|
+
else
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Converts the given integer size in bytes into a string with 'K', 'M', 'G' suffix, as appropriate.
|
118
|
+
#
|
119
|
+
# reverse of interpret_size in https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/config.rb
|
120
|
+
#
|
121
|
+
def reverse_interpret_size(size)
|
122
|
+
size = size.to_i
|
123
|
+
if size < 1024
|
124
|
+
"#{size}"
|
125
|
+
elsif size < 1024 * 1024
|
126
|
+
"#{size/1024}K"
|
127
|
+
elsif size < 1024 * 1024 * 1024
|
128
|
+
"#{size/(1024*1024)}M"
|
129
|
+
else
|
130
|
+
"#{size/(1024*1024*1024)}G"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def interpret_log_level(level)
|
135
|
+
if level.is_a? Symbol
|
136
|
+
case level
|
137
|
+
when :debug then "DEBUG"
|
138
|
+
when :info then "INFO"
|
139
|
+
when :warn then "ERROR"
|
140
|
+
when :error then "ERROR"
|
141
|
+
when :fatal then "FATAL"
|
142
|
+
else "INFO"
|
143
|
+
end
|
144
|
+
elsif level.is_a?(Integer) && defined?(Logger)
|
145
|
+
case level
|
146
|
+
when Logger::DEBUG then "DEBUG"
|
147
|
+
when Logger::INFO then "INFO"
|
148
|
+
when Logger::WARN then "ERROR"
|
149
|
+
when Logger::ERROR then "ERROR"
|
150
|
+
when Logger::FATAL then "FATAL"
|
151
|
+
else "INFO"
|
152
|
+
end
|
153
|
+
else
|
154
|
+
"INFO"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
class RsyncCommand
|
4
|
+
class ThreadPool
|
5
|
+
class << self
|
6
|
+
attr_accessor :default_size
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(size=nil)
|
10
|
+
@size = size || ThreadPool.default_size || 10
|
11
|
+
@jobs = Queue.new
|
12
|
+
@retvals = []
|
13
|
+
@pool = Array.new(@size) do |i|
|
14
|
+
Thread.new do
|
15
|
+
Thread.current[:id] = i
|
16
|
+
catch(:exit) do
|
17
|
+
loop do
|
18
|
+
job, args = @jobs.pop
|
19
|
+
@retvals << job.call(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
def schedule(*args, &block)
|
26
|
+
@jobs << [block, args]
|
27
|
+
end
|
28
|
+
def shutdown
|
29
|
+
@size.times do
|
30
|
+
schedule { throw :exit }
|
31
|
+
end
|
32
|
+
@pool.map(&:join)
|
33
|
+
@retvals
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|