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

Add working --serveTLS #272

Closed
wants to merge 3 commits into from
Closed

Add working --serveTLS #272

wants to merge 3 commits into from

Conversation

ineiti
Copy link
Member

@ineiti ineiti commented Sep 26, 2023

Instead of using --noTLS to indicate not to use TLS, changed the flag to --serveTLS. Now:

  • if a certificate and a chain is given, --serveTLS is enabled
  • if --serveTLS is enabled w/o a certificate, a self-signed certificate is created
  • if the remote point starts with https, a TLS connection is established, and the certificate is verified using public CAs used in the system

@ineiti ineiti self-assigned this Sep 26, 2023
Copy link
Contributor

@pierluca pierluca left a 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.

Comment on lines +89 to 92
Name: "serveTLS",
Usage: "enables TLS on the gRPC server and creates a self-signed certificate if necessary",
Required: false,
Value: false,
Copy link
Contributor

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",
Copy link
Contributor

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.

Comment on lines +250 to +252
if certChain != "" {
fmt.Println("certChain:", certChain, "certKey:", certKey)
cert, err := tls.LoadX509KeyPair(certChain, certKey)
Copy link
Contributor

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 ? 🤔

Comment on lines +16 to +17
"google.golang.org/grpc/credentials/insecure"
"strings"
Copy link
Contributor

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.

Copy link
Member Author

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?

Comment on lines +123 to +124
fmt.Fprintf(req.Out, "--token %s%s\n",
token, certHash)
Copy link
Contributor

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.

Comment on lines +277 to +279
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")
Copy link
Contributor

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 ?

Copy link
Member Author

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 {
Copy link
Contributor

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.

Copy link
Member Author

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.

Comment on lines +691 to +692
// If the remote is accessible via port 443, we suppose it is TLS terminated and signed by a
// CA available on the system.
Copy link
Contributor

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 😕

Comment on lines +550 to +560
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)
}
}
Copy link
Contributor

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 🤔

Comment on lines -485 to -508
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)
}
}
Copy link
Contributor

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 !

@ineiti
Copy link
Member Author

ineiti commented Sep 27, 2023

Use cases

Currently I see two use-cases, and a third for paranoid people:

  1. Use dela behind a reverse proxy with TLS termination
  2. Use dela in development environment
  3. Use dela standalone with self-signed certificates

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 dela

Perhaps the arguments could be reduced to --certKey and --certChain, which must either both be given, or none. If they are present, TLS is served. For convenience, we could add a --makeCertificate which writes the keys and the chain in the files given by the other two arguments. But if you're paranoid, perhaps you want to use another tool to create your certificate.

Do we need --certKey AND --certChain?

I'm always lost with certificates and never know which contain the private key and which contain the public key. But I think that --certChain doesn't make sense: when you Join to a TLS serving dela, you have to give the hash of its certificate anyway. So why should you add it? Or is the --certChain for the certificate of the --certKey?

History

In cothority we also started serving TLS in the conodes for the websockets, but this turned out to be complicated with renewing and reloading of certificates. It's still there, but mostly unused.

We kept self-signed certificates only for the node-to-node communication. Which is not ideal, because you need a dedicated port and cannot use a reverse proxy for that. Or at least I never tried.

@ineiti
Copy link
Member Author

ineiti commented Sep 27, 2023

After discussion:

By default:

  • makeCertificate and listen on TLS

Or one of:

  • give --certKey
  • give both --certKey and --certChain
  • --noTLS to remove listening on TLS

@ineiti ineiti mentioned this pull request Sep 28, 2023
@ineiti
Copy link
Member Author

ineiti commented Mar 27, 2024

Done in #280 and #282

@ineiti ineiti closed this Mar 27, 2024
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