class Puppetserver::Ca::Config::Puppet

Provides an interface for asking for Puppet settings w/o loading Puppet. Includes a simple ini parser that will ignore Puppet’s more complicated conventions.

Constants

TTL_FORMAT

A regex describing valid formats with groups for capturing the value and units

TTL_UNITMAP

How we convert from various units to seconds.

Attributes

errors[R]
settings[R]

Public Class Methods

new(supplied_config_path = nil) click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 35
def initialize(supplied_config_path = nil)
  @using_default_location = !supplied_config_path
  @config_path = supplied_config_path || user_specific_puppet_config

  @settings = nil
  @errors = []
end
parse(config_path, logger) click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 26
def self.parse(config_path, logger)
  instance = new(config_path)
  instance.load(logger: logger)

  return instance
end

Public Instance Methods

default_certname() click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 79
def default_certname
  hostname = Facter.value(:hostname)
  domain = Facter.value(:domain)
  if domain and domain != ''
    fqdn = [hostname, domain].join('.')
  else
    fqdn = hostname
  end
  fqdn.chomp('.')
end
load(cli_overrides: {}, logger:, ca_dir_warn: true) click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 57
def load(cli_overrides: {}, logger:, ca_dir_warn: true)
  if explicitly_given_config_file_or_default_config_exists?
    results = parse_text(File.read(@config_path))
  end

  results ||= {}
  results[:main] ||= {}
  # The [master] config section is deprecated
  # We now favor [server], but support both for backwards compatibility
  results[:master] ||= {}
  results[:server] ||= {}
  results[:agent] ||= {}

  overrides = results[:agent].merge(results[:main]).merge(results[:master]).merge(results[:server])
  overrides.merge!(cli_overrides)
  if overrides[:masterport]
    overrides[:serverport] ||= overrides.delete(:masterport)
  end

  @settings = resolve_settings(overrides, logger, ca_dir_warn: ca_dir_warn).freeze
end
parse_text(text) click to toggle source

Parse an inifile formatted String. Only captures word character class keys/section names but nearly any character values (excluding leading whitespace) up to one of whitespace, opening curly brace, or hash sign (Our concern being to capture filesystem path values).

ca_root and root_ca_name values may include whitespace

Put values without a section into :main.

Return Hash of Symbol section names with Symbol setting keys and String values.

