-
Notifications
You must be signed in to change notification settings - Fork 71
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
Add MITM support to smokescreen #225
Conversation
Pull Request Test Coverage Report for Build 10911714189Details
💛 - Coveralls |
7520500
to
f4ef674
Compare
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.
Left a first round of comments!
```shellsession | ||
$ go test ./... | ||
``` | ||
See [Development.md](Development.md) | ||
|
||
# Contributors |
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.
You should add yourself!
pkg/smokescreen/acl/v1/acl.go
Outdated
// if the host matches any of the rule's allowed domains with MITM config, allow | ||
for _, dg := range rule.DomainMitmGlobs { | ||
if HostMatchesGlob(host, dg.Domain) { | ||
d.Result, d.Reason = Allow, "host matched allowed domain in rule" |
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 update reason to reference that this matched an allowed domain in the MITM globs?
Also, do we need this check? Why would a proxy request need to be MITMd but not be in the original ACL rule?
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.
On the check, are you talking about HostMatchesGlob
? host
is the current host being checked for a decision. So this loop is trying to find if there's a MITM rules corresponding to the current host
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 believe I meant why are we updating the decision reason if we're only trying to determine if the domain needs to be MITMd. Shouldn't the ACL checking behavior be separate?
pkg/smokescreen/smokescreen.go
Outdated
if sctx.proxyType == connectProxy { | ||
if sctx.proxyType == connectProxy || pctx.ConnectAction == goproxy.ConnectMitm { | ||
// If we have a MITM and option is enabled, we can add detailed Request log fields | ||
if pctx.ConnectAction == goproxy.ConnectMitm && sctx.Decision.MitmConfig != nil && sctx.Decision.MitmConfig.DetailedHttpLogs { |
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.
Does the MitmConfig
reference need to live with the decision? Could we not move this directly into the context?
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.
Decision.MitmConfig
comes from ACLDecision.MitmConfig because the config is in the rule which comes from rule.DomainMitmGlobs
Al these happen in the decision functions (checkIfRequestShouldBeProxied
, checkACLsForRequest
, config.EgressACL.Decide
and none of these have any context.
Do you think we should pass pctx
down to all these methods to set MitmConfig
there?
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.
Mmmm no I don't feel too strongly about this, and it's fine leaving if the context isn't already available where we need it (I thought it was).
85d2bc1
to
85fea80
Compare
Thanks for this great review! I've actioned all your comments except I have re-tested ALL manual tests listed in the description of this PR. |
85fea80
to
8fcae44
Compare
We talked on Slack and agreed it would be best for |
6c30765
to
b319203
Compare
b319203
to
c905517
Compare
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.
Thanks for addressing all of the feedback! Left one comment about a function name but otherwise LGTM
return fmt.Errorf("could not load mitmCa: %v", err) | ||
return fmt.Errorf("mitm_ca_key_file error tls.LoadX509KeyPair: %w", err) | ||
} | ||
// set the leaf certificat to reduce per-handshake processing |
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.
nit: certificate?
pkg/smokescreen/acl/v1/acl.go
Outdated
// if the host matches any of the rule's allowed domains with MITM config, allow | ||
for _, dg := range rule.DomainMitmGlobs { | ||
if HostMatchesGlob(host, dg.Domain) { | ||
d.Result, d.Reason = Allow, "host matched allowed domain in rule" |
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 believe I meant why are we updating the decision reason if we're only trying to determine if the domain needs to be MITMd. Shouldn't the ACL checking behavior be separate?
pkg/smokescreen/smokescreen.go
Outdated
if sctx.proxyType == connectProxy { | ||
if sctx.proxyType == connectProxy || pctx.ConnectAction == goproxy.ConnectMitm { | ||
// If we have a MITM and option is enabled, we can add detailed Request log fields | ||
if pctx.ConnectAction == goproxy.ConnectMitm && sctx.Decision.MitmConfig != nil && sctx.Decision.MitmConfig.DetailedHttpLogs { |
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.
Mmmm no I don't feel too strongly about this, and it's fine leaving if the context isn't already available where we need it (I thought it was).
pkg/smokescreen/acl/v1/acl.go
Outdated
@@ -83,7 +83,7 @@ func (acl *ACL) Add(svc string, r Rule) error { | |||
return err | |||
} | |||
|
|||
err = acl.ValidateRuleDomainsGlobs(svc, r) | |||
err = acl.ValidateRuleDomainsGlobsAndMitm(svc, r) |
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.
Should this actually just be ValidateRule
since we're passing in the whole rule 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.
Good idea!
b936c70
to
7311611
Compare
7311611
to
4cf6e0b
Compare
Description
This PR adds MITM support:
I've also:
Development.md
which now includes instructions on how to run locally for multiple scenarios:.vscode/launch.json
to allow debugging easily in Vscodeconfig *Config
argument fromlogProxy
as it was un-usedextractContextLogFields
are added to accommodate the MITM codepctx.RoundTripper
which is normally used forhttp
proxy request is now also used by the MITM outbound request. By default it pools requests and keep them idle for a short period of time to potentially re-use. (even whenResponse.Body.Close()
is run) This doesn't work well withInstrumentedConn
that logsCANONICAL-PROXY-CN-CLOSE
once the connection is closed.There are multiple ways to go around this but I chose to run
proxy.Tr.CloseIdleConnections
as thiscloses any connections which were previously connected from previous requests but are now sitting idle in a "keep-alive" state
. This proxy is not primarily intended to support browser traffic and the performance gain of keeping this connection is negligible for our use-case.Alternatives considered:
req.Header.Set("Connection", "close")
would work but the header is wiped in goproxy which calls removeProxyHeaders which deletes the Connection header.Testing
I have added automated tests for ACL and the whole MITM flow.
HTTP Proxy (happy path) ✅
See set-up from Development.md HTTP Proxy)
HTTP CONNECT Proxy (happy path) ✅
See set-up from Development.md HTTP CONNECT Proxy)
HTTP CONNECT Proxy over TLS (happy path) ✅
See set-up from Development.md HTTP CONNECT Proxy over TLS)
MITM (Man in the middle) Proxy (happy path) ✅
See set-up from Development.md MITM (Man in the middle) Proxy)
Accept-Language: el
was correctly sentNotice the
mitm_req_headers
,mitm_req_method
andmitm_req_url
fieldsMITM (Man in the middle) Proxy over TLS (happy path) ✅
See set-up from Development.md MITM (Man in the middle) Proxy over TLS)
Accept-Language: el
was correctly sent (weather is in Greek)Notice the
mitm_req_headers
,mitm_req_method
,mitm_req_url
(MITM),inbound_remote_x509_cn
andinbound_remote_x509_ou
(TLS) fields.MITM config not configured with ACL configured ✅
Miss-configuration fails gracefully