amarillo 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a94e70709b0f4147b7c04711d1e1f58a54eeea7a743364ee4e8dc2d25984b83a
4
- data.tar.gz: 1f179ac5eea2d4919e22d09a44b491cbed3e2704128d9da71a9c00a10c177275
3
+ metadata.gz: b53b0e0803cc4bd89185cbfc67aeb3d468ab7f34988073d7c405f6e88f38e6fe
4
+ data.tar.gz: ef2b8ec288bae0acd3924aa61ded5ccde2f9915bb15ec664316693641d4391e8
5
5
  SHA512:
6
- metadata.gz: 3acd69905f4138ab59a2de3a1cb8493a6e1ea9363ccaaa622c8d565112850e78ce11ea6fad940052c7a4795a3e19980bb2f24f392775ac4aeab463f19ef6d013
7
- data.tar.gz: 0025b7ca2705e934750ca1bf267644ddc9fd610072ab64a2d5fc7c762b1c16d200b6ea1c6f74b87723c2a75fe6251982478ee72cfc9bc7c5b11581211b2d1a9d
6
+ metadata.gz: 16eb56677f677b3b752de0af6e1b67dc6495b01a494e84e8c935ea19db84f3b16a2bf71cbc67893025388b4289b39d9ebf84f86b2e81b0abf0742c7707bd2e33
7
+ data.tar.gz: 94af28ad358c6313fa4a43c00b0b31fc9f01d577556c90e8bf49c9396c06946c7d689abdd6fcd826b558fbcf74b3767b93c183055c2db7514510369033a8c1f6
data/bin/amarillo CHANGED
@@ -23,9 +23,18 @@
23
23
  require 'optparse'
24
24
  require 'fileutils'
25
25
  require 'amarillo'
26
+ require 'amarillo/environment'
26
27
 
27
28
  options = {}
28
29
  OptionParser.new do |opts|
30
+ opts.on("-i", "--initialize", "Initialize amarillo defaults") do |i|
31
+ options[:initialize] = i
32
+ end
33
+
34
+ opts.on("-r", "--renew", "Renew certificates") do |r|
35
+ options[:renew] = r
36
+ end
37
+
29
38
  opts.on("-z", "--zone ZONE", "Hosted zone") do |z|
30
39
  options[:zone] = z
31
40
  end
@@ -38,8 +47,8 @@ OptionParser.new do |opts|
38
47
  options[:name] = n
39
48
  end
40
49
 
41
- opts.on("-o", "--output-directory OUTPUT_PATH", "Output directory of certificates and keys") do |o|
42
- options[:certificate_path] = o
50
+ opts.on("-a", "--amarillo-home AMARILLO_HOME", "Home directory for configuration, keys, and certificates") do |o|
51
+ options[:amarillo_home] = a
43
52
  end
44
53
 
45
54
  opts.on("-h", "--help") do |h|
