Skip to content

Commit 67332ee

Browse files
committed
Custom Download CA Certificates
Previously, when a download was done using the DownloadCache using HTTPS, it was assumed that the SSL certificate presented by the server could be validated with the CA store packaged with OpenSSL. This didn't work for servers that included self-signed, or non-standard CA -based certificates. This change updates the DownloadCache to allow a user to present a collection of additional CA certificates. By creating a /resources/ca_certs directory and placing the certificates (in PEM format) within it, the DownloadCache will use the provided certificates during verification.
1 parent c19642c commit 67332ee

5 files changed

Lines changed: 89 additions & 25 deletions

File tree

Gemfile.lock

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ GEM
55
ast (2.0.0)
66
astrolabe (1.3.0)
77
parser (>= 2.2.0.pre.3, < 3.0)
8-
codeclimate-test-reporter (0.4.0)
8+
codeclimate-test-reporter (0.4.1)
99
simplecov (>= 0.7.1, < 1.0.0)
1010
crack (0.4.2)
1111
safe_yaml (~> 1.0.0)
12-
debase (0.0.9)
13-
debugger-ruby_core_source
12+
debase (0.1.0)
13+
debase-ruby_core_source
14+
debase-ruby_core_source (0.6.0)
1415
debugger-ruby_core_source (1.3.5)
1516
diff-lcs (1.2.5)
1617
docile (1.1.5)
1718
multi_json (1.10.1)
18-
parser (2.2.0.pre.4)
19+
parser (2.2.0.pre.5)
1920
ast (>= 1.1, < 3.0)
2021
slop (~> 3.4, >= 3.4.5)
2122
powerpack (0.0.9)
@@ -26,14 +27,14 @@ GEM
2627
rspec-core (~> 3.1.0)
2728
rspec-expectations (~> 3.1.0)
2829
rspec-mocks (~> 3.1.0)
29-
rspec-core (3.1.4)
30+
rspec-core (3.1.6)
3031
rspec-support (~> 3.1.0)
31-
rspec-expectations (3.1.1)
32+
rspec-expectations (3.1.2)
3233
diff-lcs (>= 1.2.0, < 2.0)
3334
rspec-support (~> 3.1.0)
34-
rspec-mocks (3.1.1)
35+
rspec-mocks (3.1.3)
3536
rspec-support (~> 3.1.0)
36-
rspec-support (3.1.0)
37+
rspec-support (3.1.2)
3738
rubocop (0.26.1)
3839
astrolabe (~> 1.3)
3940
parser (>= 2.2.0.pre.4, < 3.0)
@@ -47,15 +48,15 @@ GEM
4748
rake (>= 0.8.1)
4849
ruby-progressbar (1.6.0)
4950
rubyzip (1.1.6)
50-
safe_yaml (1.0.3)
51+
safe_yaml (1.0.4)
5152
simplecov (0.9.1)
5253
docile (~> 1.1.0)
5354
multi_json (~> 1.0)
5455
simplecov-html (~> 0.8.0)
5556
simplecov-html (0.8.0)
5657
slop (3.6.0)
5758
tee (1.0.0)
58-
webmock (1.18.0)
59+
webmock (1.19.0)
5960
addressable (>= 2.3.6)
6061
crack (>= 0.3.2)
6162
yard (0.8.7.4)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ To learn how to configure various properties of the buildpack, follow the "Confi
6262
* [Caches](docs/extending-caches.md) ([Configuration](docs/extending-caches.md#configuration))
6363
* [Logging](docs/extending-logging.md) ([Configuration](docs/extending-logging.md#configuration))
6464
* [Repositories](docs/extending-repositories.md) ([Configuration](docs/extending-repositories.md#configuration))
65-
* [Utiltities](docs/extending-utiltities.md)
65+
* [Utilities](docs/extending-utiltities.md)
6666
* [Debugging the Buildpack](docs/debugging-the-buildpack.md)
6767
* [Buildpack Modes](docs/buildpack-modes.md)
6868
* Related Projects

java-buildpack.iml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,34 +275,33 @@
275275
<orderEntry type="library" scope="PROVIDED" name="ast (v2.0.0, rbenv: 1.9.3-p547) [gem]" level="application" />
276276
<orderEntry type="library" scope="PROVIDED" name="astrolabe (v1.3.0, rbenv: 1.9.3-p547) [gem]" level="application" />
277277
<orderEntry type="library" scope="PROVIDED" name="bundler (v1.7.3, rbenv: 1.9.3-p547) [gem]" level="application" />
278-
<orderEntry type="library" scope="PROVIDED" name="codeclimate-test-reporter (v0.4.0, rbenv: 1.9.3-p547) [gem]" level="application" />
278+
<orderEntry type="library" scope="PROVIDED" name="codeclimate-test-reporter (v0.4.1, rbenv: 1.9.3-p547) [gem]" level="application" />
279279
<orderEntry type="library" scope="PROVIDED" name="crack (v0.4.2, rbenv: 1.9.3-p547) [gem]" level="application" />
280-
<orderEntry type="library" scope="PROVIDED" name="debase (v0.0.9, rbenv: 1.9.3-p547) [gem]" level="application" />
281280
<orderEntry type="library" scope="PROVIDED" name="debugger-ruby_core_source (v1.3.5, rbenv: 1.9.3-p547) [gem]" level="application" />
282281
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.2.5, rbenv: 1.9.3-p547) [gem]" level="application" />
283282
<orderEntry type="library" scope="PROVIDED" name="docile (v1.1.5, rbenv: 1.9.3-p547) [gem]" level="application" />
284283
<orderEntry type="library" scope="PROVIDED" name="multi_json (v1.10.1, rbenv: 1.9.3-p547) [gem]" level="application" />
285-
<orderEntry type="library" scope="PROVIDED" name="parser (v2.2.0.pre.4, rbenv: 1.9.3-p547) [gem]" level="application" />
284+
<orderEntry type="library" scope="PROVIDED" name="parser (v2.2.0.pre.5, rbenv: 1.9.3-p547) [gem]" level="application" />
286285
<orderEntry type="library" scope="PROVIDED" name="powerpack (v0.0.9, rbenv: 1.9.3-p547) [gem]" level="application" />
287286
<orderEntry type="library" scope="PROVIDED" name="rainbow (v2.0.0, rbenv: 1.9.3-p547) [gem]" level="application" />
288287
<orderEntry type="library" scope="PROVIDED" name="rake (v10.3.2, rbenv: 1.9.3-p547) [gem]" level="application" />
289288
<orderEntry type="library" scope="PROVIDED" name="redcarpet (v3.1.2, rbenv: 1.9.3-p547) [gem]" level="application" />
290289
<orderEntry type="library" scope="PROVIDED" name="rspec (v3.1.0, rbenv: 1.9.3-p547) [gem]" level="application" />
291-
<orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.1.4, rbenv: 1.9.3-p547) [gem]" level="application" />
292-
<orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.1.1, rbenv: 1.9.3-p547) [gem]" level="application" />
293-
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.1.1, rbenv: 1.9.3-p547) [gem]" level="application" />
294-
<orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.1.0, rbenv: 1.9.3-p547) [gem]" level="application" />
290+
<orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.1.6, rbenv: 1.9.3-p547) [gem]" level="application" />
291+
<orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.1.2, rbenv: 1.9.3-p547) [gem]" level="application" />
292+
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.1.3, rbenv: 1.9.3-p547) [gem]" level="application" />
293+
<orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.1.2, rbenv: 1.9.3-p547) [gem]" level="application" />
295294
<orderEntry type="library" scope="PROVIDED" name="rubocop (v0.26.1, rbenv: 1.9.3-p547) [gem]" level="application" />
296295
<orderEntry type="library" scope="PROVIDED" name="ruby-debug-base19x (v0.11.30.pre15, rbenv: 1.9.3-p547) [gem]" level="application" />
297296
<orderEntry type="library" scope="PROVIDED" name="ruby-debug-ide (v0.4.22, rbenv: 1.9.3-p547) [gem]" level="application" />
298297
<orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.6.0, rbenv: 1.9.3-p547) [gem]" level="application" />
299298
<orderEntry type="library" scope="PROVIDED" name="rubyzip (v1.1.6, rbenv: 1.9.3-p547) [gem]" level="application" />
300-
<orderEntry type="library" scope="PROVIDED" name="safe_yaml (v1.0.3, rbenv: 1.9.3-p547) [gem]" level="application" />
299+
<orderEntry type="library" scope="PROVIDED" name="safe_yaml (v1.0.4, rbenv: 1.9.3-p547) [gem]" level="application" />
301300
<orderEntry type="library" scope="PROVIDED" name="simplecov (v0.9.1, rbenv: 1.9.3-p547) [gem]" level="application" />
302301
<orderEntry type="library" scope="PROVIDED" name="simplecov-html (v0.8.0, rbenv: 1.9.3-p547) [gem]" level="application" />
303302
<orderEntry type="library" scope="PROVIDED" name="slop (v3.6.0, rbenv: 1.9.3-p547) [gem]" level="application" />
304303
<orderEntry type="library" scope="PROVIDED" name="tee (v1.0.0, rbenv: 1.9.3-p547) [gem]" level="application" />
305-
<orderEntry type="library" scope="PROVIDED" name="webmock (v1.18.0, rbenv: 1.9.3-p547) [gem]" level="application" />
304+
<orderEntry type="library" scope="PROVIDED" name="webmock (v1.19.0, rbenv: 1.9.3-p547) [gem]" level="application" />
306305
<orderEntry type="library" scope="PROVIDED" name="yard (v0.8.7.4, rbenv: 1.9.3-p547) [gem]" level="application" />
307306
</component>
308307
<component name="RModuleSettingsStorage">

