pact-foundation/pact-provider-verifier

certificate verify failed (OpenSSL::SSL::SSLError) for the ssl that signed by QuoVadis

justindongnz opened this issue · 25 comments

The pact provider verifier returns SSL error if the SSL is signed by QuoVadis.

Does pact provider verifier support QuoVadis?
If yes, was it something wrong with my configuration?
If not, is it possible to bypass certificate verification or any other solutions about that?

It's working if the certificate is signed by Amazon but not QuoVadis.
And my QuoVadis signed pact provider is working when using Firefox/chrome. So it shouldn't be related to the certificate issue. I assume something wrong with pact-provider-verifier.

I took a lot time to make it through. But only find a workaround solution as below.
vi /app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/vendor/ruby/2.2.0/gems/pact-1.41.0/lib/pact/hal/http_client.rb
added the content on line 53.
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

vi ./node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/2.2.0/net/http.rb
change line 574 from
opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt)
to
opt = {verify_mode: OpenSSL::SSL::VERIFY_NONE}.update(opt)

It is not a good way to do so to be honest.

Screenshot 2019-09-11 at 11 28 40

It will be because the SSL certs bundled in the ruby runtime don't have the root cert for QuoVadis. Try setting the env var SSL_CERT_FILE with a root certificate for QuoVadis as per https://github.com/pact-foundation/pact-ruby-standalone/releases#pact-provider-verifier

Thank you very much.
I'll try.

Hi @bethesque We have tried setting env var SSL_CERT_FILE as well SSL_CERT_DIR but no success. I have a question since we are using nodejs package will these env variables be picked up by ruby runtime?

Yes, they should be. Can you try an experiment for me?

Set SSL_CERT_FILE to what you think it should be. Then in your terminal run /app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/bin/irb (the interactive ruby shell). I just hand typed that line from looking at your screenshot so there may be typos.

Modify this code to point it to your server and execute it.

require 'net/http'
require 'uri'

uri = URI("https://postman-echo.com/post")
req = Net::HTTP::Post.new(uri)
response =  Net::HTTP.start(uri.hostname, uri.port, :ENV, use_ssl: true) do |http|
  http.request req
end
puts response.code
puts response.to_hash
puts response.body

If that doesn't work, there's something wrong with the SSL_CERT_FILE value or the contents of the file. If it does work, then we need to work out why the SSL_CERT_FILE is not being passed through to the right code.

Thanks a lot @bethesque I will try this and let u know outcome....

I have tried it but same error.... I have followed steps as mentioned below...

export $SSL_CERT_FILE=~/<directorypath>/QuoVadis_Root_CA.crt

Since i am using Mac irb is located in different path.

node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/bin/irb

and there i have executed code you have suggested. But got following result

node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/bin/irb
No entry for terminal type "xterm-256color";
using dumb terminal settings.
No entry for terminal type "xterm-256color";
using dumb terminal settings.
No entry for terminal type "xterm-256color";
using dumb terminal settings.
irb(main):001:0> require 'net/http'
=> true
irb(main):002:0> require 'uri'
=> false
irb(main):003:0> uri = URI("https://PACT_BROKER_URL")
=> #<URI::HTTPS https://PACT_BROKER_URL>
irb(main):004:0> req = Net::HTTP::Post.new(uri)
=> #<Net::HTTP::Post POST>
irb(main):005:0> response =  Net::HTTP.start(uri.hostname, uri.port, :ENV, use_ssl: true) do |http|
irb(main):006:1*   http.request req
irb(main):007:1> end
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/lib/ruby/2.2.0/net/http.rb:923:in `connect'
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/lib/ruby/2.2.0/net/http.rb:923:in `block in connect'
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/lib/ruby/2.2.0/timeout.rb:74:in `timeout'
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/lib/ruby/2.2.0/net/http.rb:923:in `connect'
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/lib/ruby/2.2.0/net/http.rb:863:in `do_start'
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/lib/ruby/2.2.0/net/http.rb:852:in `start'
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/lib/ruby/2.2.0/net/http.rb:583:in `start'
	from (irb):5
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/bin.real/irb:11:in `<main>'
irb(main):008:0> puts response.code
NoMethodError: undefined method `code' for nil:NilClass
	from (irb):8
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/bin.real/irb:11:in `<main>'
irb(main):009:0> puts response.to_hash
NoMethodError: undefined method `to_hash' for nil:NilClass
	from (irb):9
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/bin.real/irb:11:in `<main>'
irb(main):010:0> puts response.body
NoMethodError: undefined method `body' for nil:NilClass
	from (irb):10
	from /Users/demo/Documents/code/backend-for-frontend/node_modules/@pact-foundation/pact-node/standalone/darwin-1.70.2/lib/ruby/bin.real/irb:11:in `<main>'
irb(main):011:0> exit

This means something is wrong with SSL_CERT_FILE. I have used respective Root CA certificate from QuoVadis used by our Pact Broker server.

Did you actually use a $ at the start of SSL_CERT_FILE or was that just a typo in the comment?