# File lib/puppetserver/ca/config/puppet.rb, line 204
def parse_text(text)
  res = {}
  current_section = :main
  text.each_line do |line|
    case line
    when /^\s*\[(\w+)\].*/
      current_section = $1.to_sym
    when /^\s*(\w+)\s*=\s*(.+?)\s*(?=[{#]|$)/
      # Using a Hash with a default key breaks RSpec expectations.
      res[current_section] ||= {}
      res[current_section][$1.to_sym] =
        if [:ca_name, :root_ca_name].include?($1.to_sym)
          $2
        else
          $2.split(' ')[0]
        end
    end
  end

  res
end
resolve_settings(overrides = {}, logger, ca_dir_warn: true) click to toggle source

Resolve settings from default values, with any overrides for the specific settings or their dependent settings (ssldir, cadir) taken into account.

# File lib/puppetserver/ca/config/puppet.rb, line 92
def resolve_settings(overrides = {}, logger, ca_dir_warn: true)
  unresolved_setting = /\$[a-z_]+/

  # Returning the key for unknown keys (rather than nil) is required to
  # keep unknown settings in the string for later verification.
  substitutions = Hash.new {|h, k| k }
  settings = {}

  # Order for base settings here matters!
  # These need to be evaluated before we can construct their dependent
  # defaults below
  base_defaults = [
    [:confdir, user_specific_puppet_confdir],
    [:ssldir,'$confdir/ssl'],
    [:certdir, '$ssldir/certs'],
    [:certname, default_certname],
    [:server, 'puppet'],
    [:serverport, '8140'],
    [:privatekeydir, '$ssldir/private_keys'],
    [:publickeydir, '$ssldir/public_keys'],
  ]

  dependent_defaults = {
    :ca_name => 'Puppet CA: $certname',
    :root_ca_name => "Puppet Root CA: #{SecureRandom.hex(7)}",
    :keylength => 4096,
    :cacert => '$cadir/ca_crt.pem',
    :cakey => '$cadir/ca_key.pem',
    :capub => '$cadir/ca_pub.pem',
    :csr_attributes => '$confdir/csr_attributes.yaml',
    :rootkey => '$cadir/root_key.pem',
    :cacrl => '$cadir/ca_crl.pem',
    :serial => '$cadir/serial',
    :cert_inventory => '$cadir/inventory.txt',
    :ca_server => '$server',
    :ca_port => '$serverport',
    :localcacert => '$certdir/ca.pem',
    :hostcrl => '$ssldir/crl.pem',
    :hostcert => '$certdir/$certname.pem',
    :hostprivkey => '$privatekeydir/$certname.pem',
    :hostpubkey => '$publickeydir/$certname.pem',
    :ca_ttl => '15y',
    :certificate_revocation => 'true',
    :signeddir => '$cadir/signed',
    :server_list => '',
  }

  # This loops through the base defaults and gives each setting a
  # default if the value isn't specified in the config file. Default
  # values given may depend upon the value of a previous base setting,
  # thus the creation of the substitution hash.
  base_defaults.each do |setting_name, default_value|
    substitution_name = '$' + setting_name.to_s
    setting_value = overrides.fetch(setting_name, default_value)
    subbed_value = setting_value.sub(unresolved_setting, substitutions)
    settings[setting_name] = substitutions[substitution_name] = subbed_value
  end

  cadir = find_cadir(overrides.fetch(:cadir, false),
                     settings[:confdir],
                     settings[:ssldir],
                     logger,
                     ca_dir_warn)
  settings[:cadir] = substitutions['$cadir'] = cadir


  dependent_defaults.each do |setting_name, default_value|
    setting_value = overrides.fetch(setting_name, default_value)
    settings[setting_name] = setting_value
  end

  # If subject-alt-names are provided, we need to add the certname in addition
  overrides[:dns_alt_names] << ',$certname' if overrides[:dns_alt_names]

  # rename dns_alt_names to subject_alt_names now that we support IP alt names
  settings[:subject_alt_names] = overrides.fetch(:dns_alt_names, "")

  # Some special cases where we need to manipulate config settings:
  settings[:ca_ttl] = munge_ttl_setting(settings[:ca_ttl])
  settings[:certificate_revocation] = parse_crl_usage(settings[:certificate_revocation])
  settings[:subject_alt_names] = Puppetserver::Ca::Utils::Config.munge_alt_names(settings[:subject_alt_names])
  settings[:keylength] = settings[:keylength].to_i
  settings[:server_list] = settings[:server_list].
                             split(/\s*,\s*/).
                             map {|entry| entry.split(":") }

  update_for_server_list!(settings)

  settings.each do |key, value|
    next unless value.is_a? String
    settings[key] = value.gsub(unresolved_setting, substitutions)
    if match = settings[key].match(unresolved_setting)
      @errors << "Could not parse #{match[0]} in #{value}, " +
                 'valid settings to be interpolated are ' +
                 '$ssldir, $certdir, $cadir, $certname, $server, or $masterport'
    end
  end

  return settings
end
user_specific_puppet_confdir() click to toggle source

Return the correct confdir. We check for being root on *nix, else the user path. We do not include a check for running as Adminstrator since non-development scenarios for Puppet Server on Windows are unsupported. Note that Puppet Server runs as the [pe-]puppet user but to start/stop it you must be root.

# File lib/puppetserver/ca/config/puppet.rb, line 49
def user_specific_puppet_confdir
  @user_specific_puppet_confdir ||= Puppetserver::Ca::Utils::Config.puppet_confdir
end
user_specific_puppet_config() click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 53
def user_specific_puppet_config
  user_specific_puppet_confdir + '/puppet.conf'
end

Private Instance Methods

explicitly_given_config_file_or_default_config_exists?() click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 255
def explicitly_given_config_file_or_default_config_exists?
  !@using_default_location || File.exist?(@config_path)
end
find_cadir(configured_cadir, confdir, ssldir, logger, ca_dir_warn) click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 229
def find_cadir(configured_cadir, confdir, ssldir, logger, ca_dir_warn)
  warning = 'The cadir is currently configured to be inside the ' +
    '%{ssldir} directory. This config setting and the directory ' +
    'location will not be used in a future version of puppet. ' +
    'Please run the puppetserver ca tool to migrate out from the ' +
    'puppet confdir to the /etc/puppetlabs/puppetserver/ca directory. ' +
    'Use `puppetserver ca migrate --help` for more info.'

  if configured_cadir
    if ca_dir_warn && configured_cadir.start_with?(ssldir)
      logger.warn(warning % {ssldir: ssldir})
    end
    configured_cadir

  else
    old_cadir = Puppetserver::Ca::Utils::Config.old_default_cadir(confdir)
    new_cadir = Puppetserver::Ca::Utils::Config.new_default_cadir(confdir)
    if File.exist?(old_cadir) && !File.symlink?(old_cadir)
      logger.warn(warning % {ssldir: ssldir}) if ca_dir_warn
      old_cadir
    else
      new_cadir
    end
  end
end
munge_ttl_setting(ca_ttl_setting) click to toggle source

Convert the value to Numeric, parsing numeric string with units if necessary.

# File lib/puppetserver/ca/config/puppet.rb, line 264
def munge_ttl_setting(ca_ttl_setting)
  case
  when ca_ttl_setting.is_a?(Numeric)
    if ca_ttl_setting < 0
      @errors << "Invalid negative 'time to live' #{ca_ttl_setting.inspect} - did you mean 'unlimited'?"
    end
    ca_ttl_setting

  when ca_ttl_setting == 'unlimited'
    Float::INFINITY

  when (ca_ttl_setting.is_a?(String) and ca_ttl_setting =~ TTL_FORMAT)
    $1.to_i * TTL_UNITMAP[$2 || 's']
  else
    @errors <<  "Invalid 'time to live' format '#{ca_ttl_setting.inspect}' for parameter: :ca_ttl"
  end
end
parse_crl_usage(setting) click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 282
def parse_crl_usage(setting)
  case setting.to_s
  when 'true', 'chain'
    :chain
  when 'leaf'
    :leaf
  when 'false'
    :ignore
  end
end
run(command) click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 259
def run(command)
  %x( #{command} )
end
update_for_server_list!(settings) click to toggle source
# File lib/puppetserver/ca/config/puppet.rb, line 293
def update_for_server_list!(settings)
  if settings.dig(:server_list, 0, 0) &&
      settings[:ca_server] == '$server'

    settings[:ca_server] = settings.dig(:server_list, 0, 0)
  end

  if settings.dig(:server_list, 0, 1) &&
      settings[:ca_port] == '$serverport'

    settings[:ca_port] = settings.dig(:server_list, 0, 1)
  end
end