summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/parser/functions/get_certificate.rb89
-rw-r--r--lib/puppet/parser/functions/get_pubkey.rb25
-rw-r--r--spec/fixtures/master_config/.gitignore2
-rw-r--r--spec/fixtures/master_config/auth.conf4
-rw-r--r--spec/fixtures/master_config/ssl/ca/ca_crl.pem8
-rw-r--r--spec/fixtures/master_config/ssl/ca/ca_crt.pem14
-rw-r--r--spec/fixtures/master_config/ssl/ca/ca_key.pem15
-rw-r--r--spec/fixtures/master_config/ssl/ca/ca_pub.pem5
-rw-r--r--spec/fixtures/master_config/ssl/ca/inventory.txt5
-rw-r--r--spec/fixtures/master_config/ssl/ca/private/ca.pass1
-rw-r--r--spec/fixtures/master_config/ssl/ca/serial1
-rw-r--r--spec/fixtures/master_config/ssl/ca/signed/bob@mydomain.com.pem15
-rw-r--r--spec/fixtures/master_config/ssl/ca/signed/puppetmaster.pem15
-rw-r--r--spec/fixtures/master_config/ssl/certs/bob@mydomain.com.pem15
-rw-r--r--spec/fixtures/master_config/ssl/certs/ca.pem14
-rw-r--r--spec/fixtures/master_config/ssl/certs/puppetmaster.pem15
-rw-r--r--spec/fixtures/master_config/ssl/crl.pem8
-rw-r--r--spec/fixtures/master_config/ssl/private_keys/bob@mydomain.com.pem15
-rw-r--r--spec/fixtures/master_config/ssl/private_keys/puppetmaster.pem15
-rw-r--r--spec/fixtures/master_config/ssl/public_keys/bob@mydomain.com.pem5
-rw-r--r--spec/fixtures/master_config/ssl/public_keys/puppetmaster.pem5
-rwxr-xr-xspec/unit/puppet/parser/functions/get_certficiate_spec.rb158
-rwxr-xr-xspec/unit/puppet/parser/functions/get_pubkey_spec.rb54
23 files changed, 503 insertions, 0 deletions
diff --git a/lib/puppet/parser/functions/get_certificate.rb b/lib/puppet/parser/functions/get_certificate.rb
new file mode 100644
index 0000000..66baba6
--- /dev/null
+++ b/lib/puppet/parser/functions/get_certificate.rb
@@ -0,0 +1,89 @@
+module Puppet::Parser::Functions
+ newfunction(:get_certificate, :type => :rvalue, :doc => <<-EOS
+Returns the public certificate of the given CN from the local or remote Puppet
+CA.
+
+Usage is:
+
+ get_certificate($cn, $options)
+
+The first argument $cn is a valid CN for the certificate you wish to
+return. A CN is usually the hostname of a machine in Puppet. You can view all available
+certificates using the facility:
+
+ puppet cert --list --all
+
+On the main CA or puppetmaster.
+
+The second argument $options allows the user to define a hash of options to
+pass to the function.
+
+The options and descriptions are:
+
+* *conn_timeout*: Adjust timeout for remote CA connectivity (in seconds). Default is 7.
+ EOS
+ ) do |arguments|
+
+ # Make sure we have enough arguments
+ if not (1..2).include?(arguments.size) then
+ raise(Puppet::ParseError, "get_certificate(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1 or 2)")
+ end
+
+ # Obtain arguments and set defaults
+ cn = arguments[0]
+ options = arguments[1] ||= {}
+ options[:conn_timeout] = 7 if !options.has_key?(:conn_timeout)
+
+ # Validation of arguments
+ if not (cn.is_a?(String) and cn.match(/^[a-zA-Z0-9@_\-\.]+$/)) then
+ raise(Puppet::ParseError, 'get_certificate(): CN name must be a valid string. Hashes and Arrays are not valid')
+ end
+ if not (1..600).include?(options[:conn_timeout]) then
+ raise(Puppet::ParseError, "get_certificate(): The option 'conn_timeout' must be an integer between 1 and 600")
+ end
+
+ # Get and return certificate using file or rest
+ if Puppet[:ca] == true then
+ # Get the certificate locally if we are acting as a CA
+ # TODO: wrap: puppet certificate --render-as s --ca-location remote find ken@bob.sh
+ ssl_cert_path = Puppet[:signeddir] + "/" + cn + ".pem"
+ if FileTest.exists?(ssl_cert_path) then
+ cert = File.open(ssl_cert_path,"r")
+ return cert.read
+ end
+ else
+ # Obtain the certificate from the CA if its remote
+ # TODO: wrap: puppet certificate --render-as s --ca-location local find ken@bob.sh
+ require 'net/http'
+ require 'net/https'
+
+ http = Net::HTTP.new(Puppet[:ca_server], Puppet[:ca_port])
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+
+ begin
+ res = timeout(options[:conn_timeout]) do
+ http.start {|h|
+ h.get("/production/certificate/#{cn}", { "Accept" => "s" })
+ }
+ end
+ rescue Timeout::Error
+ raise(Puppet::Error, "Transaction timed out when connecting to #{Puppet[:ca_server]}:#{Puppet[:ca_port]}. Check your CA is running and that your ca_server and ca_port settings are correct on the machine this function ran on.")
+ rescue Errno::ECONNREFUSED
+ raise(Puppet::Error, "Connection refused when connecting to #{Puppet[:ca_server]}:#{Puppet[:ca_port]}. Check your CA is running and that your ca_server and ca_port settings are correct on the machine this function ran on.")
+ end
+
+ case res.code
+ when "200"
+ return res.body if res.body
+ when "404"
+ return :undef
+ else
+ raise(Puppet::Error, "Error with REST call: #{res.code}")
+ end
+ end
+
+ :undef
+ end
+end
diff --git a/lib/puppet/parser/functions/get_pubkey.rb b/lib/puppet/parser/functions/get_pubkey.rb
new file mode 100644
index 0000000..744b9df
--- /dev/null
+++ b/lib/puppet/parser/functions/get_pubkey.rb
@@ -0,0 +1,25 @@
+module Puppet::Parser::Functions
+ newfunction(:get_pubkey, :type => :rvalue, :doc => <<-EOS
+Gets a public key given a CN. This function accepts all the same
+parameters as get_certificate(), but instead returns the public
+key portion of the certificate.
+
+See get_certificate() for a more complete list of options available.
+EOS
+ ) do |arguments|
+
+ # Wrap the get_certificate method
+ method = Puppet::Parser::Functions.function(:get_certificate)
+ cert_text = send(method, arguments)
+
+ require 'openssl'
+
+ if cert_text == :undef then
+ return :undef
+ else
+ cert = OpenSSL::X509::Certificate.new(cert_text)
+ pubkey = cert.public_key
+ return pubkey.to_s
+ end
+ end
+end
diff --git a/spec/fixtures/master_config/.gitignore b/spec/fixtures/master_config/.gitignore
new file mode 100644
index 0000000..7d4e912
--- /dev/null
+++ b/spec/fixtures/master_config/.gitignore
@@ -0,0 +1,2 @@
+manifests/
+var/
diff --git a/spec/fixtures/master_config/auth.conf b/spec/fixtures/master_config/auth.conf
new file mode 100644
index 0000000..aecb32e
--- /dev/null
+++ b/spec/fixtures/master_config/auth.conf
@@ -0,0 +1,4 @@
+path /certificate/
+auth no
+method find
+allow *
diff --git a/spec/fixtures/master_config/ssl/ca/ca_crl.pem b/spec/fixtures/master_config/ssl/ca/ca_crl.pem
new file mode 100644
index 0000000..90c7a03
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/ca_crl.pem
@@ -0,0 +1,8 @@
+-----BEGIN X509 CRL-----
+MIH5MGQCAQEwDQYJKoZIhvcNAQEFBQAwIjEgMB4GA1UEAwwXUHVwcGV0IENBOiBw
+dXBwZXRtYXN0ZXIXDTExMDgxMzIwMDAwOFoXDTE2MDgxMTIwMDAwOFqgDjAMMAoG
+A1UdFAQDAgEAMA0GCSqGSIb3DQEBBQUAA4GBACBHLkJD4RvEV75ak8w468Kq7r5p
+s87Fzs0Vj2fgqH/3GPoazwBD4R0TvqMb+NUuF0WnipexdQQRjaiERmqX9aIhRjRA
+vs4ItdoxAvcgCzWs6cYm/e4SAAqY5lipfJqd+aRlQgzWaj6WDbFMVEKvqMXqM5wU
+gGQRYVnXHbohA+/I
+-----END X509 CRL-----
diff --git a/spec/fixtures/master_config/ssl/ca/ca_crt.pem b/spec/fixtures/master_config/ssl/ca/ca_crt.pem
new file mode 100644
index 0000000..7910b2b
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/ca_crt.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICMzCCAZygAwIBAgIBATANBgkqhkiG9w0BAQUFADAiMSAwHgYDVQQDDBdQdXBw
+ZXQgQ0E6IHB1cHBldG1hc3RlcjAeFw0xMTA4MTIyMDAwMDhaFw0xNjA4MTAyMDAw
+MDhaMCIxIDAeBgNVBAMMF1B1cHBldCBDQTogcHVwcGV0bWFzdGVyMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQDA6rbkI3p/YmrjE5ZNwuCPRfqUtywnBHqClp2o
+nBgqrBZiKitxAmdEH4lidGA9AbiNnBiMh0fC4s5sKAUZUjPjv1I7VBqrueYWKnKP
+1IBuggaJDoUQysj73XxPUnfFiuBuDVO+FEjLCrbB7WCfdli3KuueUJjHbcLyUh0n
+o2ceMwIDAQABo3kwdzA4BglghkgBhvhCAQ0EKxYpUHVwcGV0IFJ1YnkvT3BlblNT
+TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUB14U4FLr4JVibAmnV+n+kw85ck4wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4GBAAZ3wF7R8DDhhT31OGQ/A+/F3L59nStqvW7AD7EabrTDPPNOVcvt/las
+oi4MXiBuGPgS/xg+n4YBREaaYoF8BcGx5YMPY1XOPS0DItnDl44Wd+eHraD69kLl
+l/4pPMlE5PQ21o82dph3i6B1E5zwLxhMXzh1mfvDcCIMmRdVobQm
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/master_config/ssl/ca/ca_key.pem b/spec/fixtures/master_config/ssl/ca/ca_key.pem
new file mode 100644
index 0000000..d073e22
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/ca_key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDA6rbkI3p/YmrjE5ZNwuCPRfqUtywnBHqClp2onBgqrBZiKitx
+AmdEH4lidGA9AbiNnBiMh0fC4s5sKAUZUjPjv1I7VBqrueYWKnKP1IBuggaJDoUQ
+ysj73XxPUnfFiuBuDVO+FEjLCrbB7WCfdli3KuueUJjHbcLyUh0no2ceMwIDAQAB
+AoGAdJRieXNHL3uWBCtuBQfjFDHBv+UBdYKrVgcWtzG9GOxtilzZa618Ihq8txaE
+odlMYacW3rVRlF/jRlDY4/hdChKO0PwffYzMmMklora8knG4Epi3LbMsVYCpbmvr
+AYNKkvAnTbSF/PQMq8hTRnRf8cL8KU6e0uFFiOfx0pc+YyECQQDyod+VtRiOxWM1
+/FE2eZpihibAiB0HV9VJuXW23WwKh2fIqHs2oQXzjvzjiDV+LiZu51L21hQQcAeH
+hMrNWRI/AkEAy4ulVjGybS0FqCvOX8UllJZBkN2z266HRag5a90TG0a0PEb0L+5Y
+3rokNTZAzxdrCxkHaLRXQ9PE7b3c/1CPDQJAWNeW491swZJbMoBSSG0cb6kJdYQh
+hPfPXHBxPuUy02QjR2ERxL4PTNB1nubYF3zUi9VeFo3qyN4Mk722+Jv9xwJADK8j
+Gn/2Un9fvt8b+TPb56qFY3WtY584psqY6XPZYPXC/Y6eYO5Fc3u+DeLXnxAih4qD
+v66dUYi82OPgBbkLcQJBAIFwHWNgrDZqSp8KBOldRUdwt2MkG3QzRiMziP8DczXF
+xvdxH+AHPWl7yzOLas/kgx23ozQZcTzNqFjDmnSrJZQ=
+-----END RSA PRIVATE KEY-----
diff --git a/spec/fixtures/master_config/ssl/ca/ca_pub.pem b/spec/fixtures/master_config/ssl/ca/ca_pub.pem
new file mode 100644
index 0000000..2ba33aa
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/ca_pub.pem
@@ -0,0 +1,5 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAMDqtuQjen9iauMTlk3C4I9F+pS3LCcEeoKWnaicGCqsFmIqK3ECZ0Qf
+iWJ0YD0BuI2cGIyHR8LizmwoBRlSM+O/UjtUGqu55hYqco/UgG6CBokOhRDKyPvd
+fE9Sd8WK4G4NU74USMsKtsHtYJ92WLcq655QmMdtwvJSHSejZx4zAgMBAAE=
+-----END RSA PUBLIC KEY-----
diff --git a/spec/fixtures/master_config/ssl/ca/inventory.txt b/spec/fixtures/master_config/ssl/ca/inventory.txt
new file mode 100644
index 0000000..51ed4af
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/inventory.txt
@@ -0,0 +1,5 @@
+# Inventory of signed certificates
+# SERIAL NOT_BEFORE NOT_AFTER SUBJECT
+0x0001 2011-08-12T20:00:08GMT 2016-08-10T20:00:08GMT /CN=Puppet CA: puppetmaster
+0x0002 2011-08-12T20:00:08GMT 2016-08-10T20:00:08GMT /CN=puppetmaster
+0x0003 2011-08-12T20:01:09GMT 2016-08-10T20:01:09GMT /CN=bob@mydomain.com
diff --git a/spec/fixtures/master_config/ssl/ca/private/ca.pass b/spec/fixtures/master_config/ssl/ca/private/ca.pass
new file mode 100644
index 0000000..234a5b9
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/private/ca.pass
@@ -0,0 +1 @@
+[Ie3rqTiZfur`@gLW5<P \ No newline at end of file
diff --git a/spec/fixtures/master_config/ssl/ca/serial b/spec/fixtures/master_config/ssl/ca/serial
new file mode 100644
index 0000000..9df46a8
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/serial
@@ -0,0 +1 @@
+0004 \ No newline at end of file
diff --git a/spec/fixtures/master_config/ssl/ca/signed/bob@mydomain.com.pem b/spec/fixtures/master_config/ssl/ca/signed/bob@mydomain.com.pem
new file mode 100644
index 0000000..a649c8d
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/signed/bob@mydomain.com.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVDCCAb2gAwIBAgIBAzANBgkqhkiG9w0BAQUFADAiMSAwHgYDVQQDDBdQdXBw
+ZXQgQ0E6IHB1cHBldG1hc3RlcjAeFw0xMTA4MTIyMDAxMDlaFw0xNjA4MTAyMDAx
+MDlaMBsxGTAXBgNVBAMMEGJvYkBteWRvbWFpbi5jb20wgZ8wDQYJKoZIhvcNAQEB
+BQADgY0AMIGJAoGBAL7+Idbd+eohxCXVXcICvo1IaqAzyjezWxfxMxoBF4mjdvwY
+9RalRM5jItm9ThVwLMezcISYSNPI42Y70+9XIK/3f6OxnSMoB7kDKX9MvcbZkRAt
+OfxDeWmAun+PXuH87VN1r7sViRSSB2dIxB3qjF1HNhAm0ocmSW+sZ3eul2lpAgMB
+AAGjgaAwgZ0wOAYJYIZIAYb4QgENBCsWKVB1cHBldCBSdWJ5L09wZW5TU0wgR2Vu
+ZXJhdGVkIENlcnRpZmljYXRlMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFFyA8pjL
++VkeCZHDRYCzKjzT4Cr9MAsGA1UdDwQEAwIFoDAnBgNVHSUEIDAeBggrBgEFBQcD
+AQYIKwYBBQUHAwIGCCsGAQUFBwMEMA0GCSqGSIb3DQEBBQUAA4GBAExa0zqinr4P
+6ZmYOYxNtS1d+8YQdzjJXOnlXUURhERHfKMjvvJ125MO9i4TRF/isetRKz2w0OfO
+Vfsdio9PSJf2Fh+/1V2r5eSvLbVenRwFnvyv/u/39ukQNbX5YSwXsl9QcWhqwtwF
+dL1eEwuy2xmfxX6ZZRPDFDrideAtTEJy
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/master_config/ssl/ca/signed/puppetmaster.pem b/spec/fixtures/master_config/ssl/ca/signed/puppetmaster.pem
new file mode 100644
index 0000000..df900fc
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/ca/signed/puppetmaster.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICUDCCAbmgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAiMSAwHgYDVQQDDBdQdXBw
+ZXQgQ0E6IHB1cHBldG1hc3RlcjAeFw0xMTA4MTIyMDAwMDhaFw0xNjA4MTAyMDAw
+MDhaMBcxFTATBgNVBAMMDHB1cHBldG1hc3RlcjCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAzxm4O4OlcihWLbNHboKEmQ9cGlsG1vs0nkFtjLeF6eyi4cV6n8Af
+1OacZV9aqzK8MrBQvYrpbpap2/kFSWx3vbhUVDz5ynsNzhu2Jt914XaJEcDmpDMU
+UwQ+pg7JCSHocW2JilofAZdC1HrefLc570yYwxS//U/cw6N5MnskS2ECAwEAAaOB
+oDCBnTA4BglghkgBhvhCAQ0EKxYpUHVwcGV0IFJ1YnkvT3BlblNTTCBHZW5lcmF0
+ZWQgQ2VydGlmaWNhdGUwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUYSrVz684wuDJ
+3awwcfFxMZGPkNQwCwYDVR0PBAQDAgWgMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr
+BgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADgYEAEL5ZXS5TVzaI56HH
+RhcGFLnvfl2BldsPuVuE5H6fqegRUyLpH7mXHCBt1Zn3IlUUdMLHOump4UHVjw/B
+QPk6ihVjWcBTCU8xu4hKj54MixZFFIo2sJveMVdfIJ3lTQTmRpTHpIU7hYHwP46q
+hLWywdpCBvhCxpK0YSi4FSDiYQ8=
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/master_config/ssl/certs/bob@mydomain.com.pem b/spec/fixtures/master_config/ssl/certs/bob@mydomain.com.pem
new file mode 100644
index 0000000..a649c8d
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/certs/bob@mydomain.com.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVDCCAb2gAwIBAgIBAzANBgkqhkiG9w0BAQUFADAiMSAwHgYDVQQDDBdQdXBw
+ZXQgQ0E6IHB1cHBldG1hc3RlcjAeFw0xMTA4MTIyMDAxMDlaFw0xNjA4MTAyMDAx
+MDlaMBsxGTAXBgNVBAMMEGJvYkBteWRvbWFpbi5jb20wgZ8wDQYJKoZIhvcNAQEB
+BQADgY0AMIGJAoGBAL7+Idbd+eohxCXVXcICvo1IaqAzyjezWxfxMxoBF4mjdvwY
+9RalRM5jItm9ThVwLMezcISYSNPI42Y70+9XIK/3f6OxnSMoB7kDKX9MvcbZkRAt
+OfxDeWmAun+PXuH87VN1r7sViRSSB2dIxB3qjF1HNhAm0ocmSW+sZ3eul2lpAgMB
+AAGjgaAwgZ0wOAYJYIZIAYb4QgENBCsWKVB1cHBldCBSdWJ5L09wZW5TU0wgR2Vu
+ZXJhdGVkIENlcnRpZmljYXRlMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFFyA8pjL
++VkeCZHDRYCzKjzT4Cr9MAsGA1UdDwQEAwIFoDAnBgNVHSUEIDAeBggrBgEFBQcD
+AQYIKwYBBQUHAwIGCCsGAQUFBwMEMA0GCSqGSIb3DQEBBQUAA4GBAExa0zqinr4P
+6ZmYOYxNtS1d+8YQdzjJXOnlXUURhERHfKMjvvJ125MO9i4TRF/isetRKz2w0OfO
+Vfsdio9PSJf2Fh+/1V2r5eSvLbVenRwFnvyv/u/39ukQNbX5YSwXsl9QcWhqwtwF
+dL1eEwuy2xmfxX6ZZRPDFDrideAtTEJy
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/master_config/ssl/certs/ca.pem b/spec/fixtures/master_config/ssl/certs/ca.pem
new file mode 100644
index 0000000..7910b2b
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/certs/ca.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICMzCCAZygAwIBAgIBATANBgkqhkiG9w0BAQUFADAiMSAwHgYDVQQDDBdQdXBw
+ZXQgQ0E6IHB1cHBldG1hc3RlcjAeFw0xMTA4MTIyMDAwMDhaFw0xNjA4MTAyMDAw
+MDhaMCIxIDAeBgNVBAMMF1B1cHBldCBDQTogcHVwcGV0bWFzdGVyMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQDA6rbkI3p/YmrjE5ZNwuCPRfqUtywnBHqClp2o
+nBgqrBZiKitxAmdEH4lidGA9AbiNnBiMh0fC4s5sKAUZUjPjv1I7VBqrueYWKnKP
+1IBuggaJDoUQysj73XxPUnfFiuBuDVO+FEjLCrbB7WCfdli3KuueUJjHbcLyUh0n
+o2ceMwIDAQABo3kwdzA4BglghkgBhvhCAQ0EKxYpUHVwcGV0IFJ1YnkvT3BlblNT
+TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUB14U4FLr4JVibAmnV+n+kw85ck4wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4GBAAZ3wF7R8DDhhT31OGQ/A+/F3L59nStqvW7AD7EabrTDPPNOVcvt/las
+oi4MXiBuGPgS/xg+n4YBREaaYoF8BcGx5YMPY1XOPS0DItnDl44Wd+eHraD69kLl
+l/4pPMlE5PQ21o82dph3i6B1E5zwLxhMXzh1mfvDcCIMmRdVobQm
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/master_config/ssl/certs/puppetmaster.pem b/spec/fixtures/master_config/ssl/certs/puppetmaster.pem
new file mode 100644
index 0000000..df900fc
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/certs/puppetmaster.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICUDCCAbmgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAiMSAwHgYDVQQDDBdQdXBw
+ZXQgQ0E6IHB1cHBldG1hc3RlcjAeFw0xMTA4MTIyMDAwMDhaFw0xNjA4MTAyMDAw
+MDhaMBcxFTATBgNVBAMMDHB1cHBldG1hc3RlcjCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAzxm4O4OlcihWLbNHboKEmQ9cGlsG1vs0nkFtjLeF6eyi4cV6n8Af
+1OacZV9aqzK8MrBQvYrpbpap2/kFSWx3vbhUVDz5ynsNzhu2Jt914XaJEcDmpDMU
+UwQ+pg7JCSHocW2JilofAZdC1HrefLc570yYwxS//U/cw6N5MnskS2ECAwEAAaOB
+oDCBnTA4BglghkgBhvhCAQ0EKxYpUHVwcGV0IFJ1YnkvT3BlblNTTCBHZW5lcmF0
+ZWQgQ2VydGlmaWNhdGUwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUYSrVz684wuDJ
+3awwcfFxMZGPkNQwCwYDVR0PBAQDAgWgMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr
+BgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADgYEAEL5ZXS5TVzaI56HH
+RhcGFLnvfl2BldsPuVuE5H6fqegRUyLpH7mXHCBt1Zn3IlUUdMLHOump4UHVjw/B
+QPk6ihVjWcBTCU8xu4hKj54MixZFFIo2sJveMVdfIJ3lTQTmRpTHpIU7hYHwP46q
+hLWywdpCBvhCxpK0YSi4FSDiYQ8=
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/master_config/ssl/crl.pem b/spec/fixtures/master_config/ssl/crl.pem
new file mode 100644
index 0000000..90c7a03
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/crl.pem
@@ -0,0 +1,8 @@
+-----BEGIN X509 CRL-----
+MIH5MGQCAQEwDQYJKoZIhvcNAQEFBQAwIjEgMB4GA1UEAwwXUHVwcGV0IENBOiBw
+dXBwZXRtYXN0ZXIXDTExMDgxMzIwMDAwOFoXDTE2MDgxMTIwMDAwOFqgDjAMMAoG
+A1UdFAQDAgEAMA0GCSqGSIb3DQEBBQUAA4GBACBHLkJD4RvEV75ak8w468Kq7r5p
+s87Fzs0Vj2fgqH/3GPoazwBD4R0TvqMb+NUuF0WnipexdQQRjaiERmqX9aIhRjRA
+vs4ItdoxAvcgCzWs6cYm/e4SAAqY5lipfJqd+aRlQgzWaj6WDbFMVEKvqMXqM5wU
+gGQRYVnXHbohA+/I
+-----END X509 CRL-----
diff --git a/spec/fixtures/master_config/ssl/private_keys/bob@mydomain.com.pem b/spec/fixtures/master_config/ssl/private_keys/bob@mydomain.com.pem
new file mode 100644
index 0000000..dcf753a
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/private_keys/bob@mydomain.com.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC+/iHW3fnqIcQl1V3CAr6NSGqgM8o3s1sX8TMaAReJo3b8GPUW
+pUTOYyLZvU4VcCzHs3CEmEjTyONmO9PvVyCv93+jsZ0jKAe5Ayl/TL3G2ZEQLTn8
+Q3lpgLp/j17h/O1Tda+7FYkUkgdnSMQd6oxdRzYQJtKHJklvrGd3rpdpaQIDAQAB
+AoGAWlPwhyFWd9/eV5JQlFgd7M3J99hmk+9Ubr9ZTrwjeKoBtPrMtxgUsZN7QQVh
+74us8gmwdlVbZCZHPeufsTtAroYX9lru3he0oopn82Revc9OYll8hHoXjpLlhJyz
+G40e1DQ5wO0z1WKxAiMvR06i56z03nCgJYp5RzVIx6As0bECQQD1HHuEHZU69C64
+NnDLlokoCMxs/zgjO/oFJbuit63vOc/Ua6oCciEt6xaxUUzVUcOK4VEcjwBKifiK
+ua2Z7ZANAkEAx3owTktjSU2RleIE6aZq7gfgC2ZAjHiIwtFRq573dfCfbj1b2wKk
+8GBudLsXNLZEEgJwJofljjXPAcPAebSLzQJBAM0hVCGCHITlHEBgl09aoViW3HaP
+tSyPojMym/CWpgMiL9OHcxVu7GOgbjJhZtrT/cE5xgcPil/XTeDTefzrevUCQEW0
+X+7sDwzNa0M50Meo3JLC87poB8ROVlPleymCiiyPYdbO4Cs+2E4bFF38Bpbn3g+B
+BJmiQUgZa3XNZpPg0D0CQGJocWKoiWBUcZSbEAaJtBCG+CO+Z8QQP62RpiiYck4W
+VMn3s3fSS8cLdl65jSLM/dXA6YWZlwT/7Vl46ooGdDQ=
+-----END RSA PRIVATE KEY-----
diff --git a/spec/fixtures/master_config/ssl/private_keys/puppetmaster.pem b/spec/fixtures/master_config/ssl/private_keys/puppetmaster.pem
new file mode 100644
index 0000000..4de4dd2
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/private_keys/puppetmaster.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDPGbg7g6VyKFYts0dugoSZD1waWwbW+zSeQW2Mt4Xp7KLhxXqf
+wB/U5pxlX1qrMrwysFC9iululqnb+QVJbHe9uFRUPPnKew3OG7Ym33XhdokRwOak
+MxRTBD6mDskJIehxbYmKWh8Bl0LUet58tznvTJjDFL/9T9zDo3kyeyRLYQIDAQAB
+AoGAIseQ9v2uxTMc9ePLtTVaC1JXB14OEgBx37nhKeaQKK7C0+OUKkvbjKeF0Ehp
+M6L7lA+kH5C6jwXiVLzHNINwwDAqgx+n2O73BjKbqXSVNmdwirX+7nlqVKP6lR+E
+xGYn3MMQhZe956qGJffHbEPQB3dx0mzIcvXSWCTwWaPfTgECQQD4gbARjcSiirGi
+1GA8hMGzdkozt9yb2hbiF79NMGN/wbdYe4FxrmnzGkKXEBvkUFPlGu7GkEwNDtkF
+3MqpsBbxAkEA1VhnLYoPpIDxupJRclwBFxEOBFQ3pkgA5/kxayoeAYLoiMRU6mfE
+bhtA8lWoV9BFNvzVCSFS3Yvb1sXLgWrbcQJAfuu9wTlm9J1hnIhbno0vYTlJLKD7
+S55XkaIPUp0kNFv8CHUL58Ps2PzQhdb0Z+ee8aSPz1pjfUfYD+Z0m7YUAQJBAIvn
+ohnB/MoS6PJBe3m0Dd7zhy6dj7TSaQ22Y4r0HqM9FoKBxXHGRJEz/B4uv+t+H7WU
+jZukJ7QzQCISqYaf7XECQQCbmDO/lv8qdmLfApN2v1H7t9R7tXhEm8jK7OLZsoAT
+QAwCPYh7YRnhpFE0gMUncg+lVu3CTrXtlBRDRgF68Lhx
+-----END RSA PRIVATE KEY-----
diff --git a/spec/fixtures/master_config/ssl/public_keys/bob@mydomain.com.pem b/spec/fixtures/master_config/ssl/public_keys/bob@mydomain.com.pem
new file mode 100644
index 0000000..3d46da5
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/public_keys/bob@mydomain.com.pem
@@ -0,0 +1,5 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAL7+Idbd+eohxCXVXcICvo1IaqAzyjezWxfxMxoBF4mjdvwY9RalRM5j
+Itm9ThVwLMezcISYSNPI42Y70+9XIK/3f6OxnSMoB7kDKX9MvcbZkRAtOfxDeWmA
+un+PXuH87VN1r7sViRSSB2dIxB3qjF1HNhAm0ocmSW+sZ3eul2lpAgMBAAE=
+-----END RSA PUBLIC KEY-----
diff --git a/spec/fixtures/master_config/ssl/public_keys/puppetmaster.pem b/spec/fixtures/master_config/ssl/public_keys/puppetmaster.pem
new file mode 100644
index 0000000..24c6361
--- /dev/null
+++ b/spec/fixtures/master_config/ssl/public_keys/puppetmaster.pem
@@ -0,0 +1,5 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAM8ZuDuDpXIoVi2zR26ChJkPXBpbBtb7NJ5BbYy3hensouHFep/AH9Tm
+nGVfWqsyvDKwUL2K6W6Wqdv5BUlsd724VFQ8+cp7Dc4btibfdeF2iRHA5qQzFFME
+PqYOyQkh6HFtiYpaHwGXQtR63ny3Oe9MmMMUv/1P3MOjeTJ7JEthAgMBAAE=
+-----END RSA PUBLIC KEY-----
diff --git a/spec/unit/puppet/parser/functions/get_certficiate_spec.rb b/spec/unit/puppet/parser/functions/get_certficiate_spec.rb
new file mode 100755
index 0000000..2f5b583
--- /dev/null
+++ b/spec/unit/puppet/parser/functions/get_certficiate_spec.rb
@@ -0,0 +1,158 @@
+#!/usr/bin/env rspec
+
+require 'spec_helper'
+require 'net/http'
+require 'thread'
+require 'fileutils'
+
+describe "the get_certificate function" do
+ include PuppetSpec::Files
+
+ before :all do
+ @sslcert = File.read("spec/master_config/ssl/ca/signed/bob@mydomain.com.pem")
+
+ Puppet::Parser::Functions.autoloader.loadall
+ end
+
+ before :each do
+ @scope = Puppet::Parser::Scope.new
+ end
+
+ it "should exist" do
+ Puppet::Parser::Functions.function("get_certificate").should == "function_get_certificate"
+ end
+
+ it "should raise a ParseError if there is less than 1 argument" do
+ lambda { @scope.function_get_certificate([]) }.should(raise_error(Puppet::ParseError))
+ end
+
+ it "should raise a ParseError if the argument is empty" do
+ lambda { @scope.function_get_certificate([""]) }.should(raise_error(Puppet::ParseError))
+ end
+
+ it "should raise a ParseError if the argument contains strange characters" do
+ lambda { @scope.function_get_certificate(["%^&"]) }.should(raise_error(Puppet::ParseError))
+ end
+
+ it "should return a valid certificate if CA is local" do
+ Puppet[:ca] = true
+ Puppet[:signeddir] = "spec/master_config/ssl/ca/signed/"
+ result = @scope.function_get_certificate(["bob@mydomain.com"])
+ result.should(eq(@sslcert))
+ end
+
+ it "should throw an error if CN is missing and CA is local" do
+ Puppet[:ca] = true
+ Puppet[:signeddir] = "spec/master_config/ssl/ca/signed/"
+ result = @scope.function_get_certificate(["missing@mydomain.com"])
+ result.should(eq(:undef))
+ end
+
+ it "should return a valid certificate if CA is remote" do
+ Puppet[:ca] = false
+ Puppet[:ssldir] = "spec/master_config/ssl"
+ Puppet[:certname] = "puppetmaster"
+
+ # Mock return
+ require 'ostruct'
+ http = OpenStruct.new
+ http.body = @sslcert
+ http.code = "200"
+
+ # Intercept http start call
+ Net::HTTP.any_instance.expects(:start).returns(http)
+
+ result = @scope.function_get_certificate(["bob@mydomain.com"])
+ result.should(eq(@sslcert))
+ end
+
+ it "should throw an error if CN doesn't exist and CA is remote (stubbed)" do
+ Puppet[:ca] = false
+ Puppet[:ssldir] = "spec/master_config/ssl"
+ Puppet[:certname] = "puppetmaster"
+
+ # Mock return
+ require 'ostruct'
+ http = OpenStruct.new
+ http.code = "404"
+
+ # Intercept http start call
+ Net::HTTP.any_instance.expects(:start).returns(http)
+
+ result = @scope.function_get_certificate(["missing@mydomain.com"])
+ result.should(eq(:undef))
+ end
+
+ describe "real puppetmaster" do
+ before :all do
+ # Prepare fixture for puppetmaster
+ @master_tmp = tmpdir("get_certificate") + "/master_config"
+ FileUtils.cp_r("spec/master_config",@master_tmp)
+
+ # Fork and start a puppetmaster
+ master_config = [
+ "--config=/dev/null",
+ "--logdest=#{@master_tmp}/var/log/logfile",
+ "--confdir=#{@master_tmp}",
+ "--no-daemonize",
+ "--masterport=9354",
+ "--bindaddress=127.0.0.1",
+ "--vardir=#{@master_tmp}/var",
+ "--ssldir=#{@master_tmp}/ssl",
+ "--certname=puppetmaster",
+ "--user=#{ENV["USER"]}",
+# "--debug",
+ ]
+ @master = Process.fork do
+ cmd = Puppet::Util::CommandLine.new("master", master_config)
+ Puppet::Plugins.on_application_intialization(:application_object => cmd)
+ app = Puppet::Application.find("master").new(cmd)
+ app.run
+ end
+
+ # Wait 1 second for puppetmatser setup
+ # TODO: must be a better wait to check if master
+ # is listening first before proceeding.
+ sleep 1
+
+ Puppet::Parser::Functions.autoloader.loadall
+ end
+
+ before :each do
+ # Standard puppet setup for each test
+ Puppet[:ca] = false
+ Puppet[:ssldir] = "#{@master_tmp}/ssl"
+ Puppet[:certname] = "puppetmaster"
+ Puppet[:ca_port] = "9354"
+ Puppet[:ca_server] = "127.0.0.1"
+ end
+
+ after :all do
+ # Kill and reap puppetmaster
+ Process.kill("TERM", @master)
+ Process.wait(@master)
+ end
+
+ it "should return a valid certificate if CA is remote" do
+ result = @scope.function_get_certificate(["bob@mydomain.com"])
+ result.should(eq(@sslcert))
+ end
+
+ it "should throw an error if CN doesn't exist and CA is remote" do
+ result = @scope.function_get_certificate(["missing@mydomain.com"])
+ result.should(eq(:undef))
+ end
+
+ it "should throw a connection refused message if CA is not running on port" do
+ Puppet[:ca_port] = "65111"
+ lambda { @scope.function_get_certificate(["missing@mydomain.com"]) }.should(raise_error(Puppet::Error))
+ end
+
+ it "should raise an exception if connection to CA times out" do
+ Puppet[:ca_server] = "10.254.254.254"
+ lambda { @scope.function_get_certificate(["missing@mydomain.com", { :conn_timeout => 1}]) }.should(raise_error(Puppet::Error))
+ end
+
+ end
+
+end
diff --git a/spec/unit/puppet/parser/functions/get_pubkey_spec.rb b/spec/unit/puppet/parser/functions/get_pubkey_spec.rb
new file mode 100755
index 0000000..e4cdd9f
--- /dev/null
+++ b/spec/unit/puppet/parser/functions/get_pubkey_spec.rb
@@ -0,0 +1,54 @@
+#!/usr/bin/env rspec
+
+require 'spec_helper'
+require 'net/http'
+require 'thread'
+require 'fileutils'
+
+describe "the get_pubkey function" do
+ include PuppetSpec::Files
+
+ before :all do
+ Puppet::Parser::Functions.autoloader.loadall
+ end
+
+ before :each do
+ @scope = Puppet::Parser::Scope.new
+ end
+
+ it "should exist" do
+ Puppet::Parser::Functions.function("get_pubkey").should == "function_get_pubkey"
+ end
+
+ it "should raise a ParseError if there is less than 1 argument" do
+ lambda { @scope.function_get_pubkey([]) }.should(raise_error(Puppet::ParseError))
+ end
+
+ it "should raise a ParseError if the argument is empty" do
+ lambda { @scope.function_get_pubkey([""]) }.should(raise_error(Puppet::ParseError))
+ end
+
+ it "should raise a ParseError if the argument contains strange characters" do
+ lambda { @scope.function_get_pubkey(["%^&"]) }.should(raise_error(Puppet::ParseError))
+ end
+
+ it "should return a valid certificate if CA is local" do
+ Puppet[:ca] = true
+ Puppet[:signeddir] = "spec/master_config/ssl/ca/signed/"
+ result = @scope.function_get_pubkey(["bob@mydomain.com"])
+ result.should(eq(<<-EOS))
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAL7+Idbd+eohxCXVXcICvo1IaqAzyjezWxfxMxoBF4mjdvwY9RalRM5j
+Itm9ThVwLMezcISYSNPI42Y70+9XIK/3f6OxnSMoB7kDKX9MvcbZkRAtOfxDeWmA
+un+PXuH87VN1r7sViRSSB2dIxB3qjF1HNhAm0ocmSW+sZ3eul2lpAgMBAAE=
+-----END RSA PUBLIC KEY-----
+EOS
+ end
+
+ it "should throw an error if CN is missing and CA is local" do
+ Puppet[:ca] = true
+ Puppet[:signeddir] = "spec/master_config/ssl/ca/signed/"
+ result = @scope.function_get_pubkey(["missing@mydomain.com"])
+ result.should(eq(:undef))
+ end
+end