lib/java_buildpack/util/cache/download_cache.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
require 'java_buildpack/util/cache/internet_availability'
2222
require 'monitor'
2323
require 'net/http'
24+
require 'pathname'
2425
require 'tmpdir'
2526
require 'uri'
2627

@@ -76,6 +77,8 @@ def evict(uri)
7677

7778
private
7879

80+
CA_CERTS_DIRECTORY = (Pathname.new(__FILE__).dirname + '../../../../resources/ca_certs').freeze
81+
7982
FAILURE_LIMIT = 5.freeze
8083

8184
HTTP_ERRORS = [
@@ -109,7 +112,7 @@ def evict(uri)
109112

110113
TIMEOUT_SECONDS = 10.freeze
111114

112-
private_constant :FAILURE_LIMIT, :HTTP_ERRORS, :REDIRECT_TYPES, :TIMEOUT_SECONDS
115+
private_constant :CA_CERTS_DIRECTORY, :FAILURE_LIMIT, :HTTP_ERRORS, :REDIRECT_TYPES, :TIMEOUT_SECONDS
113116

114117
def attempt(http, request, cached_file)
115118
downloaded = false
@@ -198,10 +201,17 @@ def from_immutable_caches(uri)
198201

199202
# Beware known problems with timeouts: https://www.ruby-forum.com/topic/143840
200203
def http_options(rich_uri)
201-
{ read_timeout: TIMEOUT_SECONDS,
202-
connect_timeout: TIMEOUT_SECONDS,
203-
open_timeout: TIMEOUT_SECONDS,
204-
use_ssl: secure?(rich_uri) }
204+
http_options = {}
205+
http_options[:connect_timeout] = TIMEOUT_SECONDS
206+
http_options[:open_timeout] = TIMEOUT_SECONDS
207+
http_options[:read_timeout] = TIMEOUT_SECONDS
208+
209+
if secure?(rich_uri)
210+
http_options[:use_ssl] = true
211+
http_options[:ca_path] = CA_CERTS_DIRECTORY.to_s if CA_CERTS_DIRECTORY.exist?
212+
end
213+
214+
http_options
205215
end
206216

207217
def proxy(uri)

spec/java_buildpack/util/cache/download_cache_spec.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
include_context 'internet_availability_helper'
2828
include_context 'logging_helper'
2929

30+
let(:ca_certs_directory) { double exist?: false, to_s: 'test-path' }
31+
3032
let(:mutable_cache_root) { app_dir + 'mutable' }
3133

3234
let(:immutable_cache_root) { app_dir + 'immutable' }
@@ -39,6 +41,10 @@
3941

4042
let(:download_cache) { described_class.new(mutable_cache_root, immutable_cache_root) }
4143

44+
before do
45+
described_class.const_set :CA_CERTS_DIRECTORY, ca_certs_directory
46+
end
47+
4248
it 'should raise error if file cannot be found',
4349
:disable_internet do
4450

@@ -228,6 +234,54 @@
228234

229235
end
230236

237+
it 'should not use ca_path if the URL is not secure and directory does not exist' do
238+
stub_request(:get, uri)
239+
.to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' })
240+
241+
allow(Net::HTTP).to receive(:Proxy).and_call_original
242+
expect(Net::HTTP).to receive(:start).with('foo-uri', 80, connect_timeout: 10, open_timeout: 10, read_timeout: 10)
243+
.and_call_original
244+
245+
download_cache.get(uri) {}
246+
end
247+
248+
it 'should not use ca_path if the URL is not secure and directory does exist' do
249+
stub_request(:get, uri)
250+
.to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' })
251+
252+
allow(ca_certs_directory).to receive(:exist?).and_return(true)
253+
allow(Net::HTTP).to receive(:Proxy).and_call_original
254+
expect(Net::HTTP).to receive(:start).with('foo-uri', 80, connect_timeout: 10, open_timeout: 10, read_timeout: 10)
255+
.and_call_original
256+
257+
download_cache.get(uri) {}
258+
end
259+
260+
it 'should not use ca_path if the URL is secure and directory does not exist' do
261+
stub_request(:get, uri_secure)
262+
.to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' })
263+
264+
allow(Net::HTTP).to receive(:Proxy).and_call_original
265+
expect(Net::HTTP).to receive(:start)
266+
.with('foo-uri', 443, connect_timeout: 10, open_timeout: 10, read_timeout: 10, use_ssl: true)
267+
.and_call_original
268+
269+
download_cache.get(uri_secure) {}
270+
end
271+
272+
it 'should use ca_path if the URL is secure and directory does exist' do
273+
stub_request(:get, uri_secure)
274+
.to_return(status: 200, body: 'foo-cached', headers: { Etag: 'foo-etag', 'Last-Modified' => 'foo-last-modified' })
275+
276+
allow(ca_certs_directory).to receive(:exist?).and_return(true)
277+
allow(Net::HTTP).to receive(:Proxy).and_call_original
278+
expect(Net::HTTP).to receive(:start)
279+
.with('foo-uri', 443, connect_timeout: 10, open_timeout: 10, read_timeout: 10, use_ssl: true,
280+
ca_path: 'test-path').and_call_original
281+
282+
download_cache.get(uri_secure) {}
283+
end
284+
231285
it 'should delete the cached file if it exists' do
232286
expect_file_deleted 'cached'
233287
end

0 commit comments

Comments
 (0)