-
Notifications
You must be signed in to change notification settings - Fork 87
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
Client TLS: Add option to require a specific DNS name in Subject Alternate Name #126
Changes from 15 commits
b3ef535
7c8fecd
a9384c4
1515dca
8d8bcbe
f8a6b39
5b49cfe
fd4ac69
0ed1d0e
7b089c7
5f08414
208cb53
3d91e48
b0e6f85
b205f7a
394edbb
06d5a6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
-----BEGIN PRIVATE KEY----- | ||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDC8CYtAwKp1uLWXLXFE | ||
Ue2Bz6PijwHZcL7jAxtlk2dbW0GlRQ+rcalHCcnExIIKAAehZANiAATlPRxDnbJb | ||
Zq9u+jh7DyEJumQZFqjIDFdFxfHtI6hwyMtlL6FIwpqn3z4uXs2wx6/NsD4XOChy | ||
j/tXXKCHS/22+51TivjGA53c9bLgc4dK/uJJNSivp0kymbtA5vgKzJE= | ||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCgZCrQEAUVqznwKRvu | ||
dwwi8wutaRaHHHWDd/IpjJopLhcdvONT7Fv57X0foCvmYFOhZANiAAR/zAKpT17i | ||
U9lmokwDicnziss91+vKhQjy2q4EAe1p7jJ9c/fPofP3Zd09pLhkAUONMu0myXjk | ||
piLE1vvL121tWg3E3F0MLjLBqiSWqSkEZjQj0YSk3NoGWX/gMgm8ZyA= | ||
-----END PRIVATE KEY----- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIByjCCAU+gAwIBAgIUYcG9p4RzCRdvUGa9BWvc6rB/wMYwCgYIKoZIzj0EAwIw | ||
EDEOMAwGA1UEAwwFdGVzdDIwIBcNMjEwODIwMTUzMjE4WhgPMjEyMTA3MjcxNTMy | ||
MThaMBAxDjAMBgNVBAMMBXRlc3QyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE5T0c | ||
Q52yW2avbvo4ew8hCbpkGRaoyAxXRcXx7SOocMjLZS+hSMKap98+Ll7NsMevzbA+ | ||
Fzgoco/7V1ygh0v9tvudU4r4xgOd3PWy4HOHSv7iSTUor6dJMpm7QOb4CsyRo2gw | ||
ZjAdBgNVHQ4EFgQUWpsZ2aWo6WEI2LiNQXoWKYr0rlkwHwYDVR0jBBgwFoAUWpsZ | ||
2aWo6WEI2LiNQXoWKYr0rlkwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUEDDAKBggr | ||
BgEFBQcDAjAKBggqhkjOPQQDAgNpADBmAjEA/Mv4OjCqVw8PzxQW4FJmZNyJB4ps | ||
xkAUBRpDy75n64ICsWKX/Mille0bo+C8d63JAjEA3IH/y1O4oyCaawNpibfcwSZK | ||
7ND9Z+WTJi50EumXUWKirmb/V59ToH5nc10x7NDX | ||
MIIB3DCCAWGgAwIBAgIUJVN8KehL1MmccvLb/mHthSMfnnswCgYIKoZIzj0EAwIw | ||
EDEOMAwGA1UEAwwFdGVzdDMwIBcNMjMwMTEwMTgxMTAwWhgPMjEyMjEyMTcxODEx | ||
MDBaMBAxDjAMBgNVBAMMBXRlc3QzMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEf8wC | ||
qU9e4lPZZqJMA4nJ84rLPdfryoUI8tquBAHtae4yfXP3z6Hz92XdPaS4ZAFDjTLt | ||
Jsl45KYixNb7y9dtbVoNxNxdDC4ywaoklqkpBGY0I9GEpNzaBll/4DIJvGcgo3ow | ||
eDAdBgNVHQ4EFgQUvyvu/TnJyRS7OGdujTbWM/W07yMwHwYDVR0jBBgwFoAUvyvu | ||
/TnJyRS7OGdujTbWM/W07yMwDwYDVR0TAQH/BAUwAwEB/zAQBgNVHREECTAHggV0 | ||
ZXN0MzATBgNVHSUEDDAKBggrBgEFBQcDAjAKBggqhkjOPQQDAgNpADBmAjEAt7HK | ||
knE2MzwZ2B2dgn1/q3ikWDiO20Hbd97jo3tmv87FcF2vMqqJpHjcldJqplfsAjEA | ||
sfAz49y6Sf6LNlNS+Fc/lbOOwcrlzC+J5GJ8OmNoQPsvvDvhzGbwFiVw1M2uMqtG | ||
-----END CERTIFICATE----- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
tls_server_config: | ||
cert_file: "server.crt" | ||
key_file: "server.key" | ||
client_auth_type: "RequireAndVerifyClientCert" | ||
client_ca_file: "client2_selfsigned.pem" | ||
client_allowed_san_regex: "bad" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
tls_server_config: | ||
cert_file: "server.crt" | ||
key_file: "server.key" | ||
client_auth_type: "RequireAndVerifyClientCert" | ||
client_ca_file: "client2_selfsigned.pem" | ||
client_allowed_san_regex: "test3" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
tls_server_config: | ||
cert_file: "server.crt" | ||
key_file: "server.key" | ||
client_auth_type: "RequireAndVerifyClientCert" | ||
client_ca_file: "client2_selfsigned.pem" | ||
client_allowed_san_regex: ".+test.+" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
tls_server_config: | ||
cert_file: "server.crt" | ||
key_file: "server.key" | ||
client_auth_type: "RequireAndVerifyClientCert" | ||
client_ca_file: "client2_selfsigned.pem" | ||
client_allowed_san_regex: (test\d|dns) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ import ( | |
"net/http" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
|
||
"github.com/coreos/go-systemd/v22/activation" | ||
"github.com/go-kit/log" | ||
|
@@ -51,6 +52,7 @@ type TLSConfig struct { | |
MinVersion TLSVersion `yaml:"min_version"` | ||
MaxVersion TLSVersion `yaml:"max_version"` | ||
PreferServerCipherSuites bool `yaml:"prefer_server_cipher_suites"` | ||
ClientAllowedSanRegex Regexp `yaml:"client_allowed_san_regex"` | ||
} | ||
|
||
type FlagConfig struct { | ||
|
@@ -66,6 +68,75 @@ func (t *TLSConfig) SetDirectory(dir string) { | |
t.ClientCAs = config_util.JoinDir(dir, t.ClientCAs) | ||
} | ||
|
||
// VerifyPeerCertificate will check the SAN entries of the client cert if there is configuration for it | ||
func (t *TLSConfig) VerifyPeerCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error { | ||
// sender cert comes first, see https://www.rfc-editor.org/rfc/rfc5246#section-7.4.2 | ||
cert, err := x509.ParseCertificate(rawCerts[0]) | ||
if err != nil { | ||
return fmt.Errorf("error parsing client certificate: %s", err) | ||
} | ||
|
||
// Build up a slice of strings with all Subject Alternate Name values | ||
sanValues := append(cert.DNSNames, cert.EmailAddresses...) | ||
roidelapluie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for _, ip := range cert.IPAddresses { | ||
roidelapluie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sanValues = append(sanValues, ip.String()) | ||
} | ||
|
||
for _, uri := range cert.URIs { | ||
roidelapluie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sanValues = append(sanValues, uri.String()) | ||
} | ||
|
||
for _, sanValue := range sanValues { | ||
if t.ClientAllowedSanRegex.MatchString(sanValue) { | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf("could not find configured SAN in client cert: %s", t.ClientAllowedSanRegex) | ||
} | ||
|
||
// Regexp encapsulates a regexp.Regexp and makes it YAML marshalable. | ||
type Regexp struct { | ||
*regexp.Regexp | ||
} | ||
|
||
// NewRegexp creates a new anchored Regexp and returns an error if the | ||
// passed-in regular expression does not compile. | ||
func NewRegexp(s string) (Regexp, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This struct borrowed from Prometheus |
||
regex, err := regexp.Compile("^(?:" + s + ")$") | ||
return Regexp{Regexp: regex}, err | ||
} | ||
|
||
// UnmarshalYAML implements the yaml.Unmarshaler interface. | ||
func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error { | ||
var s string | ||
if err := unmarshal(&s); err != nil { | ||
return err | ||
} | ||
r, err := NewRegexp(s) | ||
if err != nil { | ||
return err | ||
} | ||
*re = r | ||
return nil | ||
} | ||
|
||
// MarshalYAML implements the yaml.Marshaler interface. | ||
func (re Regexp) MarshalYAML() (interface{}, error) { | ||
if re.String() != "" { | ||
return re.String(), nil | ||
} | ||
return nil, nil | ||
} | ||
|
||
// String returns the original string used to compile the regular expression. | ||
func (re Regexp) String() string { | ||
str := re.Regexp.String() | ||
// Trim the anchor `^(?:` prefix and `)$` suffix. | ||
return str[4 : len(str)-2] | ||
} | ||
|
||
type HTTPConfig struct { | ||
HTTP2 bool `yaml:"http2"` | ||
Header map[string]string `yaml:"headers,omitempty"` | ||
|
@@ -163,6 +234,11 @@ func ConfigToTLSConfig(c *TLSConfig) (*tls.Config, error) { | |
cfg.ClientCAs = clientCAPool | ||
} | ||
|
||
if c.ClientAllowedSanRegex.Regexp != nil { | ||
// verify that the client cert contains the allowed domain name | ||
cfg.VerifyPeerCertificate = c.VerifyPeerCertificate | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need that if? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I'm not fully tracking the question, without the |
||
} | ||
|
||
switch c.ClientAuth { | ||
case "RequestClientCert": | ||
cfg.ClientAuth = tls.RequestClientCert | ||
|
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 is documented twice