Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented a dynamic generation of SSL request certificates #198

Merged
merged 2 commits into from
Nov 10, 2017

Conversation

Jack12816
Copy link
Contributor

@Jack12816 Jack12816 commented Oct 15, 2017

- What is it good for?

This update ships a fully secure proxy handling of HTTPS requests with
the runtime generation of a certificate authority and the generation of
the request certificates based on the requested domains. A user can then
take care of the importing of the certificate authority. From the
perspective of puffing billy we do not have to handle outdated
certificates or static files anymore in the future with the help of this
feature. This is the same case for users of older puffing billy
versions as well.

The full feature mimics the mighty mitmproxy functionality which results
in secure proxying on modern browsers.

- What I did

I added three new classes, (Authority, Certificate and CertificateChain) with
unit tests for each of them. All static certificate files were removed
(mitm.{crt,key}, test-server.{crt,key}). The ProxyConnection class now makes
use of the new certificate generation. The spec test server uses now
dynamically generated certificates (same like ProxyConnection does it) and
proxy_spec/@https is configured to verify the SSL connection. (By adding our
custom certificate authority file to faraday) And last but not least I
documented the usage of the certificate authority with a Google Chrome Headless
example.

- A picture of a cute animal (not mandatory but encouraged)

images 1

@Jack12816 Jack12816 force-pushed the handle-ssl-better branch 2 times, most recently from fbea17c to 3390d58 Compare October 22, 2017 10:23
This update ships a fully secure proxy handling of HTTPS requests with
the runtime generation of a certificate authority and the generation of
the request certificates based on the requested domains. A user can then
take care of the importing of the certificate authority. From the
perspective of puffing billy we do not have to handle outdated
certificates or static files anymore in the future with the help of this
feature. This is the same case for users of older puffing billy
versions as well.

The full feature mimics the mighty mitmproxy functionality which results
in secure proxying on modern browsers.

Signed-off-by: Hermann Mayer <[email protected]>
@Jack12816
Copy link
Contributor Author

It's now ready for a review. :)

@ronwsmith maybe?

Copy link
Collaborator

@ronwsmith ronwsmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty great contribution! Mostly minor comments to fix up. Will this break any existing test suites?

README.md Outdated
SCRIPT
```

Mind the reset of the `HOME` environment variable. Furtunately Chrome takes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in Fortunately

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.

ENV['HOME'] = "#{Dir.tmpdir}/chrome-home-#{Time.now.to_i}"

# Clear and recreate the Chrome home directory.
FileUtils.rm_rf(ENV['HOME'])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Time.now above should pretty much guarantee this directory will never exist right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do the Time.now trick, by then we have a mess while cleaning up. I think of a new configured directory like cache_dir and certs_dir. I also do not like the manipulation of the HOME environment variable. But it's just for child processes. It would be better to have a handling like written below.

FileUtils.mkdir_p(ENV['HOME'])

# Setup a new pki certificate database for Chrome
system <<~SCRIPT
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we create a rake task to do all this?

Copy link
Contributor Author

@Jack12816 Jack12816 Oct 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good, yeah. This is not possible right now due to the fact that the rake task does not have access to a puffing billy module. And when it starts it, it would be meaningless, because the user test suite will get a new instance and the authority changes per run. So this would not work out.

But we could package this as a helper on the Billy module. Something which can be used like this:

RSpec.configure do |config|
  config.before :suite do
    Billy:.Browsers::Chrome.setup_user_directory
  end
end

We could also include this at the Watir and Capybara driver level. But I like to keep the
Billy:.Browsers::Chrome.setup_user_directory option open for users. (It's also for me, because I configure my drivers by myself, Chrome Headless, you know..)

private_key_file: File.expand_path('../mitm.key', __FILE__),
cert_chain_file: File.expand_path('../mitm.crt', __FILE__)
)
start_tls(private_key_file: @key_file, cert_chain_file: @chain_file)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should avoid the use of instance variables. Perhaps the new helper method can return a hash and be used directly as a param in this method call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

end

# Write out the private key to file (PEM format) and give back the
# file path. This will produce a temporary file which will be remove
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment.


# Give back an appropriate date for the beginning of this
# certificate life. We give back now 2 days ago.
def valid_from
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe these three helper methods move to a proper helper so they aren't copy/pasted in both classes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me.

# Give back an appropriate date for the beginning of this
# certificate life. We give back now 2 days ago.
def valid_from
Time.now - (2 * 24 * 60 * 60)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just use 2.days.ago?

Copy link
Contributor Author

@Jack12816 Jack12816 Oct 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No ActiveSupport at the gemspec. ;) NoMethodError: undefined method +days+ for 2:Fixnum

# Give back an appropriate date for the end of this certificate life.
# We give back now in 2 days.
def valid_to
Time.now + (2 * 24 * 60 * 60)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just use 2.days.from_now?

Copy link
Contributor Author

@Jack12816 Jack12816 Oct 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No ActiveSupport at the gemspec. ;) NoMethodError: undefined method +days+ for 2:Fixnum


module Billy
# This class is dedicated to the generation of a certificate chain in the
# PEM format. Fortunately we just have to concatinate the given certificates
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

concatenate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.

ca = Billy.certificate_authority.cert
cert = Billy::Certificate.new(domain)
chain = Billy::CertificateChain.new(domain, cert.cert, ca)
[chain.file, cert.key_file]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just return a hash and use directly here: http_server.ssl_options = certificate_chain(domain)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

@Jack12816
Copy link
Contributor Author

Will this break any existing test suites?

No, I don't think so for two reasons.

  1. The original mitm.crt is expired since Oct 2 06:42:11 2013 GMT.
    So it was not valid from there on and if a browser on a suite was checking this
    it was hardly complaining all the time.
  2. The original mitm.crt was never valid on the subject of the domain. In the past
    you could allow this certificate (at least until 2013) and all requests which were
    signed by it. But modern browsers like Chrome 59+ and Firefox 54+ will not allow
    any requests which are signed this certificate.

With this in mind, any user of puffing billy disabled the SSL check (like it was done on our
test suite) or was simply not using SSL with modern browsers. (Phantomjs doesn't count)
So no. No test suite will break from my point of view.

Evidence:

$  openssl x509 -text -noout -in mitm.crt

    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C = US, ST = CA, L = Mountain View, O = Internet Widgits Pty Ltd, CN = Puffing Billy, emailAddress = [email protected]
        Validity
            Not Before: Oct  2 06:42:11 2012 GMT
            Not After : Oct  2 06:42:11 2013 GMT

@Jack12816
Copy link
Contributor Author

@ronwsmith Ping :)

@ronwsmith
Copy link
Collaborator

@Jack12816 Looks great now. Awesome contribution, thanks!

@ronwsmith ronwsmith merged commit 83e010a into oesmith:master Nov 10, 2017
@ronwsmith
Copy link
Collaborator

Released in v0.11.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants