Skip to content

Commit

Permalink
Container Certificate Trust Store
Browse files Browse the repository at this point in the history
This change adds a framework that provides a Java trust store containing the
certificates trusted by the container's operating system.  This feature is
disabled by default, as it requires around 45 seconds to process the 173
default certificates.

[#76531562]
  • Loading branch information
nebhale committed Feb 12, 2016
1 parent a8ca100 commit d902368
Show file tree
Hide file tree
Showing 9 changed files with 343 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ To learn how to configure various properties of the buildpack, follow the "Confi
* [Tomcat](docs/container-tomcat.md) ([Configuration](docs/container-tomcat.md#configuration))
* Standard Frameworks
* [AppDynamics Agent](docs/framework-app_dynamics_agent.md) ([Configuration](docs/framework-app_dynamics_agent.md#configuration))
* [Container Certificate Trust Store](docs/framework-container_certificate_trust_store.md) ([Configuration](docs/framework-container_certificate_trust_store.md#configuration))
* [Debug](docs/framework-debug.md) ([Configuration](docs/framework-debug.md#configuration))
* [DynaTrace Agent](docs/framework-dyna_trace_agent.md) ([Configuration](docs/framework-dyna_trace_agent.md#configuration))
* [Introscope Agent](docs/framework-introscope_agent.md) ([Configuration](docs/framework-introscope_agent.md#configuration))
Expand Down
1 change: 1 addition & 0 deletions config/components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jres:

frameworks:
- "JavaBuildpack::Framework::AppDynamicsAgent"
- "JavaBuildpack::Framework::ContainerCertificateTrustStore"
- "JavaBuildpack::Framework::Debug"
# - "JavaBuildpack::Framework::DynaTraceAgent"
# - "JavaBuildpack::Framework::IntroscopeAgent"
Expand Down
18 changes: 18 additions & 0 deletions config/container_certificate_trust_store.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Cloud Foundry Java Buildpack
# Copyright 2014-2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Container certificate truststore configuration
---
enabled: false
5 changes: 3 additions & 2 deletions docs/container-java_main.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Command line arguments may optionally be configured.

<table>
<tr>
<td><strong>Detection Criteria</strong></td><td><tt>Main-Class</tt> attribute set in <tt>META-INF/MANIFEST.MF</tt> or <tt>java_main_class</tt> set in <tt>config/java_main.yml<tt></td>
<td><strong>Detection Criteria</strong></td>
<td><tt>Main-Class</tt> attribute set in <tt>META-INF/MANIFEST.MF</tt> or <tt>java_main_class</tt> set in <tt>config/java_main.yml<tt></td>
</tr>
<tr>
<td><strong>Tags</strong></td>
Expand All @@ -18,7 +19,7 @@ Command line arguments may optionally be configured.
</table>
Tags are printed to standard output by the buildpack detect script

If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others.
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others.

## Spring Boot

Expand Down
3 changes: 2 additions & 1 deletion docs/container-tomcat.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ The Tomcat Container allows servlet 2 and 3 web applications to be run. These a

<table>
<tr>
<td><strong>Detection Criterion</strong></td><td>Existence of a <tt>WEB-INF/</tt> folder in the application directory and <a href="container-java_main.md">Java Main</a> not detected</td>
<td><strong>Detection Criterion</strong></td>
<td>Existence of a <tt>WEB-INF/</tt> folder in the application directory and <a href="container-java_main.md">Java Main</a> not detected</td>
</tr>
<tr>
<td><strong>Tags</strong></td>
Expand Down
26 changes: 26 additions & 0 deletions docs/framework-container_certificate_trust_store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Container Certificate Trust Store Framework
The Container Certificate Trust Store Framework contributes a Java `KeyStore` containing the certificates trusted by the operating system in the container to the application at rutime.

<table>
<tr>
<td><strong>Detection Criterion</strong></td>
<td>Existence of a <tt>/etc/ssl/certs/ca-certificates.crt</tt> file and <tt>enabled</tt> set in the <tt>config/container_certificate_trust_store.yml</tt> file</td>
</tr>
<tr>
<td><strong>Tags</strong></td>
<td><tt>container-certificate-trust-store=&lt;number-of-certificates&gt;</tt></td>
</tr>
</table>
Tags are printed to standard output by the buildpack detect script

## Configuration
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].

The framework can be configured by creating or modifying the [`config/container_certificate_trust_store.yml`][] file in the buildpack fork.

| Name | Description
| ---- | -----------
| `enabled` | Whether to enable the trust store

[`config/container_certificate_trust_store.yml`]: ../config/container_certificate_trust_store.yml
[Configuration and Extension]: ../README.md#configuration-and-extension
137 changes: 137 additions & 0 deletions lib/java_buildpack/framework/container_certificate_trust_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013-2016 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'java_buildpack/component/base_component'
require 'java_buildpack/framework'
require 'java_buildpack/util/dash_case'
require 'java_buildpack/util/format_duration'
require 'fileutils'
require 'shellwords'
require 'tempfile'

module JavaBuildpack
module Framework

# Encapsulates the functionality for contributing container-based certificates to an application.
class ContainerCertificateTrustStore < JavaBuildpack::Component::BaseComponent

# Creates an instance
#
# @param [Hash] context a collection of utilities used the component
def initialize(context)
@logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger ContainerCertificateTrustStore
super(context)
end

# (see JavaBuildpack::Component::BaseComponent#detect)
def detect
(supports_configuration? && supports_file?) ? id(certificates.length) : nil
end

# (see JavaBuildpack::Component::BaseComponent#compile)
def compile
puts '-----> Creating TrustStore with container certificates'

resolved_certificates = certificates
with_timing(caption(resolved_certificates)) do
FileUtils.mkdir_p trust_store.parent
resolved_certificates.each_with_index { |certificate, index| add_certificate certificate, index }
end
end

# (see JavaBuildpack::Component::BaseComponent#release)
def release
@droplet.java_opts
.add_system_property('javax.net.ssl.trustStore', trust_store)
.add_system_property('javax.net.ssl.trustStorePassword', password)
end

private

CA_CERTIFICATES = Pathname.new('/etc/ssl/certs/ca-certificates.crt').freeze

private_constant :CA_CERTIFICATES

def add_certificate(certificate, index)
@logger.debug { "Adding certificate\n#{certificate}" }

file = write_certificate certificate
shell "#{keytool} -importcert -noprompt -keystore #{trust_store} -storepass #{password} " \
"-file #{file.to_path} -alias certificate-#{index}"
end

def ca_certificates
CA_CERTIFICATES
end

def caption(resolved_certificates)
"Adding #{resolved_certificates.count} certificates to #{trust_store.relative_path_from(@droplet.root)}"
end

def certificates
certificates = []

certificate = nil
ca_certificates.each_line do |line|
if line =~ /BEGIN CERTIFICATE/
certificate = line
elsif line =~ /END CERTIFICATE/
certificate += line
certificates << certificate
certificate = nil
elsif !certificate.nil?
certificate += line
end
end

certificates
end

def id(count)
"#{self.class.to_s.dash_case}=#{count}"
end

def keytool
@droplet.java_home.root + 'bin/keytool'
end

def password
'java-buildpack-trust-store-password'
end

def supports_configuration?
@configuration['enabled']
end

def supports_file?
ca_certificates.exist?
end

def trust_store
@droplet.sandbox + 'truststore.jks'
end

def write_certificate(certificate)
file = Tempfile.new('certificate-')
file.write(certificate)
file.fsync
file
end

end

end
end
77 changes: 77 additions & 0 deletions spec/fixtures/ca-certificates.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Wed Jan 20 04:12:04 2016
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
## file (certdata.txt). This file can be found in the mozilla source tree:
## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
##
## It contains the certificates in PEM format and therefore
## can be directly used with curl / libcurl / php_curl, or with
## an Apache+mod_ssl webserver for SSL client authentication.
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.25.
## SHA1: 0ab47e2f41518f8d223eab517cb799e5b071231e
##


GlobalSign Root CA
==================
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

GlobalSign Root CA - R2
=======================
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6
ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp
s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN
S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL
TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C
ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i
YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN
BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp
9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu
01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7
9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
-----END CERTIFICATE-----

CA WoSign ECC Root
==================
-----BEGIN CERTIFICATE-----
MIICCTCCAY+gAwIBAgIQaEpYcIBr8I8C+vbe6LCQkDAKBggqhkjOPQQDAzBGMQswCQYDVQQGEwJD
TjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMTEkNBIFdvU2lnbiBFQ0MgUm9v
dDAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQK
ExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAxMSQ0EgV29TaWduIEVDQyBSb290MHYwEAYHKoZI
zj0CAQYFK4EEACIDYgAE4f2OuEMkq5Z7hcK6C62N4DrjJLnSsb6IOsq/Srj57ywvr1FQPEd1bPiU
t5v8KB7FVMxjnRZLU8HnIKvNrCXSf4/CwVqCXjCLelTOA7WRf6qU0NGKSMyCBSah1VES1ns2o0Iw
QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqv3VWqP2h4syhf3R
MluARZPzA7gwCgYIKoZIzj0EAwMDaAAwZQIxAOSkhLCB1T2wdKyUpOgOPQB0TKGXa/kNUTyh2Tv0
Daupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu
a/GRspBl9JrmkO5K
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013-2016 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'spec_helper'
require 'component_helper'
require 'java_buildpack/framework/container_certificate_trust_store'

describe JavaBuildpack::Framework::ContainerCertificateTrustStore do
include_context 'component_helper'

let(:ca_certificates) { Pathname.new('spec/fixtures/ca-certificates.crt') }

let(:configuration) { { 'enabled' => true } }

it 'detects with ca-certificates file' do
allow(component).to receive(:ca_certificates).and_return(ca_certificates)

expect(component.detect).to eq('container-certificate-trust-store=3')
end

it 'does not detect without ca-certificates file' do
allow(component).to receive(:ca_certificates).and_return(Pathname.new('spec/fixtures/ca-certificates-no-exist.crt'))

expect(component.detect).to be_nil
end

context do
let(:configuration) { { 'enabled' => false } }

it 'does not detect when disabled' do
allow(component).to receive(:ca_certificates).and_return(ca_certificates)

expect(component.detect).to be_nil
end
end

it 'creates truststore' do
allow(component).to receive(:ca_certificates).and_return(ca_certificates)
allow(component).to receive(:write_certificate).and_return(Pathname.new('/certificate-0'),
Pathname.new('/certificate-1'),
Pathname.new('/certificate-2'))
allow(component).to receive(:shell).with("#{java_home.root}/bin/keytool -importcert -noprompt " \
"-keystore #{sandbox}/truststore.jks -storepass " \
'java-buildpack-trust-store-password -file /certificate-0 -alias ' \
'certificate-0')
allow(component).to receive(:shell).with("#{java_home.root}/bin/keytool -importcert -noprompt " \
"-keystore #{sandbox}/truststore.jks -storepass " \
'java-buildpack-trust-store-password -file /certificate-1 -alias ' \
'certificate-1')
allow(component).to receive(:shell).with("#{java_home.root}/bin/keytool -importcert -noprompt " \
"-keystore #{sandbox}/truststore.jks -storepass " \
'java-buildpack-trust-store-password -file /certificate-2 -alias ' \
'certificate-2')

component.compile
end

it 'adds truststore properties' do
component.release
expect(java_opts).to include('-Djavax.net.ssl.trustStore=$PWD/.java-buildpack/container_certificate_trust_store/' \
'truststore.jks')
expect(java_opts).to include('-Djavax.net.ssl.trustStorePassword=java-buildpack-trust-store-password')
end

end

0 comments on commit d902368

Please sign in to comment.