Skip to content

Commit

Permalink
Fixes #23799 - Refactor: Make PuppetCa pluggable
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Todt committed Jun 21, 2018
1 parent 14a7505 commit 641f2e4
Show file tree
Hide file tree
Showing 18 changed files with 258 additions and 143 deletions.
4 changes: 3 additions & 1 deletion config/settings.d/puppetca.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
# Can be true, false, or http/https to enable just one of the protocols
:enabled: false

# valid providers:
# - puppetca_hostname_whitelisting (verify CSRs based on a hostname whitelist)
#:use_provider: puppetca_hostname_whitelisting
#:ssldir: /var/lib/puppet/ssl
#:autosignfile: /etc/puppet/autosign.conf
#:puppetca_use_sudo: true
#:sudo_command: /usr/bin/sudo
6 changes: 6 additions & 0 deletions config/settings.d/puppetca_hostname_whitelisting.yml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
#
# Configuration of the PuppetCA hostname_whitelisting provider
#

#:autosignfile: /etc/puppet/autosign.conf
33 changes: 33 additions & 0 deletions extra/migrations/2018062000000_migrate_puppetca_settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'yaml'

class MigratePuppetCaSettings < ::Proxy::Migration
def migrate
copy_original_configuration_except(path('settings.d', 'puppetca.yml'),
path('settings.d', 'puppetca_hostname_whitelisting.yml.example'))

module_settings = YAML.load_file(path(src_dir, 'settings.d', 'puppetca.yml'))
provider_settings = YAML.load_file(path(src_dir, 'settings.d', 'puppetca_hostname_whitelisting.yml'))

write_yaml(path(dst_dir, 'settings.d', 'puppetca_hostname_whitelisting.yml'),
transform_provider_yaml(module_settings, provider_settings))
write_yaml(path(dst_dir, 'settings.d', 'puppetca.yml'), transform_puppetca_yaml(module_settings))
end

def transform_puppetca_yaml(input)
input.delete(:autosignfile)
input[:use_provider] = 'puppetca_hostname_whitelisting'
input
end

def transform_provider_yaml(module_settings, provider_settings)
provider_settings = {} unless provider_settings.is_a? Hash
provider_settings[:autosignfile] = module_settings[:autosignfile]
provider_settings
end

def write_yaml(filepath, yaml)
File.open(filepath, 'w') do |f|
f.write(yaml.to_yaml)
end
end
end
1 change: 1 addition & 0 deletions lib/smart_proxy_main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ module Proxy
require 'dhcp_native_ms/dhcp_native_ms'
require 'dhcp_libvirt/dhcp_libvirt'
require 'puppetca/puppetca'
require 'puppetca_hostname_whitelisting/puppetca_hostname_whitelisting'
require 'puppet_proxy/puppet'
require 'puppet_proxy_customrun/puppet_proxy_customrun'
require 'puppet_proxy_legacy/puppet_proxy_legacy'
Expand Down
8 changes: 8 additions & 0 deletions modules/puppetca/dependency_injection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Proxy::PuppetCa
module DependencyInjection
include Proxy::DependencyInjection::Accessors
def container_instance
@container_instance ||= ::Proxy::Plugins.instance.find {|p| p[:name] == :puppetca }[:di_container]
end
end
end
13 changes: 13 additions & 0 deletions modules/puppetca/plugin_configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module ::Proxy::PuppetCa
class PluginConfiguration
def load_classes
require 'puppetca/puppetca_puppet_cert'
require 'puppetca/dependency_injection'
require 'puppetca/puppetca_api'
end

def load_dependency_injection_wirings(container_instance, settings)
container_instance.dependency :puppet_cert, lambda { ::Proxy::PuppetCa::PuppetCert.new }
end
end
end
3 changes: 1 addition & 2 deletions modules/puppetca/puppetca.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
require 'puppetca/plugin_configuration'
require 'puppetca/puppetca_plugin'

module Proxy::PuppetCa; end
18 changes: 10 additions & 8 deletions modules/puppetca/puppetca_api.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
require 'puppetca/puppetca_main'

module Proxy::PuppetCa
class Api < ::Sinatra::Base
extend Proxy::PuppetCa::DependencyInjection
inject_attr :puppet_cert, :puppet_cert
inject_attr :autosigner, :autosigner

