-
Notifications
You must be signed in to change notification settings - Fork 222
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #23211 - Add PuppetCa TokenVerify provider
- Loading branch information
Julian Todt
committed
Jun 21, 2018
1 parent
e325683
commit 5bf0c2b
Showing
15 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
group :puppetca_token_whitelisting do | ||
gem 'jwt' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
# | ||
# Configuration of the PuppetCA token_whitelisting provider | ||
# | ||
|
||
#:sign_all: false | ||
#:hosts_file: /tmp/foreman-proxy/hosts.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
modules/puppetca_token_whitelisting/plugin_configuration.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module ::Proxy::PuppetCa::TokenWhitelisting | ||
class PluginConfiguration | ||
def load_classes | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting_autosigner' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting_csr' | ||
end | ||
|
||
def load_dependency_injection_wirings(container_instance, settings) | ||
container_instance.dependency :autosigner, lambda { ::Proxy::PuppetCa::TokenWhitelisting::Autosigner.new } | ||
end | ||
end | ||
end | ||
|
2 changes: 2 additions & 0 deletions
2
modules/puppetca_token_whitelisting/puppetca_token_whitelisting.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
require 'puppetca_token_whitelisting/plugin_configuration' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting_plugin' |
96 changes: 96 additions & 0 deletions
96
modules/puppetca_token_whitelisting/puppetca_token_whitelisting_autosigner.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
require 'jwt' | ||
require 'openssl' | ||
|
||
module ::Proxy::PuppetCa::TokenWhitelisting | ||
class Autosigner | ||
include ::Proxy::Log | ||
include ::Proxy::Util | ||
|
||
def sign_all | ||
Proxy::PuppetCa::TokenWhitelisting::Plugin.settings.sign_all | ||
end | ||
|
||
def hosts_file | ||
Proxy::PuppetCa::TokenWhitelisting::Plugin.settings.hosts_file | ||
end | ||
|
||
def smartproxy_cert | ||
OpenSSL::PKey::RSA.new File.read Proxy::SETTINGS.ssl_private_key.to_s | ||
end | ||
|
||
def ensure_hostsfile | ||
unless File.exist?(hosts_file) | ||
FileUtils.mkdir_p hosts_file.rpartition('/').first | ||
FileUtils.touch hosts_file | ||
File.write(hosts_file, [].to_yaml) | ||
end | ||
end | ||
|
||
# Invalidate a token based on the certname | ||
def disable certname | ||
hosts = autosign_list | ||
hosts.delete(certname) | ||
File.write hosts_file, hosts.to_yaml | ||
end | ||
|
||
# Create a new token for a certname | ||
def autosign certname | ||
ensure_hostsfile | ||
payload = { certname: certname, exp: Time.now.to_i + 10 * 60 * 60 } | ||
token = JWT.encode payload, smartproxy_cert, 'RS512' | ||
File.write hosts_file, autosign_list.push(certname).to_yaml | ||
{ generated_token: token }.to_json | ||
end | ||
|
||
# List the hosts that are currently valid | ||
def autosign_list | ||
ensure_hostsfile | ||
YAML.load_file(hosts_file).to_a | ||
end | ||
|
||
# Check whether a csr is valid and should be signed | ||
# by checking its token if it exists | ||
def validate_csr csr | ||
if csr.nil? | ||
logger.warn "Request did not include a CSR." | ||
return false | ||
end | ||
if sign_all | ||
logger.warn "Signing CSR without token verification." | ||
return true | ||
end | ||
begin | ||
req = Proxy::PuppetCa::TokenWhitelisting::CSR.new csr | ||
token = req.challenge_password | ||
rescue | ||
logger.warn "Invalid CSR" | ||
return false | ||
end | ||
if token.nil? | ||
logger.warn "CSR did not include a token." | ||
return false | ||
end | ||
validate_token token | ||
end | ||
|
||
def validate_token token | ||
# token didnt expire? | ||
begin | ||
decoded = JWT.decode(token, smartproxy_cert.public_key, true, algorithm: 'RS512') | ||
rescue JWT::ExpiredSignature | ||
logger.warn "Token already expired." | ||
return false | ||
rescue JWT::DecodeError | ||
logger.warn "Failed to decode token." | ||
return false | ||
end | ||
# token in our list? | ||
unless autosign_list.include? decoded.first['certname'] | ||
logger.warn "Certname not valid." | ||
return false | ||
end | ||
disable decoded.first['certname'] | ||
return true | ||
end | ||
end | ||
end |
25 changes: 25 additions & 0 deletions
25
modules/puppetca_token_whitelisting/puppetca_token_whitelisting_csr.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module Proxy::PuppetCa::TokenWhitelisting | ||
class CSR | ||
attr_reader :csr | ||
|
||
def initialize(raw_csr) | ||
@csr = OpenSSL::X509::Request.new(raw_csr) | ||
end | ||
|
||
def challenge_password | ||
attribute = custom_attributes.detect do |attr| | ||
['challengePassword', '1.2.840.113549.1.9.7'].include?(attr[:oid]) | ||
end | ||
attribute ? attribute[:value] : nil | ||
end | ||
|
||
def custom_attributes | ||
@csr.attributes.map do |attr| | ||
{ | ||
oid: attr.oid, | ||
value: attr.value.value.first.value | ||
} | ||
end | ||
end | ||
end | ||
end |
11 changes: 11 additions & 0 deletions
11
modules/puppetca_token_whitelisting/puppetca_token_whitelisting_plugin.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module ::Proxy::PuppetCa::TokenWhitelisting | ||
class Plugin < ::Proxy::Provider | ||
plugin :puppetca_token_whitelisting, ::Proxy::VERSION | ||
|
||
requires :puppetca, ::Proxy::VERSION | ||
default_settings :sign_all => false, :hosts_file => '/tmp/foreman-proxy/hosts.yml' | ||
|
||
load_classes ::Proxy::PuppetCa::TokenWhitelisting::PluginConfiguration | ||
load_dependency_injection_wirings ::Proxy::PuppetCa::TokenWhitelisting::PluginConfiguration | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
-----BEGIN CERTIFICATE REQUEST----- | ||
MIIEdTCCAl0CAQAwGzEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLmNvbTCCAiIwDQYJ | ||
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAK+eSI5LYrBUROs02A9DYPqEpnN1YokD | ||
MSb47NQW0A4+o7B7h/B3HFN2Moo/US/zNLDvHGuVDZIaIpudtNBTBSX0MQPtF8tQ | ||
Lm7WA6wGwqpX3eR3OLXdWR/T4wIvFQ1gvCS/snuW8YennSeAl1Yijtr2EHiPFJ/i | ||
Dy67vGhvXrqVDl/svIf13uw9zcxZ34VfE8zYg5WZ/thlhjbue/KKJdn+riNIYNK4 | ||
gwEjFNER9U38UPZUXMNJCEAXEJ7GKXQUKMXXtybp5jihPxTjbxFKilaIEJNM35ej | ||
9Ra0OREGc0Cc0beaW9+n9ZFBxsfM/NV0nzOos4tmSEyAq54Nbd80ropYivrmSRQf | ||
/yDPijvmC4frL1nhSJaf17rmfw27urbPcObbpuQFXCIlNFJd/CwpPds6ikZ9otDx | ||
yTzPveLbQUCh3CzReODC7jopi7vchPH3cZVvuaE90REKL++3xHqOzRpq5tZ1xc2j | ||
3agiQmTthnzABx6cj/q2ab4YAwpaSIAZFoHtzD8tsyD1WTGkL9jzBkCULd7ZcQdR | ||
AL0PwGLYI2hbCsQV6i9noVZZ19+hHEjjk06lG5SKK+H8eTIybWr9IPU/yZowNPtd | ||
et6samT8ltBLAxqTFffrdsTUTQAoN+ykgsML/N3LUuPACg1G6ge0MQVVvBIfCNiP | ||
SjfPJmthHHc1AgMBAAGgFTATBgkqhkiG9w0BCQcxBhMEMTIzNDANBgkqhkiG9w0B | ||
AQsFAAOCAgEAfTtWZAP8z9pxR6esLkoCfhhXaYzjnzJ5/4r+x/VPpJQEzI37CScG | ||
Dma+UzVkIddCVc5oFtzLtVZtGTaygW2QyR7wu+an0qQBs+MVzkjPPnMLerDUR89c | ||
Nk5DlDMaKFYV3JJpg2G2YdmOR0SEF0640Aw0/Ftx41iTSLSFopDMcTPRbZ9zm2AN | ||
uer2TMIWpio6k6OyEJHkyifQOG1kgI+amVgk21kVRm5qUZV01QduLSxuN8KQKDYT | ||
dmE31BpSQdVYzvrRPV558+NiWSrRheQtLfCl4BUZsZjfgh7OXSiy/yCZ6Co7FeDX | ||
WbVtlIeaFukt5fPD4VKBQVIP296ZiB4BFIDLUcq87DaWbjbO1owS/B89MbAM6Fjy | ||
FHCE3x0nUev3LYrKrJGouHYXiyUNGQk1ilI60y1xc3+B3ErjzpszJB1uBRk1fsem | ||
Np60VUVqNPD7GzxlE/acgEa2Xij1vl+g4yIjWGVv5Fokb4fO6K+n8iSEYKG4+nMr | ||
+vjP7bTcW4GymYPH38TtQfzhXvFMODL8ehiy2xjndEfLMitrbP1vjiLrCZD6gIk3 | ||
alxj7nHJMXF83Rqg/7OhERMVmUGE+tDiRD95bMK/eYq4sj49zke6puf5r0MeW6Fv | ||
jfuZ0r0kJmJF/r2FZEKuScl0uS4/RWUvgUdUFwpZ3i8KzJWJ6NDm7eY= | ||
-----END CERTIFICATE REQUEST----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
- foo.example.com | ||
- test.bar.example.com |
96 changes: 96 additions & 0 deletions
96
test/puppetca_token_whitelisting/puppetca_token_whitelisting_autosigner_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
require 'test_helper' | ||
require 'tempfile' | ||
require 'fileutils' | ||
require 'openssl' | ||
require 'yaml' | ||
require 'json' | ||
|
||
require 'puppetca/puppetca' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting_autosigner' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting_csr' | ||
|
||
class PuppetCaTokenWhitelistingAutosignerTest < Test::Unit::TestCase | ||
def setup | ||
@file = Tempfile.new('autosign_test') | ||
begin | ||
## Setup | ||
FileUtils.cp './test/fixtures/puppetca/hosts.yml', @file.path | ||
rescue | ||
@file.close | ||
@file.unlink | ||
@file = nil | ||
end | ||
@autosigner = Proxy::PuppetCa::TokenWhitelisting::Autosigner.new | ||
@autosigner.stubs(:hosts_file).returns(@file.path) | ||
rsa_cert = OpenSSL::PKey::RSA.generate 2048 | ||
@autosigner.stubs(:smartproxy_cert).returns(rsa_cert) | ||
end | ||
|
||
def teardown | ||
@file.close | ||
@file.unlink | ||
end | ||
|
||
def test_should_list_autosign_entries | ||
assert_equal @autosigner.autosign_list, ['foo.example.com', 'test.bar.example.com'] | ||
end | ||
|
||
def test_should_add_autosign_entry | ||
@autosigner.autosign 'foobar.example.com' | ||
assert_equal @autosigner.autosign_list, ['foo.example.com', 'test.bar.example.com', 'foobar.example.com'] | ||
end | ||
|
||
def test_should_create_correct_token | ||
response = @autosigner.autosign 'baz.example.com' | ||
token = JSON.parse(response)['generated_token'] | ||
decoded = JWT.decode(token, @autosigner.smartproxy_cert.public_key, true, algorithm: 'RS512') | ||
assert_equal decoded.first['certname'], 'baz.example.com' | ||
assert 100 > (decoded.first['exp'] - Time.now.to_i - 10 * 60 * 60).abs | ||
end | ||
|
||
def test_should_remove_autosign_entry | ||
@autosigner.disable 'foo.example.com' | ||
assert_equal @autosigner.autosign_list, ['test.bar.example.com'] | ||
end | ||
|
||
def test_should_validate_on_sign_all | ||
@autosigner.stubs(:sign_all).returns(true) | ||
assert_true @autosigner.validate_csr '' | ||
end | ||
|
||
def test_should_call_verification | ||
csr_example = File.read './test/fixtures/puppetca/csr_example.pem' | ||
@autosigner.expects(:validate_token).with('1234').returns(true) | ||
assert_true @autosigner.validate_csr csr_example | ||
end | ||
|
||
def test_should_validate_a_correct_token | ||
response = @autosigner.autosign 'signme.example.com' | ||
token = JSON.parse(response)['generated_token'] | ||
|
||
assert_true @autosigner.validate_token token | ||
end | ||
|
||
def test_should_not_validate_expired_token | ||
payload = { certname: 'foo.example.com', exp: Time.now.to_i - 10 } | ||
token = JWT.encode payload, @autosigner.smartproxy_cert, 'RS512' | ||
|
||
assert_false @autosigner.validate_token token | ||
end | ||
|
||
def test_should_not_validate_token_with_invalid_certname | ||
payload = { certname: 'unknown.example.com', exp: Time.now.to_i + 999_999 } | ||
token = JWT.encode payload, @autosigner.smartproxy_cert, 'RS512' | ||
|
||
assert_false @autosigner.validate_token token | ||
end | ||
|
||
def test_should_not_validate_token_with_unkown_signature | ||
unknown_cert = OpenSSL::PKey::RSA.generate 2048 | ||
payload = { certname: 'foo.example.com', exp: Time.now.to_i + 999_999 } | ||
token = JWT.encode payload, unknown_cert, 'RS512' | ||
|
||
assert_false @autosigner.validate_token token | ||
end | ||
end |
12 changes: 12 additions & 0 deletions
12
test/puppetca_token_whitelisting/puppetca_token_whitelisting_config_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
require 'test_helper' | ||
require 'puppetca/puppetca' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting_plugin' | ||
|
||
class PuppetCATokenWhitelistingConfigTest < Test::Unit::TestCase | ||
def test_omitted_settings_have_default_values | ||
Proxy::PuppetCa::TokenWhitelisting::Plugin.load_test_settings({}) | ||
assert_equal false, Proxy::PuppetCa::TokenWhitelisting::Plugin.settings.sign_all | ||
assert_equal '/tmp/foreman-proxy/hosts.yml', Proxy::PuppetCa::TokenWhitelisting::Plugin.settings.hosts_file | ||
end | ||
end |
23 changes: 23 additions & 0 deletions
23
test/puppetca_token_whitelisting/puppetca_token_whitelisting_csr_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
require 'test_helper' | ||
|
||
require 'puppetca/puppetca' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting' | ||
require 'puppetca_token_whitelisting/puppetca_token_whitelisting_csr' | ||
|
||
class PuppetCaTokenWhitelistingCSRTest < Test::Unit::TestCase | ||
def setup | ||
@csr_example = File.read './test/fixtures/puppetca/csr_example.pem' | ||
end | ||
|
||
def test_should_extract_correct_attribute | ||
req = Proxy::PuppetCa::TokenWhitelisting::CSR.new @csr_example | ||
assert_equal '1234', req.challenge_password | ||
end | ||
|
||
def test_should_fail_on_invalid_csr | ||
@csr_example.slice!(42...69) | ||
assert_raise OpenSSL::X509::RequestError do | ||
Proxy::PuppetCa::TokenWhitelisting::CSR.new @csr_example | ||
end | ||
end | ||
end |