diff options
| -rw-r--r-- | lib/leap_cli.rb | 1 | ||||
| -rw-r--r-- | lib/leap_cli/commands/ca.rb | 71 | ||||
| -rw-r--r-- | lib/leap_cli/commands/deploy.rb | 2 | ||||
| -rw-r--r-- | lib/leap_cli/core_ext/time.rb | 86 | ||||
| -rw-r--r-- | lib/leap_cli/version.rb | 4 | 
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'] | 
