-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Configurable mtls #297
Configurable mtls #297
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.
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)
cmd/headscale/cli/utils.go
Outdated
@@ -40,6 +40,7 @@ func LoadConfig(path string) error { | |||
|
|||
viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache") | |||
viper.SetDefault("tls_letsencrypt_challenge_type", "HTTP-01") | |||
viper.SetDefault("tls_client_auth_mode", "disabled") |
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 default is different than the current setting, how come?
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.
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.
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'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?
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.
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?
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.
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.
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.
Agreed! Sorry for the confusion there.
Should have things buttoned up tonight/early tomorrow.
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.
Alrighty, the latest commit preserves the original default.
docs/tls.md
Outdated
the following values to the `tls_client_auth_mode` setting in the configuration | ||
file. | ||
|
||
| Value | Behavior | |
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 file needs to be ran through prettier
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.
It has now been ran through prettier. My bad on the oversight.
app.go
Outdated
@@ -644,12 +645,34 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) { | |||
if !strings.HasPrefix(h.cfg.ServerURL, "https://") { | |||
log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") | |||
} | |||
|
|||
var clientAuthMode tls.ClientAuthType | |||
if h.cfg.TLSClientAuthMode == "disabled" { |
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 should be a switch
statement, and the alternatives should be Constants.
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. I'm new to Go, so thank you for this critique. Much less typing in a switch statement.
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.
In the latest commit, I moved this logic to a new function to make testing a bit simpler and implemented a switch statement.
…to configurable-mtls
…to configurable-mtls
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.
Looking like its get there, I think this might be a way to simplify the flow a bit and remove some redundant checks.
app.go
Outdated
TLSKeyPath string | ||
TLSCertPath string | ||
TLSKeyPath string | ||
TLSClientAuthMode string |
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 would argue that you can just use the
tls.ClientAuthType
type here.
app.go
Outdated
clientAuthMode, err := h.GetClientAuthMode() | ||
if err != nil { | ||
return nil, 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.
- Then remove this ( or cut it for later )
app.go
Outdated
tlsConfig := &tls.Config{ | ||
ClientAuth: tls.RequireAnyClientCert, | ||
ClientAuth: clientAuthMode, |
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.
- Then do this:
ClientAuth: clientAuthMode, | |
ClientAuth: h.cfg.TLSClientAuthMode, |
cmd/headscale/cli/utils.go
Outdated
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." | ||
} |
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.
- Then instead here call the
h.GetClientAuthMode
function and check the error instead of having thatif
.
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"), |
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.
- Then just set it here if there was no error in 4.:
TLSClientAuthMode: viper.GetString("tls_client_auth_mode"), | |
TLSClientAuthMode: clientAuthMode, |
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 |
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 should be relaxed to reflect the default.
tls_client_auth_mode: disabled | |
tls_client_auth_mode: relaxed |
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 |
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.
Duplicate wording? by by applying one of
@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. |
Need another run of |
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 thedisabled
value by default and can be configured to require any certificate (relaxed
) or require and verify (enforced
) a client certificate.Related Issues