-
Notifications
You must be signed in to change notification settings - Fork 169
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
Conversation
fbea17c
to
3390d58
Compare
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]>
3390d58
to
d36cc01
Compare
It's now ready for a review. :) @ronwsmith maybe? |
There was a problem hiding this 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in Fortunately
There was a problem hiding this comment.
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']) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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..)
lib/billy/proxy_connection.rb
Outdated
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
lib/billy/ssl/authority.rb
Outdated
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the comment.
lib/billy/ssl/certificate.rb
Outdated
|
||
# Give back an appropriate date for the beginning of this | ||
# certificate life. We give back now 2 days ago. | ||
def valid_from |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me.
lib/billy/ssl/certificate.rb
Outdated
# 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) |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
lib/billy/ssl/certificate.rb
Outdated
# 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) |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
lib/billy/ssl/certificate_chain.rb
Outdated
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
concatenate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep.
spec/support/test_server.rb
Outdated
ca = Billy.certificate_authority.cert | ||
cert = Billy::Certificate.new(domain) | ||
chain = Billy::CertificateChain.new(domain, cert.cert, ca) | ||
[chain.file, cert.key_file] |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
Signed-off-by: Hermann Mayer <[email protected]>
No, I don't think so for two reasons.
With this in mind, any user of puffing billy disabled the SSL check (like it was done on our Evidence:
|
@ronwsmith Ping :) |
@Jack12816 Looks great now. Awesome contribution, thanks! |
Released in v0.11.0 |
- 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 ourcustom 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)