Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #23799 - Refactor: Make PuppetCa pluggable #586

Merged
merged 1 commit into from
Jun 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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