tls-cookbook-cli 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f229793f288565888b208f802076074a4f17c3de
4
+ data.tar.gz: 222c9728bc5b935a0d4c737596a5f91b70d99c41
5
+ SHA512:
6
+ metadata.gz: b534f591efd00f47c6e3ba21be967f59747bf9da57a4e0f42cfc0c979395ab8473057987c8ebbfa35477e16301a840574c1ae6de5dfb1f20006b4fc1894ceb42
7
+ data.tar.gz: dee4a11547884ca1f1450589529f2db457388240c03e17e55b0e4e377463273c4795503d38a9806e8a3e89bb28ada83d6da021bdc9848a1a1065e7a53458b569
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tls/cli'
3
+ ::ChefCookbook::TLS::CLI::Main.start(ARGV)
data/lib/tls/cli.rb ADDED
@@ -0,0 +1 @@
1
+ require 'tls/cli/main'
@@ -0,0 +1,195 @@
1
+ require 'thor'
2
+ require 'openssl'
3
+ require 'date'
4
+ require 'digest'
5
+ require 'base64'
6
+
7
+ module ChefCookbook
8
+ module TLS
9
+ module CLI
10
+ module Helpers
11
+ def self.valid_key_file?(key_file)
12
+ key = nil
13
+ if ::File.exist?(key_file)
14
+ begin
15
+ key = ::OpenSSL::PKey.read(::IO.read(key_file))
16
+ rescue ::OpenSSL::PKey::PKeyError
17
+ key = nil
18
+ end
19
+ end
20
+
21
+ !key.nil?
22
+ end
23
+
24
+ def self.valid_certificate_file?(cert_file)
25
+ cert = nil
26
+ if ::File.exist?(cert_file)
27
+ cert = ::OpenSSL::X509::Certificate.new(::IO.read(cert_file))
28
+ end
29
+
30
+ !cert.nil?
31
+ end
32
+
33
+ def self.valid_certificate_directory?(path)
34
+ valid_key_file?(::File.join(path, 'server.key')) &&
35
+ valid_certificate_file?(::File.join(path, 'server.crt')) &&
36
+ ::File.exist?(::File.join(path, 'server.chain.crt')) &&
37
+ ::File.exist?(::File.join(path, 'server.fullchain.crt'))
38
+ end
39
+
40
+ def self.valid_next_directory?(path)
41
+ valid_key_file?(::File.join(path, 'server.key'))
42
+ end
43
+
44
+ def self.valid_directory?(path)
45
+ certificate_dir_regexp = /^\d{4}-\d{2}-\d{2}$/
46
+ has_next_dir = false
47
+ has_certificate_dir = false
48
+ dir = ::Dir.new(path)
49
+ dir.each do |x|
50
+ subdir_path = ::File.join(path, x)
51
+ if ::File.directory?(subdir_path)
52
+ if x == 'next'
53
+ has_next_dir = valid_next_directory?(subdir_path)
54
+ end
55
+
56
+ if !has_certificate_dir && !certificate_dir_regexp.match(x).nil?
57
+ has_certificate_dir = valid_certificate_directory?(subdir_path)
58
+ end
59
+ end
60
+ end
61
+
62
+ has_next_dir && has_certificate_dir
63
+ end
64
+
65
+ def self.list_entries(pwd)
66
+ dir = ::Dir.new(pwd)
67
+ stop_list = %w(
68
+ .
69
+ ..
70
+ .emergency
71
+ )
72
+ dir.select do |x|
73
+ path = ::File.join(pwd, x)
74
+ !stop_list.include?(x) && ::File.directory?(path) && valid_directory?(path)
75
+ end
76
+ end
77
+
78
+ def self.get_possible_items(path)
79
+ dir = ::Dir.new(path)
80
+ certificate_dir_regexp = /^\d{4}-\d{2}-\d{2}$/
81
+ dir.select do |x|
82
+ subdir_path = ::File.join(path, x)
83
+ ::File.directory?(subdir_path) && !certificate_dir_regexp.match(x).nil? && valid_certificate_directory?(subdir_path)
84
+ end
85
+ end
86
+
87
+ def self.find_valid_item(path)
88
+ get_possible_items(path).max_by do |x|
89
+ ::Date.parse(x)
90
+ end
91
+ end
92
+
93
+ def self.get_private_key(pwd, entry_name, item_name)
94
+ path = ::File.join(pwd, entry_name, item_name, 'server.key')
95
+ return ::IO.read(path).strip
96
+ end
97
+
98
+ def self.get_certificates(path)
99
+ certificates = []
100
+ ::IO.readlines(path).each do |ln|
101
+ if ln == "-----BEGIN CERTIFICATE-----\n"
102
+ certificates << ln
103
+ else
104
+ certificates[-1] += ln
105
+ end
106
+ end
107
+
108
+ certificates.map { |x| x.strip }
109
+ end
110
+
111
+ def self.get_fullchain(pwd, entry_name, item_name)
112
+ fullchain_file = ::File.join(pwd, entry_name, item_name, 'server.fullchain.crt')
113
+ get_certificates(fullchain_file)
114
+ end
115
+
116
+ def self.get_domain_list(pwd, entry_name, item_name)
117
+ cert_file = ::File.join(pwd, entry_name, item_name, 'server.crt')
118
+ cert = ::OpenSSL::X509::Certificate.new(::IO.read(cert_file))
119
+ domains = []
120
+
121
+ cert.extensions.each do |x|
122
+ if x.oid == 'subjectAltName'
123
+ domains += x.value.split(',').map { |x| x.split(':')[1] }
124
+ end
125
+ end
126
+
127
+ domains
128
+ end
129
+
130
+ def self.get_hpkp_pin(key_file)
131
+ key = ::OpenSSL::PKey.read(::IO.read(key_file))
132
+ public_key = nil
133
+ if key.class == ::OpenSSL::PKey::RSA
134
+ public_key = key.public_key
135
+ elsif key.class == ::OpenSSL::PKey::EC
136
+ public_key = ::OpenSSL::PKey::EC.new(key.group.curve_name)
137
+ public_key.public_key = key.public_key
138
+ end
139
+
140
+ ::Digest::SHA256.base64digest(public_key.to_der)
141
+ end
142
+
143
+ def self.get_hpkp_pin_list(pwd, entry_name, item_name)
144
+ pin_list = []
145
+ main_key_file = ::File.join(pwd, entry_name, item_name, 'server.key')
146
+ pin_list << get_hpkp_pin(main_key_file)
147
+
148
+ emergency_key_file = nil
149
+ key = ::OpenSSL::PKey.read(::IO.read(main_key_file))
150
+ if key.class == ::OpenSSL::PKey::RSA
151
+ emergency_key_file = ::File.join(pwd, '.emergency', 'rsa', 'server.key')
152
+ elsif key.class == ::OpenSSL::PKey::EC
153
+ emergency_key_file = ::File.join(pwd, '.emergency', 'ec', 'server.key')
154
+ end
155
+ if !emergency_key_file.nil? && ::File.file?(emergency_key_file)
156
+ pin_list.unshift(get_hpkp_pin(emergency_key_file))
157
+ end
158
+
159
+ next_key_file = ::File.join(pwd, entry_name, 'next', 'server.key')
160
+ pin_list << get_hpkp_pin(next_key_file)
161
+
162
+ pin_list
163
+ end
164
+
165
+ def self.get_scts(pwd, entry_name, item_name)
166
+ scts_dir = ::File.join(pwd, entry_name, item_name, 'scts')
167
+ h = {}
168
+ if ::File.directory?(scts_dir)
169
+ ::Dir.new(scts_dir).each do |x|
170
+ path = ::File.join(scts_dir, x)
171
+ if ::File.file?(path) && ::File.extname(path) == '.sct'
172
+ log_name = ::File.basename(path, '.sct')
173
+ h[log_name] = ::Base64.strict_encode64(::IO.read(path))
174
+ end
175
+ end
176
+ end
177
+
178
+ h
179
+ end
180
+
181
+ def self.jsonify_entry(pwd, entry_name)
182
+ item_name = find_valid_item(::File.join(pwd, entry_name))
183
+ {
184
+ name: entry_name,
185
+ domains: get_domain_list(pwd, entry_name, item_name),
186
+ chain: get_fullchain(pwd, entry_name, item_name),
187
+ private_key: get_private_key(pwd, entry_name, item_name),
188
+ hpkp_pins: get_hpkp_pin_list(pwd, entry_name, item_name),
189
+ scts: get_scts(pwd, entry_name, item_name)
190
+ }
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,30 @@
1
+ require 'thor'
2
+ require 'json'
3
+ require 'tls/cli/helpers'
4
+
5
+ module ChefCookbook
6
+ module TLS
7
+ module CLI
8
+ class Main < ::Thor
9
+ desc 'list', 'List directory'
10
+ option :pwd
11
+ def list
12
+ pwd = options[:pwd] || ::Dir.pwd
13
+ puts ::ChefCookbook::TLS::CLI::Helpers.list_entries(pwd)
14
+ end
15
+
16
+ desc 'jsonify ENTRY_NAME', 'Present ENTRY_NAME in JSON format'
17
+ option :pwd
18
+ def jsonify(entry_name)
19
+ pwd = options[:pwd] || ::Dir.pwd
20
+ puts ::JSON.pretty_generate(
21
+ ::ChefCookbook::TLS::CLI::Helpers.jsonify_entry(
22
+ pwd,
23
+ entry_name
24
+ )
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tls-cookbook-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Pyatkin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.19.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.19.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: TLS Cookbook CLI
56
+ email: aspyatkin@gmail.com
57
+ executables:
58
+ - tls-cookbook-cli
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - bin/tls-cookbook-cli
63
+ - lib/tls/cli.rb
64
+ - lib/tls/cli/helpers.rb
65
+ - lib/tls/cli/main.rb
66
+ homepage: https://github.com/aspyatkin/tls-cookbook-cli
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '2.3'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.6.11
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: TLS Cookbook CLI
90
+ test_files: []