helpers ::Proxy::Helpers
authorize_with_trusted_hosts
authorize_with_ssl_client

get "/?" do
content_type :json
begin
Proxy::PuppetCa.list.to_json
puppet_cert.list.to_json
rescue => e
log_halt 406, "Failed to list certificates: #{e}"
end
Expand All @@ -18,7 +20,7 @@ class Api < ::Sinatra::Base
get "/autosign" do
content_type :json
begin
Proxy::PuppetCa.autosign_list.to_json
autosigner.autosign_list.to_json
rescue => e
log_halt 406, "Failed to list autosign entries: #{e}"
end
Expand All @@ -28,7 +30,7 @@ class Api < ::Sinatra::Base
content_type :json
certname = params[:certname]
begin
Proxy::PuppetCa.autosign(certname)
autosigner.autosign(certname)
rescue => e
log_halt 406, "Failed to enable autosign for #{certname}: #{e}"
end
Expand All @@ -38,7 +40,7 @@ class Api < ::Sinatra::Base
content_type :json
certname = params[:certname]
begin
Proxy::PuppetCa.disable(certname)
autosigner.disable(certname)
rescue Proxy::PuppetCa::NotPresent => e
log_halt 404, e.to_s
rescue => e
Expand All @@ -50,7 +52,7 @@ class Api < ::Sinatra::Base
content_type :json
certname = params[:certname]
begin
Proxy::PuppetCa.sign(certname)
puppet_cert.sign(certname)
rescue => e
log_halt 406, "Failed to sign certificate(s) for #{certname}: #{e}"
end
Expand All @@ -60,7 +62,7 @@ class Api < ::Sinatra::Base
begin
content_type :json
certname = params[:certname]
Proxy::PuppetCa.clean(certname)
puppet_cert.clean(certname)
rescue Proxy::PuppetCa::NotPresent => e
log_halt 404, e.to_s
rescue => e
Expand Down
7 changes: 6 additions & 1 deletion modules/puppetca/puppetca_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ class Plugin < ::Proxy::Plugin
http_rackup_path File.expand_path("http_config.ru", File.expand_path("../", __FILE__))
https_rackup_path File.expand_path("http_config.ru", File.expand_path("../", __FILE__))

default_settings :ssldir => '/var/lib/puppet/ssl', :autosignfile => '/etc/puppet/autosign.conf'
default_settings :ssldir => '/var/lib/puppet/ssl'

uses_provider
default_settings :use_provider => 'puppetca_hostname_whitelisting'

load_classes ::Proxy::PuppetCa::PluginConfiguration
load_dependency_injection_wirings ::Proxy::PuppetCa::PluginConfiguration
plugin :puppetca, ::Proxy::VERSION
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
require 'set'

module Proxy::PuppetCa
extend ::Proxy::Log
extend ::Proxy::Util

class NotPresent < RuntimeError; end

class << self
class PuppetCert
include ::Proxy::Log
include ::Proxy::Util

def sign certname
puppetca("sign", certname)
end
Expand All @@ -16,53 +17,6 @@ def clean certname
puppetca("clean", certname)
end

#remove certname from autosign if exists
def disable certname
raise "No such file #{autosign_file}" unless File.exist?(autosign_file)

found = false
entries = File.readlines(autosign_file).collect do |l|
if l.chomp != certname
l
else
found = true
nil
end
end.uniq.compact
if found
open(autosign_file, File::TRUNC|File::RDWR) do |autosign|
autosign.write entries.join
end
logger.debug "Removed #{certname} from autosign"
else
logger.debug "Attempt to remove nonexistent client autosign for #{certname}"
raise NotPresent, "Attempt to remove nonexistent client autosign for #{certname}"
end
end

# add certname to puppet autosign file
# parameter is certname to use
def autosign certname
FileUtils.touch(autosign_file) unless File.exist?(autosign_file)

open(autosign_file, File::RDWR) do |autosign|
# Check that we don't have that host already
found = autosign.readlines.find { |line| line.chomp == certname }
autosign.puts certname unless found
end
logger.debug "Added #{certname} to autosign"
end

