Skip to content

Configurable mtls#297

Merged
kradalby merged 21 commits intojuanfont:mainfrom
ImpostorKeanu:configurable-mtls
Feb 24, 2022
Merged

Configurable mtls#297
kradalby merged 21 commits intojuanfont:mainfrom
ImpostorKeanu:configurable-mtls

Conversation

@ImpostorKeanu
Copy link
Copy Markdown
Contributor

@ImpostorKeanu ImpostorKeanu commented Jan 29, 2022

  • read the [CONTRIBUTING guidelines](README.md#user-content-contributing
  • raised a GitHub issue or discussed it on the projects chat beforehand
  • added unit tests
  • [] added integration tests
  • updated documentation if needed
  • [] updated CHANGELOG.md

The Problem

Currently, mTLS is set to require any certificate from clients when TLS is enabled. Verification of the certificate is not performed by Headscale, and as far as I can tell, there isn't a way to configure the Tailscale client to accept TLS certificate for authentication.

Implemented Solution

This merge makes mTLS configurable by adding a configuration parameter to config.yaml. It's disabled via the disabled value by default and can be configured to require any certificate (relaxed) or require and verify (enforced) a client certificate.

Related Issues

Copy link
Copy Markdown
Collaborator

@kradalby kradalby left a comment

Choose a reason for hiding this comment

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

Could you add a test to verify that the values work as intended and a failure case to verify failure?

And add the entry to config-example.yaml with some docs (and linking to the tls doc)

Comment thread cmd/headscale/cli/utils.go Outdated

viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache")
viper.SetDefault("tls_letsencrypt_challenge_type", "HTTP-01")
viper.SetDefault("tls_client_auth_mode", "disabled")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This default is different than the current setting, how come?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Please correct me if I'm wrong, but I think Tailscale doesn't support client certificates right now.

The current default, RequireAnyClientCert, prevents connections over TLS unless any cert is provided by the client.

If I'm not overlooking something, then this means that Tailscale can't interact with Headscale when it's configured to use TLS.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If I'm not overlooking something, then this means that Tailscale can't interact with Headscale when it's configured to use TLS.

Hmm, I am not sure what this means? I am running my headscale with TLS?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

How curious! All of my browsers throw a bad ssl error unless a client certificate is supplied, then it works as expected.

I'm using the config parameters to point at cert values for letsencrypt files I already have on hand.

I believe issue #254 reflects the same outcome.

Do we have a misconfiguration somewhere or something?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, happy to make it configurable so users with this problem can at least test them, however, there are many successful users (me included) and I think think there is a reason to change the default for everyone.

My current setup has tls set in nginx, but a week ago, I used the letsencrypt built in to headscale.

So yep, let's get it in, but, we should not change the default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed! Sorry for the confusion there.

Should have things buttoned up tonight/early tomorrow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Alrighty, the latest commit preserves the original default.

Comment thread app.go
Comment thread docs/tls.md Outdated
the following values to the `tls_client_auth_mode` setting in the configuration
file.

| Value | Behavior |
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This file needs to be ran through prettier

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It has now been ran through prettier. My bad on the oversight.

Comment thread app.go Outdated
}

var clientAuthMode tls.ClientAuthType
if h.cfg.TLSClientAuthMode == "disabled" {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should be a switch statement, and the alternatives should be Constants.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call. I'm new to Go, so thank you for this critique. Much less typing in a switch statement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In the latest commit, I moved this logic to a new function to make testing a bit simpler and implemented a switch statement.

Copy link
Copy Markdown
Collaborator

@kradalby kradalby left a comment

Choose a reason for hiding this comment

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

Looking like its get there, I think this might be a way to simplify the flow a bit and remove some redundant checks.

Comment thread app.go Outdated
TLSKeyPath string
TLSCertPath string
TLSKeyPath string
TLSClientAuthMode string
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. I would argue that you can just use the tls.ClientAuthType type here.

Comment thread app.go Outdated
Comment on lines +658 to +661
clientAuthMode, err := h.GetClientAuthMode()
if err != nil {
return nil, err
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. Then remove this ( or cut it for later )

Comment thread app.go Outdated

tlsConfig := &tls.Config{
ClientAuth: tls.RequireAnyClientCert,
ClientAuth: clientAuthMode,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. Then do this:
Suggested change
ClientAuth: clientAuthMode,
ClientAuth: h.cfg.TLSClientAuthMode,

Comment thread cmd/headscale/cli/utils.go Outdated
Comment on lines +90 to +93
clientAuthMode := viper.GetString("tls_client_auth_mode")
if clientAuthMode != "disabled" && clientAuthMode != "relaxed" && clientAuthMode != "enforced" {
errorText += "Invalid tls_client_auth_mode supplied. Accepted values: disabled, relaxed, enforced."
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. Then instead here call the h.GetClientAuthMode function and check the error instead of having that if.

Comment thread cmd/headscale/cli/utils.go Outdated
TLSKeyPath: absPath(viper.GetString("tls_key_path")),
TLSCertPath: absPath(viper.GetString("tls_cert_path")),
TLSKeyPath: absPath(viper.GetString("tls_key_path")),
TLSClientAuthMode: viper.GetString("tls_client_auth_mode"),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. Then just set it here if there was no error in 4.:
Suggested change
TLSClientAuthMode: viper.GetString("tls_client_auth_mode"),
TLSClientAuthMode: clientAuthMode,

Comment thread config-example.yaml Outdated
# - disabled: client authentication disabled
# - relaxed: client certificate is required but not verified
# - enforced: client certificate is required and verified
tls_client_auth_mode: disabled
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should be relaxed to reflect the default.

Suggested change
tls_client_auth_mode: disabled
tls_client_auth_mode: relaxed

Comment thread docs/tls.md Outdated
### Configuring Mutual TLS Authentication (mTLS)

mTLS is a method by which an HTTPS server authenticates clients, e.g. Tailscale,
using TLS certificates. The capability can be configured by by applying one of
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicate wording? by by applying one of

@kradalby
Copy link
Copy Markdown
Collaborator

@Arch4ngel Looks good, last few things is just make the linters happy, and add a line to the changelog under TBD.

Good job :)

@ImpostorKeanu
Copy link
Copy Markdown
Contributor Author

@Arch4ngel Looks good, last few things is just make the linters happy, and add a line to the changelog under TBD.

Good job :)

Thanks for your patience on this! Work consumed me for a few weeks.

@kradalby
Copy link
Copy Markdown
Collaborator

Need another run of prettier and then I'll approve, good work, sorry for all the nits ;)

@kradalby kradalby merged commit 5596a0a into juanfont:main Feb 24, 2022
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.

3 participants