Hi, I wanted to change how I use/classify my puppet agents, and use trusted facts but had to figure out, it doesn't work for me as it should ;)
It took me quite some time to dig through it, but I eventually got it to work with unicorn behind nginx and apache, and the plain webrick puppetmaster. First I tried to run the puppet built-in webrick puppetmaster, to get the certificate extensions available in my manifests, I had to tweak the lib/puppet/ssl/certificate.rb. Don't know what's wrong here, why it not worked as it was before, maybe some Ruby guru can shed some light on that one. After I got that to work, I switched back to my unicorn behind nginx setup, and found that also not working. Some debugging, I found the lib/puppet/network/http/rack/rest.rb, esp. the +ExportCertData comment in it. This is only available in Apache, so switched to use apache in front of unicorn, but still no luck. I figured that running unicorn behind apache reverse proxying, the environment variable is not available. Therefore I added an additional header that gets passed to unicorn: X-SSL-Client-Cert. However, that header is sent as single line from Apache to unicorn, and not as valid PEM encoded certificate. Therefore the gsub! manipulations to restore a valid PEM certificate again. To make trusted facts work for Apache/unicorn, add a line like: RequestHeader set X-SSL-Client-Cert %{SSL_CLIENT_CERT}e To pass that on. Then switched back to nginx. Nginx has $ssl_client_cert variable as well, but nginx passes that variable on as multi-line header. Doh! Unicorn doesn't like that at all. For nginx, install the lua flavor, and add this to the location statement: location / { set_by_lua $client_cert "return ngx.var.ssl_client_raw_cert:gsub('\\n',' ')"; proxy_set_header X-SSL-Client-Cert $client_cert; } That's transforming the certificate into a single line before it is passed on to the unicorn. Is someone using puppetmaster on OpenBSD, and makes use of trusted facts? If so, how are you using it? I planned to also report this upstream. Generally, feedback welcome, or even OK? cheers, Sebastian Index: Makefile =================================================================== RCS file: /cvs/ports/sysutils/ruby-puppet/3/Makefile,v retrieving revision 1.132 diff -u -r1.132 Makefile --- Makefile 29 Apr 2016 07:59:53 -0000 1.132 +++ Makefile 27 May 2016 08:15:42 -0000 @@ -3,7 +3,7 @@ PORTROACH= limit:^3 VERSION= 3.8.7 -REVISION= 2 +REVISION= 3 RUN_DEPENDS+= databases/ruby-hiera,${MODRUBY_FLAVOR} \ sysutils/ruby-facter>=2.0.1p0 Index: patches/patch-lib_puppet_network_http_rack_rest_rb =================================================================== RCS file: patches/patch-lib_puppet_network_http_rack_rest_rb diff -N patches/patch-lib_puppet_network_http_rack_rest_rb --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lib_puppet_network_http_rack_rest_rb 27 May 2016 08:15:42 -0000 @@ -0,0 +1,31 @@ +$OpenBSD$ + +Make puppet trusted facts available when running with unicorn behind +nginx or apache + +--- lib/puppet/network/http/rack/rest.rb.orig Fri May 27 10:11:44 2016 ++++ lib/puppet/network/http/rack/rest.rb Fri May 27 10:11:50 2016 +@@ -92,7 +92,22 @@ class Puppet::Network::HTTP::RackREST + # NOTE: The SSL_CLIENT_CERT environment variable will be the empty string + # when Puppet agent nodes have not yet obtained a signed certificate. + if cert.nil? || cert.empty? +- nil ++ # When running with unicorn, the SSL_CLIENT_CERT variable is not available ++ # in the environment, therefore we have to pass a header: 'X-SSL-Client-Cert' ++ cert = request.env['HTTP_X_SSL_CLIENT_CERT'] ++ if cert.nil? || cert.empty? ++ nil ++ else ++ # in contrast to the environment variable, the client cert is passed in ++ # as single string, therefore restore the certificate to a valid pem ++ # encoded certificate ++ cert.gsub!(/ /, "\n") ++ cert.gsub!(/BEGIN\nCERT/, "BEGIN CERT") ++ cert.gsub!(/END\nCERT/, "END CERT") ++ cert = Puppet::SSL::Certificate.from_instance(OpenSSL::X509::Certificate.new(cert)) ++ warn_if_near_expiration(cert) ++ cert ++ end + else + cert = Puppet::SSL::Certificate.from_instance(OpenSSL::X509::Certificate.new(cert)) + warn_if_near_expiration(cert) Index: patches/patch-lib_puppet_ssl_certificate_rb =================================================================== RCS file: patches/patch-lib_puppet_ssl_certificate_rb diff -N patches/patch-lib_puppet_ssl_certificate_rb --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lib_puppet_ssl_certificate_rb 27 May 2016 08:15:42 -0000 @@ -0,0 +1,16 @@ +$OpenBSD$ + +Make certificate extension available in puppet manifests + +--- lib/puppet/ssl/certificate.rb.orig Wed May 25 08:13:50 2016 ++++ lib/puppet/ssl/certificate.rb Wed May 25 08:14:35 2016 +@@ -60,6 +60,8 @@ DOC + Puppet::SSL::Oids.subtree_of?('ppPrivCertExt', ext.oid) + end + +- custom_exts.map { |ext| {'oid' => ext.oid, 'value' => ext.value} } ++ extensions = [] ++ custom_exts.map { |ext| extensions << {'oid' => ext.oid, 'value' => ext.value} } ++ return extensions + end + end
Index: Makefile =================================================================== RCS file: /cvs/ports/sysutils/ruby-puppet/3/Makefile,v retrieving revision 1.132 diff -u -r1.132 Makefile --- Makefile 29 Apr 2016 07:59:53 -0000 1.132 +++ Makefile 27 May 2016 08:15:42 -0000 @@ -3,7 +3,7 @@ PORTROACH= limit:^3 VERSION= 3.8.7 -REVISION= 2 +REVISION= 3 RUN_DEPENDS+= databases/ruby-hiera,${MODRUBY_FLAVOR} \ sysutils/ruby-facter>=2.0.1p0 Index: patches/patch-lib_puppet_network_http_rack_rest_rb =================================================================== RCS file: patches/patch-lib_puppet_network_http_rack_rest_rb diff -N patches/patch-lib_puppet_network_http_rack_rest_rb --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lib_puppet_network_http_rack_rest_rb 27 May 2016 08:15:42 -0000 @@ -0,0 +1,31 @@ +$OpenBSD$ + +Make puppet trusted facts available when running with unicorn behind +nginx or apache + +--- lib/puppet/network/http/rack/rest.rb.orig Fri May 27 10:11:44 2016 ++++ lib/puppet/network/http/rack/rest.rb Fri May 27 10:11:50 2016 +@@ -92,7 +92,22 @@ class Puppet::Network::HTTP::RackREST + # NOTE: The SSL_CLIENT_CERT environment variable will be the empty string + # when Puppet agent nodes have not yet obtained a signed certificate. + if cert.nil? || cert.empty? +- nil ++ # When running with unicorn, the SSL_CLIENT_CERT variable is not available ++ # in the environment, therefore we have to pass a header: 'X-SSL-Client-Cert' ++ cert = request.env['HTTP_X_SSL_CLIENT_CERT'] ++ if cert.nil? || cert.empty? ++ nil ++ else ++ # in contrast to the environment variable, the client cert is passed in ++ # as single string, therefore restore the certificate to a valid pem ++ # encoded certificate ++ cert.gsub!(/ /, "\n") ++ cert.gsub!(/BEGIN\nCERT/, "BEGIN CERT") ++ cert.gsub!(/END\nCERT/, "END CERT") ++ cert = Puppet::SSL::Certificate.from_instance(OpenSSL::X509::Certificate.new(cert)) ++ warn_if_near_expiration(cert) ++ cert ++ end + else + cert = Puppet::SSL::Certificate.from_instance(OpenSSL::X509::Certificate.new(cert)) + warn_if_near_expiration(cert) Index: patches/patch-lib_puppet_ssl_certificate_rb =================================================================== RCS file: patches/patch-lib_puppet_ssl_certificate_rb diff -N patches/patch-lib_puppet_ssl_certificate_rb --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lib_puppet_ssl_certificate_rb 27 May 2016 08:15:42 -0000 @@ -0,0 +1,16 @@ +$OpenBSD$ + +Make certificate extension available in puppet manifests + +--- lib/puppet/ssl/certificate.rb.orig Wed May 25 08:13:50 2016 ++++ lib/puppet/ssl/certificate.rb Wed May 25 08:14:35 2016 +@@ -60,6 +60,8 @@ DOC + Puppet::SSL::Oids.subtree_of?('ppPrivCertExt', ext.oid) + end + +- custom_exts.map { |ext| {'oid' => ext.oid, 'value' => ext.value} } ++ extensions = [] ++ custom_exts.map { |ext| extensions << {'oid' => ext.oid, 'value' => ext.value} } ++ return extensions + end + end