# list of hosts which are now allowed to be installed via autosign
def autosign_list
return [] unless File.exist?(autosign_file)
File.read(autosign_file).split("\n").reject do |v|
v =~ /^\s*#.*|^$/ ## Remove comments and empty lines
end.map do |v|
v.chomp ## Strip trailing spaces
end
end

# list of all certificates and their state/fingerprint
def list
find_puppetca
Expand Down Expand Up @@ -129,10 +83,6 @@ def ssldir
Proxy::PuppetCa::Plugin.settings.ssldir
end

def autosign_file
Proxy::PuppetCa::Plugin.settings.autosignfile
end

# parse the puppetca --list output
def certificate str
case str
Expand Down
12 changes: 12 additions & 0 deletions modules/puppetca_hostname_whitelisting/plugin_configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module ::Proxy::PuppetCa::HostnameWhitelisting
class PluginConfiguration
def load_classes
require 'puppetca_hostname_whitelisting/puppetca_hostname_whitelisting_autosigner'
end

def load_dependency_injection_wirings(container_instance, settings)
container_instance.dependency :autosigner, lambda { ::Proxy::PuppetCa::HostnameWhitelisting::Autosigner.new }
end
end
end

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require 'puppetca_hostname_whitelisting/plugin_configuration'
require 'puppetca_hostname_whitelisting/puppetca_hostname_whitelisting_plugin'
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
module ::Proxy::PuppetCa::HostnameWhitelisting
class Autosigner
include ::Proxy::Log
include ::Proxy::Util

def autosign_file
Proxy::PuppetCa::HostnameWhitelisting::Plugin.settings.autosignfile
end

#remove certname from autosign if exists
def disable certname
raise "No such file #{autosign_file}" unless File.exist?(autosign_file)

found = false
entries = File.readlines(autosign_file).collect do |l|
if l.chomp != certname
l
else
found = true
nil
end
end.uniq.compact
if found
open(autosign_file, File::TRUNC|File::RDWR) do |autosign|
autosign.write entries.join
end
logger.debug "Removed #{certname} from autosign"
else
logger.debug "Attempt to remove nonexistent client autosign for #{certname}"
raise ::Proxy::PuppetCa::NotPresent, "Attempt to remove nonexistent client autosign for #{certname}"
end
end

# add certname to puppet autosign file
# parameter is certname to use
def autosign certname
FileUtils.touch(autosign_file) unless File.exist?(autosign_file)

open(autosign_file, File::RDWR) do |autosign|
# Check that we don't have that host already
found = autosign.readlines.find { |line| line.chomp == certname }
autosign.puts certname unless found
end
logger.debug "Added #{certname} to autosign"
end

# list of hosts which are now allowed to be installed via autosign
def autosign_list
return [] unless File.exist?(autosign_file)
File.read(autosign_file).split("\n").reject do |v|
v =~ /^\s*#.*|^$/ ## Remove comments and empty lines
end.map do |v|
v.chomp ## Strip trailing spaces
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module ::Proxy::PuppetCa::HostnameWhitelisting
class Plugin < ::Proxy::Provider
plugin :puppetca_hostname_whitelisting, ::Proxy::VERSION

requires :puppetca, ::Proxy::VERSION
default_settings :autosignfile => '/etc/puppet/autosign.conf'

load_classes ::Proxy::PuppetCa::HostnameWhitelisting::PluginConfiguration
load_dependency_injection_wirings ::Proxy::PuppetCa::HostnameWhitelisting::PluginConfiguration
end
end
4 changes: 2 additions & 2 deletions test/puppetca/puppetca_config_test.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
require 'test_helper'
require 'puppetca/puppetca_plugin'
require 'puppetca/puppetca'

class PuppetCAConfigTest < Test::Unit::TestCase
def test_omitted_settings_have_default_values
Proxy::PuppetCa::Plugin.load_test_settings({})
assert_equal '/var/lib/puppet/ssl', Proxy::PuppetCa::Plugin.settings.ssldir
assert_equal '/etc/puppet/autosign.conf', Proxy::PuppetCa::Plugin.settings.autosignfile
assert_equal 'puppetca_hostname_whitelisting', Proxy::PuppetCa::Plugin.settings.use_provider
end
end
Loading

0 comments on commit 641f2e4

Please sign in to comment.