Can you try without it? export SSL_CERT_FILE=~/<directorypath>/QuoVadis_Root_CA.crt

Oh! That's just a typo. A quick editing problem!

Currently we are downloading pact json file explicitly and verify against it. But to upload verification results we must have to use pactbrokerurl in options of pact verifier.

By any chance is there an API to publish the results just like an API to publish pacts?

Yes, you can publish results https://github.com/pact-foundation/pact-js#publishing-verification-results-to-a-pact-broker

But I think you're saying that you can only publish results if you set the pactBrokerUrl in the config. That shouldn't be the case. You should be able to publish results for any pact that is retrieved from a broker, no matter if you use a direct URL or tell the broker to fetch them dynamically using the provider name. If that is not the case (some other custom JS validation is stopping you), please raise an issue.

Regarding your original issue, which root cert are you using? Can you paste the contents here?

May be i was not able to explain well.. For publishing results i must have to fetch pact contract by either using pactUrls or combination of pactBrokerUrl and providername. Since we ran into SSL issue we try to download pact file with external node js library without using @pact-foundation package. Save downloaded file locally and run provider verifier against local file using below option.

pactUrls: [path.resolve(
         process.cwd(),
         `${pactDownloadPath}/pacts.json`,
       )],

This means @pact-foundation library is unaware of pact broker.

I see there is an API to publish pacts but not pact results. If there is a way to publish results also via API we can skip SSL validation.

Regarding original issue... here is content of ROOT_CA certificate

-----BEGIN CERTIFICATE-----
MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----

Try following these instructions to get the certificate bundle pact-foundation/pact-js#203 (comment)

Pooh!! Tried following the way mentioned in above article. But no luck. I tried to set it in NodeJS app and also in interactive ruby shell.

@nikunjtilva have you seen this issue: pact-foundation/pact-js#203 (comment)

It is documented there how to achieve this.

yes @mefellows I followed same steps to generate ca_bundle.crt. Also in firefox you can directly export chained certificate.

@bethesque I created the certificate and also export the SSL_CERT_FILE to the cert file.
I'm sure that the certificate is correct.
But it still doesn't use the certificate that I export.
Does it related to the file that located node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/bin/ruby_environment
#!/bin/bash ROOT=dirname "$0" ROOT=cd "$ROOT/.." && pwd`

echo ORIG_LD_LIBRARY_PATH="$LD_LIBRARY_PATH"
echo ORIG_SSL_CERT_DIR="$SSL_CERT_DIR"
echo ORIG_SSL_CERT_FILE="$SSL_CERT_FILE"
echo ORIG_RUBYOPT="$RUBYOPT"
echo ORIG_RUBYLIB="$RUBYLIB"

echo LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$ROOT/lib"
echo SSL_CERT_FILE="$ROOT/lib/ca-bundle.crt"
echo RUBYOPT="-r$ROOT/lib/restore_environment"
for DIR in "$ROOT"/lib/ruby/gems//deplibs//*; do
echo LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$DIR"
done
echo GEM_HOME="$ROOT/lib/ruby/gems/2.2.0"
echo GEM_PATH="$ROOT/lib/ruby/gems/2.2.0"
echo RUBYLIB="$ROOT/lib/ruby/site_ruby/2.2.0:$ROOT/lib/ruby/site_ruby/2.2.0/x86_64-linux:$ROOT/lib/ruby/site_ruby:$ROOT/lib/ruby/vendor_ruby/2.2.0:$ROOT/lib/ruby/vendor_ruby/2.2.0/x86_64-linux:$ROOT/lib/ruby/vendor_ruby:$ROOT/lib/ruby/2.2.0:$ROOT/lib/ruby/2.2.0/x86_64-linux"

echo export ORIG_LD_LIBRARY_PATH
echo export ORIG_SSL_CERT_DIR
echo export ORIG_SSL_CERT_FILE
echo export ORIG_RUBYOPT
echo export ORIG_RUBYLIB