@@ -55,57 +64,56 @@ Usage: amarillo --zone ZONE --name COMMONNAME --email EMAIL [--output-path OUTP
55
64
  exit 0
56
65
  end
57
66
 
58
- if options[:zone].nil? or
59
- options[:email].nil? or
60
- options[:name].nil? then
67
+ if options[:initialize] then
68
+ e = Amarillo::Environment.new
61
69
 
62
- puts "Usage: amarillo --zone ZONE --name COMMONNAME --email EMAIL [--output-directory OUTPUT_PATH]"
70
+ e.init options[:zone], options[:email]
63
71
 
64
- exit -1
72
+ exit 0
65
73
  end
66
74
 
67
- if options[:output_path].nil?
68
- certificate_path = "/etc/ssl/amarillo"
69
- key_path = "/etc/ssl/amarillo/private"
75
+ e = Amarillo::Environment.new
76
+ e.load_config
77
+
78
+ if options[:zone].nil?
79
+ zone = e.config["defaults"]["zone"]
80
+ if zone.nil? or zone == '' then
81
+ puts "Error: Specify a default zone in config.yml or use --zone"
82
+ exit -1
83
+ end
70
84
  else
71
- certificate_path = options[:output_path]
72
- key_path = "#{certificate_path}/private"
85
+ zone = options[:zone]
73
86
  end
74
87
 
75
- # Try to create the certificate_path and key_path
76
- keypath_dirname = File.dirname(key_path)
77
- unless File.directory?(keypath_dirname)
78
- begin
79
- FileUtils.mkdir_p(keypath_dirname)
80
- rescue
81
- writableMessage = <<-"HEREDOC"
82
- Error: #{certificate_path} and #{key_path} are not writable directories to store keys and certificates.
83
- HEREDOC
84
- print writableMessage
85
- exit -1
86
- end
88
+ if options[:email].nil?
89
+ email = e.config["defaults"]["email"]
90
+ if email.nil? or email == '' then
91
+ puts "Error: Specify a default e-mail address in config.yml or use --email"
92
+ exit -1
93
+ end
94
+ else
95
+ email = options[:email]
87
96
  end
88
97
 
89
- # Check for existense of aws.env
90
- awsEnvPath = Pathname.new("/etc/amarillo/aws.env")
91
- if not awsEnvPath.exist? then
92
- awsEnvMessage = <<-"HEREDOC"
93
- Error: /etc/amarillo/aws.env AWS credentials file not found.
94
-
95
- /etc/amarillo/aws.env must exist and set both aws_access_key_id and aws_secret_access_key for an IAM user with AmazonRoute53FullAccess permissions.
96
-
97
- Example:
98
+ if options[:name].nil? and options[:renew].nil? then
99
+ puts "Usage: amarillo --name COMMONNAME [--zone ZONE] [--email EMAIL] [--amarillo-home AMARILLO_HOME]"
100
+ exit -1
101
+ else
102
+ name = options[:name]
103
+ end
98
104
 
99
- [default]
100
- aws_access_key_id = your_access_key_id
101
- aws_secret_access_key = your_secret_access_key
102
- HEREDOC
105
+ if options[:amarillo_home].nil?
106
+ amarillo_home = "/usr/local/etc/amarillo"
107
+ else
108
+ amarillo_home = options[:amarillo_home]
109
+ end
103
110
 
104
- print awsEnvMessage
111
+ y = Amarillo.new amarillo_home
105
112
 
106
- exit -1
113
+ if options[:renew] then
114
+ y.renewCertificates
115
+ else
116
+ y.requestCertificate zone, name, email, nil
107
117
  end
108
118
 
109
119
 
110
- y = Amarillo.new(certificate_path, key_path, awsEnvPath)
111
- y.requestCertificate(options[:zone], options[:name], options[:email])
data/lib/amarillo.rb CHANGED
@@ -29,14 +29,23 @@ require 'openssl' # Key Generation
29
29
  require 'aws-sdk-core' # Credentials
30
30
  require 'aws-sdk-route53' # Route 53
31
31
  require 'resolv' # DNS Resolvers
32
+ require 'yaml' # YAML
32
33
 
33
34
  class Amarillo
34
35
 
35
- def initialize(certificatePath, keyPath, awsEnvPath)
36
+ def initialize(amarilloHome)
36
37
 
37
- @certificatePath = certificatePath
38
- @keyPath = keyPath
39
- @awsEnvPath = awsEnvPath
38
+ @environment = Amarillo::Environment.new(amarilloHome: amarilloHome)
39
+
40
+ if not @environment.verify then raise "Cannot initialize amarillo" end
41
+
42
+ @environment.load_config
43
+
44
+ @certificatePath = @environment.certificatePath
45
+ @keyPath = @environment.keyPath
46
+ @config = @environment.config
47
+ @awsEnvFile = @environment.awsEnvFile
48
+ @configsPath = @environment.configsPath
40
49
 
41
50
  @logger = Logger.new(STDOUT)
42
51
  @logger.level = Logger::INFO
@@ -65,19 +74,35 @@ class Amarillo
65
74
  valid
66
75
  end
67
76
 
68
- def requestCertificate(zone, commonName, email)
77
+ def get_route53
78
+ shared_creds = Aws::SharedCredentials.new(path: "#{@awsEnvFile}")
79
+
80
+ Aws.config.update(credentials: shared_creds)
81
+
82
+ region = @config["defaults"]["region"] ? @config["defaults"]["region"] : 'us-east-2'
83
+ @route53 = Aws::Route53::Client.new(region: region)
84
+ @hzone = @route53.list_hosted_zones(max_items: 100).hosted_zones.detect { |z| z.name == "#{@zone}." }
85
+
86
+ end
87
+
88
+ def requestCertificate(zone, commonName, email, key_type)
89
+
90
+ @zone = zone
91
+
92
+ acmeUrl = @config["defaults"]["acme_url"] ? @config["defaults"]["acme_url"] : 'https://acme-v02.api.letsencrypt.org/directory'
93
+
94
+ # Load private key
69
95
 
70
- @logger.info "Generating 4096-bit RSA private key"
71
- key = OpenSSL::PKey::RSA.new(4096)
72
- client = Acme::Client.new(
73
- private_key: key,
74
- directory: 'https://acme-v02.api.letsencrypt.org/directory'
75
- )
96
+ @logger.info "Loading 4096-bit RSA private key for Let's Encrypt account"
97
+ @logger.info "Let's Encrypt directory set to #{acmeUrl}"
76
98
 
77
- account = client.new_account(
78
- contact: "mailto:#{email}",
79
- terms_of_service_agreed: true
80
- )
99
+ key = OpenSSL::PKey::RSA.new File.read "#{@keyPath}/letsencrypt.key"
100
+
101
+ client = Acme::Client.new private_key: key,
102
+ directory: acmeUrl
103
+
104
+ account = client.new_account contact: "mailto:#{email}",
105
+ terms_of_service_agreed: true
81
106
 
82
107
  # Generate a certificate order
83
108
  @logger.info "Creating certificate order request for #{commonName}"
@@ -90,16 +115,7 @@ class Amarillo
90
115
 
91
116
  @logger.info "Challenge value for #{commonName} in #{zone} zone is #{challengeValue}"
92
117
 
93
- # Update Route 53
94
-
95
- shared_creds = Aws::SharedCredentials.new(path: "#{@awsEnvPath}")
96
- Aws.config.update(credentials: shared_creds)
97
-
98
- # TODO: Allow the user to set the region
99
- route53 = Aws::Route53::Client.new(region: 'us-east-2')
100
- hzone = route53.list_hosted_zones(max_items: 100)
101
- .hosted_zones
102
- .detect { |z| z.name == "#{zone}." }
118
+ self.get_route53
103
119
 
104
120
  change = {
105
121
  action: 'UPSERT',
@@ -114,30 +130,20 @@ class Amarillo
114
130
  }
115
131
 
116
132
  options = {
117
- hosted_zone_id: hzone.id,
133
+ hosted_zone_id: @hzone.id,
118
134
  change_batch: {
119
135
  changes: [change]
120
136
  }
121
137
  }
122
138
 
123
- route53.change_resource_record_sets(options)
124
-
125
- nameservers = []
139
+ @route53.change_resource_record_sets(options)
126
140
 
127
- @logger.info "Looking up nameservers for #{zone}"
141
+ nameservers = @environment.get_zone_nameservers
128
142
 
129
- Resolv::DNS.open(nameserver: '9.9.9.9') do |dns|
130
- while nameservers.length == 0
131
- nameservers = dns.getresources(
132
- zone,
133
- Resolv::DNS::Resource::IN::NS
134
- ).map(&:name).map(&:to_s)
135
- end
136
- end
137
-
138
- @logger.info "Waiting for DNS record to propogate"
143
+ @logger.info "Waiting for DNS record to propagate"
139
144
  while !check_dns(commonName, nameservers, challengeValue)
140
- sleep 1
145
+ sleep 2
146
+ @logger.info "Still waiting..."
141
147
  end
142
148
 
143
149
  authorization.dns.request_validation
@@ -150,15 +156,40 @@ class Amarillo
150
156
  authorization.dns.reload
151
157
  end
152
158
 
153
- @logger.info "Requesting certificate..."
159
+ @logger.info "Generating key"
160
+
161
+ # Create certificate yml
162
+ certConfig = {
163
+ "commonName" => commonName,
164
+ "email" => email,
165
+ "zone" => zone
166
+ }
154
167
 
155
- cert_key = OpenSSL::PKey::RSA.new(4096)
156
- csr = Acme::Client::CertificateRequest.new(
157
- private_key: cert_key,
158
- names: [commonName]
159
- )
168
+ if key_type
169
+ certConfig["key_type"] = key_type
170
+ else
171
+ key_type = @config["defaults"]["key_type"]
172
+ certConfig["key_type"] = key_type
173
+ end
160
174
 
161
- order.finalize(csr: csr)
175
+ type, args = key_type.split(',')
176
+
177
+ if type == 'ec' then
178
+ certPrivateKey = OpenSSL::PKey::EC.new(args).generate_key
179
+ elsif type == 'rsa' then
180
+ certPrivateKey = OpenSSL::PKey::RSA.new(args)
181
+ end
182
+
183
+ @logger.info "Requesting certificate..."
184
+ csr = Acme::Client::CertificateRequest.new private_key: certPrivateKey,
185
+ names: [commonName]
186
+
187
+ begin
188
+ order.finalize(csr: csr)
189
+ rescue
190
+ @logger.error("ERROR")
191
+ self.cleanup label, record_type, challengeValue
192
+ end
162
193
 
163
194
  sleep(1) while order.status == 'processing'
164
195
 
@@ -168,8 +199,9 @@ class Amarillo
168
199
  @logger.info "Saving private key to #{keyOutputPath}"
169
200
 
170
201
  File.open(keyOutputPath, "w") do |f|
171
- f.puts cert_key.to_pem.to_s
202
+ f.puts certPrivateKey.to_pem.to_s
172
203
  end
204
+ File.chmod(0600, keyOutputPath)
173
205
 
174
206
  @logger.info "Saving certificate to #{certOutputPath}"
175
207
 
@@ -177,6 +209,14 @@ class Amarillo
177
209
  f.puts order.certificate
178
210
  end
179
211
 
212
+ certConfigFile = "#{@configsPath}/#{commonName}.yml"
213
+ File.write(certConfigFile, certConfig.to_yaml)
214
+
215
+ self.cleanup label, record_type, challengeValue
216
+
217
+ end
218
+
219
+ def cleanup(label, record_type, challengeValue)
180
220
  @logger.info "Cleaning up..."
181
221
 
182
222
  change = {
@@ -192,12 +232,45 @@ class Amarillo
192
232
  }
193
233
 
194
234
  options = {
195
- hosted_zone_id: hzone.id,
235
+ hosted_zone_id: @hzone.id,
196
236
  change_batch: {
197
237
  changes: [change]
198
238
  }
199
239
  }
200
240
 
201
- route53.change_resource_record_sets(options)
241
+ @route53.change_resource_record_sets(options)
242
+ end
243
+
244
+ def renewCertificate(zone, commonName, email)
245
+
246
+ end
247
+
248
+ def renewCertificates
249
+ t = Time.now
250
+ @logger.info "Renewing certificates"
251
+
252
+ Dir["#{@configsPath}/*.yml"].each do |c|
253
+ config = YAML.load(File.read(c))
254
+
255
+ cn = config["commonName"]
256
+ email = config["email"]
257
+ zone = config["zone"]
258
+ key_type = config["key_type"]
259
+
260
+ certificatePath = "#{@certificatePath}/#{cn}.crt"
261
+ raw = File.read certificatePath
262
+ certificate = OpenSSL::X509::Certificate.new raw
263
+ daysToExpiration = (certificate.not_after - t).to_i / (24 * 60 * 60)
264
+
265
+ if daysToExpiration < 30 then
266
+ @logger.info "#{cn} certificate needs to be renewed"
267
+ self.requestCertificate zone, cn, email, key_type
268
+ else
269
+ @logger.info "#{cn} certificate does not need to be renewed"
270
+ end
271
+ end
202
272
  end
203
273
  end
274
+
275
+
276
+ require 'amarillo/environment'
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Inspired by Pete Keen's (pete@petekeen.net) post
4
+ # https://www.petekeen.net/lets-encrypt-without-certbot
5
+ #
6
+ # Copyright 2021 iAchieved.it LLC
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ require 'logger'
27
+ require 'yaml'
28
+ require 'aws-sdk-core'
29
+
30
+ DefaultAmarilloHome = "/usr/local/etc/amarillo/"
31
+
32
+ class Amarillo::Environment
33
+
34
+ attr_reader :certificatePath, :keyPath, :configPath, :configsPath, :config, :awsEnvFile
35
+
36
+ def initialize(amarilloHome: DefaultAmarilloHome)
37
+
38
+ @logger = Logger.new(STDOUT)
39
+ @logger.level = Logger::INFO
40
+
41
+ @amarilloHome = amarilloHome
42
+ @certificatePath = amarilloHome + "/certificates"
43
+ @keyPath = amarilloHome + "/keys"
44
+ @configPath = amarilloHome
45
+ @configsPath = amarilloHome + "/configs"
46
+ @configFile = amarilloHome + "/config.yml"
47
+ @awsEnvFile = amarilloHome + "/aws.env"
48
+
49
+ end
50
+
51
+ # Public method to create default configuration files
52
+ def init(zone = nil, email = nil)
53
+
54
+ unless File.exist?(@configsPath) and File.directory?(@configsPath)
55
+ begin
56
+ @logger.info "Creating #{@configsPath} directory"
57
+ FileUtils.mkpath(@configsPath)
58
+ rescue
59
+ @logger.error("Cannot create #{@configsPath} directory")
60
+ return false
61
+ end
62
+ end
63
+
64
+ unless File.exist?(@certificatePath) and File.directory?(@certificatePath)
65
+ begin
66
+ @logger.info "Creating #{@certificatePath} directory"
67
+ FileUtils.mkpath(@certificatePath)
68
+ rescue
69
+ @logger.error("Cannot create #{@certificatePath} directory")
70
+ return false
71
+ end
72
+ end
73
+
74
+ unless File.exist?(@keyPath) and File.directory?(@keyPath)
75
+ begin
76
+ @logger.info "Creating #{@keyPath} directory"
77
+ FileUtils.mkpath(@keyPath)
78
+ rescue
79
+ @logger.error("Cannot create #{@keyPath} directory")
80
+ return false
81
+ end
82
+ end
83
+
84
+ # Create aws.env
85
+ unless File.exist?(@awsEnvFile) then
86
+ awsEnv = <<-HEREDOC
87
+ [default]
88
+ aws_access_key_id =
89
+ aws_secret_access_key =
90
+ HEREDOC
91
+ @logger.info("Creating blank #{@awsEnvFile}")
92
+ @logger.warn("NOTE: aws_access_key_id and aws_secret_access_key must be specified in this file.")
93
+ File.write(@awsEnvFile, awsEnv)
94
+ else
95
+ @logger.info("Refusing to overwrite #{@awsEnvFile}")
96
+ end
97
+
98
+ # Create config.yml
99
+ unless File.exist?(@configFile) then
100
+ @logger.info("Creating default configuration #{@configFile}")
101
+ config = {
102
+ "defaults" => {
103
+ "region" => 'us-east-2',
104
+ "profile" => 'default',
105
+ "email" => email,
106
+ "zone" => zone,
107
+ "nameservers" => ['208.67.222.222', '9.9.9.9'],
108
+ "key_type" => 'ec,secp384r1'
109
+ }}
110
+ File.write(@configFile, config.to_yaml)
111
+ else
112
+ @logger.info("Refusing to overwrite #{@configFile}")
113
+ end
114
+
115
+ # Create RSA private key for Let's Encrypt account
116
+ privateKeyPath = "#{@keyPath}/letsencrypt.key"
117
+
118
+ unless File.exist? privateKeyPath then
119
+ @logger.info "Generating 4096-bit RSA private key for Let's Encrypt account"
120
+
121
+ privateKey = OpenSSL::PKey::RSA.new(4096)
122
+
123
+
124
+ File.open(privateKeyPath, "w") do |f|
125
+ f.puts privateKey.to_pem.to_s
126
+ end
127
+ File.chmod(0400, privateKeyPath)
128
+ end
129
+ end
130
+
131
+ #
132
+ # Verify paths exist and are writable
133
+ # Verify aws.env exists and is formatted correctly
134
+ # Verify config.yml exists and is formatted correctly
135
+ #
136
+ def verify
137
+ @logger.info "Verifying amarillo environment"
138
+ if not verify_env() then return false end
139
+ if not verify_awsenv() then return false end
140
+ if not verify_config() then return false end
141
+ return true
142
+ end
143
+
144
+ def verify_env
145
+ unless File.stat(@certificatePath).writable? then
146
+ @logger.error(@certificatePath + " is not writable")
147
+ return false
148
+ end
149
+
150
+ unless File.stat(@keyPath).writable? then
151
+ @logger.error(@keyPath + " is not writable")
152
+ return false
153
+ end
154
+
155
+ return true
156
+ end
157
+
158
+ def verify_awsenv()
159
+ awsEnvFile = Pathname.new(@awsEnvFile)
160
+ if not awsEnvFile.exist? then
161
+ @logger.error("#{awsEnvFile} does not exist")
162
+ return false
163
+ end
164
+
165
+ awsCredentials = Aws::SharedCredentials.new(path: "#{@awsEnvFile}")
166
+
167
+ if awsCredentials.credentials.access_key_id.length != 20 then
168
+ @logger.error("#{@awsEnvFile} aws_access_key_id does not appear to be valid")
169
+ return false
170
+ end
171
+
172
+ if awsCredentials.credentials.secret_access_key.length != 40 then
173
+ @logger.error("#{@awsEnvFile} aws_secret_access_key does not appear to be valid")
174
+ return false
175
+ end
176
+
177
+ return true
178
+ end
179
+
180
+ def verify_config()
181
+ if not File.exist?(@configFile) then
182
+ @logger.error("#{@configFile} does not exist")
183
+ return false
184
+ end
185
+
186
+ begin
187
+ YAML.load(File.read(@configFile))
188
+ rescue
189
+ @logger.error("Unable to load configuration file")
190
+ return false
191
+ end
192
+
193
+ return true
194
+ end
195
+
196
+ def load_config()
197
+ if verify_config() then
198
+ @config = YAML.load(File.read(@configFile))
199
+ end
200
+ end
201
+
202
+ def get_zone_nameservers
203
+
204
+ self.load_config
205
+
206
+ nameservers = @config["defaults"]["nameservers"]
207
+ zone = @config["defaults"]["zone"]
208
+
209
+ @logger.info "Looking up nameservers for #{zone}"
210
+
211
+ zone_nameservers = []
212
+ Resolv::DNS.open(nameserver: nameservers) do |dns|
213
+ while zone_nameservers.length == 0
214
+ zone_nameservers = dns.getresources(
215
+ zone,
216
+ Resolv::DNS::Resource::IN::NS
217
+ ).map(&:name).map(&:to_s)
218
+ end
219
+ end
220
+
221
+ @logger.info "Found #{zone_nameservers.length} nameservers for zone #{zone}: #{zone_nameservers}"
222
+
223
+ return zone_nameservers
224
+ end
225
+
226
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amarillo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - iAchieved.it LLC
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-28 00:00:00.000000000 Z
11
+ date: 2021-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acme-client
@@ -75,11 +75,12 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - bin/amarillo
77
77
  - lib/amarillo.rb
78
+ - lib/amarillo/environment.rb
78
79
  homepage: https://github.com/iachievedit/amarillo
79
80
  licenses:
80
81
  - MIT
81
82
  metadata: {}
82
- post_install_message:
83
+ post_install_message:
83
84
  rdoc_options: []
84
85
  require_paths:
85
86
  - lib
@@ -94,8 +95,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
95
  - !ruby/object:Gem::Version
95
96
  version: '0'
96
97
  requirements: []
97
- rubygems_version: 3.0.3
98
- signing_key:
98
+ rubygems_version: 3.2.3
99
+ signing_key:
99
100
  specification_version: 4
100
101
  summary: Amarillo
101
102
  test_files: []