summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2015-01-28 22:19:25 -0800
committerelijah <elijah@riseup.net>2015-01-28 22:19:25 -0800
commite01bf415d80883c369dfd4247aae401fde421b80 (patch)
treed648c6fcf5d6c2c4fd62bfed3f02c7b54faa3c93
parent5676272e90dc69234181eaa5b2e96447f904966e (diff)
cert generation: (1) auto generate certs (2) actually support configuring expiration times. REQUIRES platform >= 0.6.1. closes #6387
-rw-r--r--lib/leap_cli.rb1
-rw-r--r--lib/leap_cli/commands/ca.rb71
-rw-r--r--lib/leap_cli/commands/deploy.rb2
-rw-r--r--lib/leap_cli/core_ext/time.rb86
-rw-r--r--lib/leap_cli/version.rb4
5 files changed, 130 insertions, 34 deletions
diff --git a/lib/leap_cli.rb b/lib/leap_cli.rb
index f07fd25..cbd8ac5 100644
--- a/lib/leap_cli.rb
+++ b/lib/leap_cli.rb
@@ -26,6 +26,7 @@ require 'leap_cli/core_ext/nil'
require 'leap_cli/core_ext/string'
require 'leap_cli/core_ext/json'
require 'leap_cli/core_ext/yaml'
+require 'leap_cli/core_ext/time'
require 'leap_cli/log'
require 'leap_cli/path'
diff --git a/lib/leap_cli/commands/ca.rb b/lib/leap_cli/commands/ca.rb
index 357792f..785e043 100644
--- a/lib/leap_cli/commands/ca.rb
+++ b/lib/leap_cli/commands/ca.rb
@@ -28,22 +28,7 @@ module LeapCli; module Commands
cert.command :update do |update|
update.switch 'force', :desc => 'Always generate new certificates', :negatable => false
update.action do |global_options,options,args|
- assert_files_exist! :ca_cert, :ca_key, :msg => 'Run `leap cert ca` to create them'
- assert_config! 'provider.ca.server_certificates.bit_size'
- assert_config! 'provider.ca.server_certificates.digest'
- assert_config! 'provider.ca.server_certificates.life_span'
- assert_config! 'common.x509.use'
-
- nodes = manager.filter!(args)
- nodes.each_node do |node|
- warn_if_commercial_cert_will_soon_expire(node)
- if !node.x509.use
- remove_file!([:node_x509_key, node.name])
- remove_file!([:node_x509_cert, node.name])
- elsif options[:force] || cert_needs_updating?(node)
- generate_cert_for_node(node)
- end
- end
+ update_certificates(manager.filter!(args), options)
end
end
@@ -139,7 +124,7 @@ module LeapCli; module Commands
cert = csr.to_cert
cert.serial_number.number = cert_serial_number(domain)
cert.not_before = yesterday
- cert.not_after = years_from_yesterday(1)
+ cert.not_after = yesterday.advance(:years => 1)
cert.parent = ca_root
cert.sign! domain_test_signing_profile
write_file! [:commercial_cert, domain], cert.to_pem
@@ -158,6 +143,29 @@ module LeapCli; module Commands
end
end
+ protected
+
+ #
+ # will generate new certificates for the specified nodes, if needed.
+ #
+ def update_certificates(nodes, options={})
+ assert_files_exist! :ca_cert, :ca_key, :msg => 'Run `leap cert ca` to create them'
+ assert_config! 'provider.ca.server_certificates.bit_size'
+ assert_config! 'provider.ca.server_certificates.digest'
+ assert_config! 'provider.ca.server_certificates.life_span'
+ assert_config! 'common.x509.use'
+
+ nodes.each_node do |node|
+ warn_if_commercial_cert_will_soon_expire(node)
+ if !node.x509.use
+ remove_file!([:node_x509_key, node.name])
+ remove_file!([:node_x509_cert, node.name])
+ elsif options[:force] || cert_needs_updating?(node)
+ generate_cert_for_node(node)
+ end
+ end
+ end
+
private
def generate_new_certificate_authority(key_file, cert_file, common_name)
@@ -179,7 +187,7 @@ module LeapCli; module Commands
# set expiration
root.not_before = yesterday
- root.not_after = years_from_yesterday(provider.ca.life_span.to_i)
+ root.not_after = yesterday_advance(provider.ca.life_span)
# generate private key
root.serial_number.number = 1
@@ -203,7 +211,7 @@ module LeapCli; module Commands
return true
else
cert = load_certificate_file([:node_x509_cert, node.name])
- if cert.not_after < months_from_yesterday(2)
+ if cert.not_after < Time.now.advance(:months => 2)
log :updating, "cert for node '#{node.name}' because it will expire soon"
return true
end
@@ -242,7 +250,7 @@ module LeapCli; module Commands
if cert.not_after < Time.now.utc
log :error, "the commercial certificate '#{path}' has EXPIRED! " +
"You should renew it with `leap cert csr --domain #{domain}`."
- elsif cert.not_after < months_from_yesterday(2)
+ elsif cert.not_after < Time.now.advance(:months => 2)
log :warning, "the commercial certificate '#{path}' will expire soon. "+
"You should renew it with `leap cert csr --domain #{domain}`."
end
@@ -261,7 +269,7 @@ module LeapCli; module Commands
# set expiration
cert.not_before = yesterday
- cert.not_after = years_from_yesterday(provider.ca.server_certificates.life_span.to_i)
+ cert.not_after = yesterday_advance(provider.ca.server_certificates.life_span)
# generate key
cert.key_material.generate_key(provider.ca.server_certificates.bit_size)
@@ -283,7 +291,7 @@ module LeapCli; module Commands
cert.serial_number.number = cert_serial_number(provider.domain)
cert.subject.common_name = [prefix, random_common_name(provider.domain)].join
cert.not_before = yesterday
- cert.not_after = years_from_yesterday(1)
+ cert.not_after = yesterday.advance(:years => 1)
cert.key_material.generate_key(1024) # just for testing, remember!
cert.parent = client_ca_root
cert.sign! client_test_signing_profile
@@ -492,16 +500,15 @@ module LeapCli; module Commands
Time.utc t.year, t.month, t.day
end
- def years_from_yesterday(num)
- t = yesterday
- Time.utc t.year + num, t.month, t.day
- end
-
- def months_from_yesterday(num)
- t = yesterday
- date = Date.new t.year, t.month, t.day
- date = date >> num # >> is months in the future operator
- Time.utc date.year, date.month, date.day
+ def yesterday_advance(string)
+ number, unit = string.split(' ')
+ unless ['years', 'months', 'days', 'hours', 'minutes'].include? unit
+ bail("The time property '#{string}' is missing a unit (one of: years, months, days, hours, minutes).")
+ end
+ unless number.to_i.to_s == number
+ bail("The time property '#{string}' is missing a number.")
+ end
+ yesterday.advance(unit.to_sym => number.to_i)
end
end; end
diff --git a/lib/leap_cli/commands/deploy.rb b/lib/leap_cli/commands/deploy.rb
index dbbaaba..204be5f 100644
--- a/lib/leap_cli/commands/deploy.rb
+++ b/lib/leap_cli/commands/deploy.rb
@@ -54,6 +54,8 @@ module LeapCli
# compile hiera files for all the nodes in every environment that is
# being deployed and only those environments.
compile_hiera_files(manager.filter(environments))
+ # update server certificates if needed
+ update_certificates(nodes)
ssh_connect(nodes, connect_options(options)) do |ssh|
ssh.leap.log :checking, 'node' do
diff --git a/lib/leap_cli/core_ext/time.rb b/lib/leap_cli/core_ext/time.rb
new file mode 100644
index 0000000..fef6c5d
--- /dev/null
+++ b/lib/leap_cli/core_ext/time.rb
@@ -0,0 +1,86 @@
+#
+# The following methods are copied from ActiveSupport's Time extension:
+# activesupport/lib/active_support/core_ext/time/calculations.rb
+#
+
+class Time
+
+ #
+ # Uses Date to provide precise Time calculations for years, months, and days
+ # according to the proleptic Gregorian calendar. The options parameter takes
+ # a hash with any of these keys: :years, :months, :weeks, :days, :hours,
+ # :minutes, :seconds.
+ #
+ def advance(options)
+ unless options[:weeks].nil?
+ options[:weeks], partial_weeks = options[:weeks].divmod(1)
+ options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
+ end
+
+ unless options[:days].nil?
+ options[:days], partial_days = options[:days].divmod(1)
+ options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
+ end
+
+ d = to_date.advance(options)
+ d = d.gregorian if d.julian?
+ time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
+ seconds_to_advance = options.fetch(:seconds, 0) +
+ options.fetch(:minutes, 0) * 60 +
+ options.fetch(:hours, 0) * 3600
+
+ if seconds_to_advance.zero?
+ time_advanced_by_date
+ else
+ time_advanced_by_date.since(seconds_to_advance)
+ end
+ end
+
+ def since(seconds)
+ self + seconds
+ rescue
+ to_datetime.since(seconds)
+ end
+
+ #
+ # Returns a new Time where one or more of the elements have been changed
+ # according to the options parameter. The time options (:hour, :min, :sec,
+ # :usec) reset cascadingly, so if only the hour is passed, then minute, sec,
+ # and usec is set to 0. If the hour and minute is passed, then sec and usec
+ # is set to 0. The options parameter takes a hash with any of these keys:
+ # :year, :month, :day, :hour, :min, :sec, :usec.
+ #
+ def change(options)
+ new_year = options.fetch(:year, year)
+ new_month = options.fetch(:month, month)
+ new_day = options.fetch(:day, day)
+ new_hour = options.fetch(:hour, hour)
+ new_min = options.fetch(:min, options[:hour] ? 0 : min)
+ new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
+ new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
+
+ if utc?
+ ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
+ elsif zone
+ ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
+ else
+ ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
+ end
+ end
+
+end
+
+class Date
+
+ # activesupport/lib/active_support/core_ext/date/calculations.rb
+ def advance(options)
+ options = options.dup
+ d = self
+ d = d >> options.delete(:years) * 12 if options[:years]
+ d = d >> options.delete(:months) if options[:months]
+ d = d + options.delete(:weeks) * 7 if options[:weeks]
+ d = d + options.delete(:days) if options[:days]
+ d
+ end
+
+end
diff --git a/lib/leap_cli/version.rb b/lib/leap_cli/version.rb
index 909131b..c945ea0 100644
--- a/lib/leap_cli/version.rb
+++ b/lib/leap_cli/version.rb
@@ -1,7 +1,7 @@
module LeapCli
unless defined?(LeapCli::VERSION)
- VERSION = '1.6.2'
- COMPATIBLE_PLATFORM_VERSION = '0.6.0'..'1.99'
+ VERSION = '1.6.3'
+ COMPATIBLE_PLATFORM_VERSION = '0.6.1'..'1.99'
SUMMARY = 'Command line interface to the LEAP platform'
DESCRIPTION = 'The command "leap" can be used to manage a bevy of servers running the LEAP platform from the comfort of your own home.'
LOAD_PATHS = ['lib', 'vendor/certificate_authority/lib', 'vendor/rsync_command/lib']