echo export LD_LIBRARY_PATH
echo unset SSL_CERT_DIR
echo export SSL_CERT_FILE
echo export RUBYOPT
echo export GEM_HOME
echo export GEM_PATH
echo export RUBYLIB`

The output is like below when I run the environment file
ORIG_LD_LIBRARY_PATH=""
ORIG_SSL_CERT_DIR="/app/cert/"
ORIG_SSL_CERT_FILE="/app/cert/combine.crt"
ORIG_RUBYOPT=""
ORIG_RUBYLIB=""
LD_LIBRARY_PATH=":/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib"
SSL_CERT_FILE="/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ca-bundle.crt"
RUBYOPT="-r/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/restore_environment"
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/gems//deplibs//*"
GEM_HOME=/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/gems/2.2.0
GEM_PATH=/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/gems/2.2.0
RUBYLIB="/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/site_ruby/2.2.0:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/site_ruby/2.2.0/x86_64-linux:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/site_ruby:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/vendor_ruby/2.2.0:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/vendor_ruby/2.2.0/x86_64-linux:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/vendor_ruby:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/2.2.0:/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ruby/2.2.0/x86_64-linux"
export ORIG_LD_LIBRARY_PATH
export ORIG_SSL_CERT_DIR
export ORIG_SSL_CERT_FILE
export ORIG_RUBYOPT
export ORIG_RUBYLIB
export LD_LIBRARY_PATH
unset SSL_CERT_DIR
export SSL_CERT_FILE
export RUBYOPT
export GEM_HOME
export GEM_PATH
export RUBYLIB

on line 12 I can see the SSL_CERT_FILE is forced to use the ca-bundle.crt
If I add the line
echo SSL_CERT_FILE="$ORIG_SSL_CERT_FILE"
Then it's working without ssl error.

It seems like the SSL_CERT_FILE is hardcode to use the ca-bundle.crt file.
Could you check it it is something wrong the the environment file?
Please correct me if I am wrong.

This line restores the original SSL_CERT_FILE once the ruby process has started https://github.com/pact-foundation/pact-ruby-standalone/blob/master/packaging/pact-provider-verifier.rb#L14

Can you find and add some debugging to that file to see what it's doing?

Hi @bethesque ,

I add debuging in the file node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/app/pact-provider-verifier.rb
`
print "before if check\n"
print "SSL_CERT_FILE is \n"
print ENV['SSL_CERT_FILE']
print "\n"
print "ORIG_SSL_CERT_FILE is \n"
print ENV['ORIG_SSL_CERT_FILE']
print "\n"

if ENV['ORIG_SSL_CERT_FILE'] && ENV['ORIG_SSL_CERT_FILE'] != ''
ENV['SSL_CERT_FILE'] = ENV['ORIG_SSL_CERT_FILE']
end

print "after if check\n"
print "SSL_CERT_FILE is \n"
print ENV['SSL_CERT_FILE']
print "\n"
print "ORIG_SSL_CERT_FILE is \n"
print ENV['ORIG_SSL_CERT_FILE']
print "\n"

require 'pact/provider_verifier/cli/verify'
`
The debug output as below:
**
before if check
SSL_CERT_FILE is
/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ca-bundle.crt
ORIG_SSL_CERT_FILE is
/app/cert/combine.crt
after if check
SSL_CERT_FILE is
/app/cert/combine.crt
ORIG_SSL_CERT_FILE is
/app/cert/combine.crt
**

It looks like the correct value is being set. Is there anything in the cert dir that is conflicting with it?

@bethesque ,

I added the cert dir debugging.

print "before if check\n"
print "SSL_CERT_DIR is \n"
print ENV['SSL_CERT_DIR']
print "\n"
print "ORIG_SSL_CERT_DIR is \n"
print ENV['ORIG_SSL_CERT_DIR']
print "\n"

if ENV['ORIG_SSL_CERT_DIR'] && ENV['ORIG_SSL_CERT_DIR'] != ''
ENV['SSL_CERT_DIR'] = ENV['ORIG_SSL_CERT_DIR']
end
print "after if check\n"
print "SSL_CERT_DIR is \n"
print ENV['SSL_CERT_DIR']
print "\n"
print "ORIG_SSL_CERT_DIR is \n"
print ENV['ORIG_SSL_CERT_DIR']
print "\n"

print "before if check\n"
print "SSL_CERT_FILE is \n"
print ENV['SSL_CERT_FILE']
print "\n"
print "ORIG_SSL_CERT_FILE is \n"
print ENV['ORIG_SSL_CERT_FILE']
print "\n"
if ENV['ORIG_SSL_CERT_FILE'] && ENV['ORIG_SSL_CERT_FILE'] != ''
ENV['SSL_CERT_FILE'] = ENV['ORIG_SSL_CERT_FILE']
end

print "after if check\n"
print "SSL_CERT_FILE is \n"
print ENV['SSL_CERT_FILE']
print "\n"
print "ORIG_SSL_CERT_FILE is \n"
print ENV['ORIG_SSL_CERT_FILE']
print "\n"

Output:
after if check
SSL_CERT_DIR is

ORIG_SSL_CERT_DIR is

after if check
SSL_CERT_DIR is

ORIG_SSL_CERT_DIR is

before if check
SSL_CERT_FILE is
/app/node_modules/@pact-foundation/pact-node/standalone/linux-x64-1.70.2/lib/ruby/lib/ca-bundle.crt
ORIG_SSL_CERT_FILE is
/app/cert/combine.crt
after if check
SSL_CERT_FILE is
/app/cert/combine.crt
ORIG_SSL_CERT_FILE is
/app/cert/combine.crt

@nikunjtilva given that this exercise failed, I am lead to the conclusion that there still must be an issue with the cert file. That is the simplest HTTP request that can be made with the SSL certificate set. If that's not working, I can't see what else the problem could be.

There is an API for publishing results. https://github.com/pact-foundation/pact_broker/blob/master/lib/pact_broker/doc/views/pact/publish-verification-results.markdown

Thanks @bethesque and @justindongnz for your support..We will be using API endpoint for publishing results...

We have used workaround to post our results through API call. It worked for us.