-
Notifications
You must be signed in to change notification settings - Fork 14
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 working --serveTLS #272
Conversation
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 have some serious doubts about these changes.
I might have missed something (it's late and it was a long day), but if I understand correctly, we're introducing (or revamping) insecure defaults, and the solution to handling reverse proxies is removing a MitM protection, relying entirely on certificate authorities to verify remote node certificates.
If my understanding is correct, this would (at the very least) break a few of our own internal (experimental) deployments, which aren't always exposed to the internet. It would also make it harder to catch configuration mistakes.
I'm happy to have a quick (offline) chat about this if it helps move the functionality along.
Name: "serveTLS", | ||
Usage: "enables TLS on the gRPC server and creates a self-signed certificate if necessary", | ||
Required: false, | ||
Value: false, |
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.
effectively, this makes it insecure by default. I'm not sure this is a great idea.
@@ -77,7 +77,7 @@ func (m miniController) SetCommands(builder node.Builder) { | |||
}, | |||
cli.StringFlag{ | |||
Name: "certKey", | |||
Usage: "provides the certificate private key path", |
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.
If I understand correctly, it becomes impossible to be certificate-aware (e.g., if nginx acts as reverse proxy to do TLS-termination) without also serving TLS. I'm not sure this is desirable.
if certChain != "" { | ||
fmt.Println("certChain:", certChain, "certKey:", certKey) | ||
cert, err := tls.LoadX509KeyPair(certChain, certKey) |
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.
why would we only load this if certKey isn't set ? 🤔
"google.golang.org/grpc/credentials/insecure" | ||
"strings" |
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're using goimports formatting, which isn't respected here.
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'm never sure what Goland does, sorry. I'll try to convince it to use goimports
. Do you have a common formatter you're using?
fmt.Fprintf(req.Out, "--token %s%s\n", | ||
token, certHash) |
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.
If I understand correctly, the certHash
here may be empty.
This gives no way for the joining node to authenticate the node it is connecting to.
That does not seem desirable.
if !tmpl.serveTLS && public.Scheme != "https" { | ||
dela.Logger.Warn().Msg("⚠️ running in insecure mode and no TLS endpoint, you should not " + | ||
"publicly expose the node's socket without TLS") |
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.
Technically grpcs
could also be a valid scheme, I believe.
Furthermore, a misconfiguration of the public address could hide this error, shouldn't we be (slightly) louder and let the sysadmin figure it out if the warning applies to them or not ?
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.
remove check for public.Scheme
and reword warning for students
serveTLS bool | ||
} | ||
|
||
func (m *minoTemplate) makeCertificate() error { |
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 know this somewhat existed before, but is it a good idea to generate a certificate when a misconfiguration could be the reason the certificate wasn't passed ? 🤔
I'd consider making it an option, e.g. --generate-certificate
, rather than a fallback that might entirely hide that there was a problem somewhere, and make the sysadmin's task harder.
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 only moved this from the overlay
to minoTemplate
to simplify newOverlay
.
// If the remote is accessible via port 443, we suppose it is TLS terminated and signed by a | ||
// CA available on the system. |
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.
this assumption might easily break in our own deployments.
that was the whole point of passing the certificate hash along with the token when joining nodes 😕
if addr.Port() == "" { | ||
switch addr.Scheme { | ||
case "http": | ||
host += ":80" | ||
case "https": | ||
host += ":443" | ||
default: | ||
return xerrors.Errorf("address doesn't contain a port and uses "+ | ||
"an unknown scheme: %s", addr.Scheme) | ||
} | ||
} |
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.
why is this needed ? Effectively the code is adding default ports for those schemes, that's normally not needed 🤔
if tmpl.cert != nil && tmpl.useTLS { | ||
chain := bytes.Buffer{} | ||
for _, c := range tmpl.cert.Certificate { | ||
chain.Write(c) | ||
} | ||
|
||
err := o.certs.Store(o.myAddr, chain.Bytes()) | ||
if err != nil { | ||
return nil, xerrors.Errorf("failed to store cert: %v", err) | ||
} | ||
} | ||
|
||
if tmpl.useTLS { | ||
cert, err := o.certs.Load(o.myAddr) | ||
if err != nil { | ||
return nil, xerrors.Errorf("while loading cert: %v", err) | ||
} | ||
|
||
if cert == nil { | ||
err = o.makeCertificate() | ||
if err != nil { | ||
return nil, xerrors.Errorf("certificate failed: %v", err) | ||
} | ||
} |
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 call on moving all this stuff ahead of the overlay creation, it feels cleaner !
Use casesCurrently I see two use-cases, and a third for paranoid people:
For 1. I think we should trust the reverse proxy to do its thing. Of course there are attacks on CAs with rogue certificates where you can have a mitm. If you care about this attack, go for 3. But using TLS-in-TLS seems overly complicated, and IIRC, was one of the problems with the last try to get it to run. For 2., no TLS is needed. It's development, it should be fast and w/o fuss. Every argument saved in development mode is a happy developer. For 3., use self-signed certificates and serve TLS with dela. Or set up a reverse proxy with terminating TLS and trust it, but then the reverse proxy could be colluding with your adversary... So with this PR, 1. and 2. is the default. No TLS served by default, for deployment we use reverse proxies. If you're paranoid and want to be really sure nobody is interfering with your communication, go for 3. Serving TLS in delaPerhaps the arguments could be reduced to Do we need
|
After discussion: By default:
Or one of:
|
Instead of using
--noTLS
to indicate not to use TLS, changed the flag to--serveTLS
. Now:--serveTLS
is enabled--serveTLS
is enabled w/o a certificate, a self-signed certificate is createdhttps
, a TLS connection is established, and the certificate is verified using public CAs used in the system