mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-27 23:03:37 -05:00
caddyhttp: Add client cert SAN placeholders
This commit is contained in:
parent
b3bff13f7d
commit
d55c3b31eb
4 changed files with 87 additions and 5 deletions
|
@ -72,9 +72,15 @@ func init() {
|
||||||
// `{http.request.tls.proto_mutual}` | The negotiated next protocol was advertised by the server
|
// `{http.request.tls.proto_mutual}` | The negotiated next protocol was advertised by the server
|
||||||
// `{http.request.tls.server_name}` | The server name requested by the client, if any
|
// `{http.request.tls.server_name}` | The server name requested by the client, if any
|
||||||
// `{http.request.tls.client.fingerprint}` | The SHA256 checksum of the client certificate
|
// `{http.request.tls.client.fingerprint}` | The SHA256 checksum of the client certificate
|
||||||
|
// `{http.request.tls.client.public_key}` | The public key of the client certificate.
|
||||||
|
// `{http.request.tls.client.public_key_sha256}` | The SHA256 checksum of the client's public key.
|
||||||
// `{http.request.tls.client.issuer}` | The issuer DN of the client certificate
|
// `{http.request.tls.client.issuer}` | The issuer DN of the client certificate
|
||||||
// `{http.request.tls.client.serial}` | The serial number of the client certificate
|
// `{http.request.tls.client.serial}` | The serial number of the client certificate
|
||||||
// `{http.request.tls.client.subject}` | The subject DN of the client certificate
|
// `{http.request.tls.client.subject}` | The subject DN of the client certificate
|
||||||
|
// `{http.request.tls.client.san.dns_names.*}` | SAN DNS names(index optional)
|
||||||
|
// `{http.request.tls.client.san.emails.*}` | SAN email addresses (index optional)
|
||||||
|
// `{http.request.tls.client.san.ips.*}` | SAN IP addresses (index optional)
|
||||||
|
// `{http.request.tls.client.san.uris.*}` | SAN URIs (index optional)
|
||||||
// `{http.request.uri.path.*}` | Parts of the path, split by `/` (0-based from left)
|
// `{http.request.uri.path.*}` | Parts of the path, split by `/` (0-based from left)
|
||||||
// `{http.request.uri.path.dir}` | The directory, excluding leaf filename
|
// `{http.request.uri.path.dir}` | The directory, excluding leaf filename
|
||||||
// `{http.request.uri.path.file}` | The filename of the path, excluding directory
|
// `{http.request.uri.path.file}` | The filename of the path, excluding directory
|
||||||
|
|
|
@ -27,7 +27,7 @@ func init() {
|
||||||
|
|
||||||
// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax:
|
// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax:
|
||||||
//
|
//
|
||||||
// basicauth [<matcher>] [<hash_algorithm>] {
|
// basicauth [<matcher>] [<hash_algorithm> [<realm>]] {
|
||||||
// <username> <hashed_password_base64> [<salt_base64>]
|
// <username> <hashed_password_base64> [<salt_base64>]
|
||||||
// ...
|
// ...
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -163,7 +164,7 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
|
||||||
if strings.HasPrefix(key, reqHostLabelsReplPrefix) {
|
if strings.HasPrefix(key, reqHostLabelsReplPrefix) {
|
||||||
idxStr := key[len(reqHostLabelsReplPrefix):]
|
idxStr := key[len(reqHostLabelsReplPrefix):]
|
||||||
idx, err := strconv.Atoi(idxStr)
|
idx, err := strconv.Atoi(idxStr)
|
||||||
if err != nil {
|
if err != nil || idx < 0 {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
reqHost, _, err := net.SplitHostPort(req.Host)
|
reqHost, _, err := net.SplitHostPort(req.Host)
|
||||||
|
@ -171,9 +172,6 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
|
||||||
reqHost = req.Host // OK; assume there was no port
|
reqHost = req.Host // OK; assume there was no port
|
||||||
}
|
}
|
||||||
hostLabels := strings.Split(reqHost, ".")
|
hostLabels := strings.Split(reqHost, ".")
|
||||||
if idx < 0 {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if idx > len(hostLabels) {
|
if idx > len(hostLabels) {
|
||||||
return "", true
|
return "", true
|
||||||
}
|
}
|
||||||
|
@ -245,6 +243,64 @@ func getReqTLSReplacement(req *http.Request, key string) (interface{}, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// subject alternate names (SANs)
|
||||||
|
if strings.HasPrefix(field, "client.san.") {
|
||||||
|
field = field[len("client.san."):]
|
||||||
|
var fieldName string
|
||||||
|
var fieldValue interface{}
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(field, "dns_names"):
|
||||||
|
fieldName = "dns_names"
|
||||||
|
fieldValue = cert.DNSNames
|
||||||
|
case strings.HasPrefix(field, "emails"):
|
||||||
|
fieldName = "emails"
|
||||||
|
fieldValue = cert.EmailAddresses
|
||||||
|
case strings.HasPrefix(field, "ips"):
|
||||||
|
fieldName = "ips"
|
||||||
|
fieldValue = cert.IPAddresses
|
||||||
|
case strings.HasPrefix(field, "uris"):
|
||||||
|
fieldName = "uris"
|
||||||
|
fieldValue = cert.URIs
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
field = field[len(fieldName):]
|
||||||
|
|
||||||
|
// if no index was specified, return the whole list
|
||||||
|
if field == "" {
|
||||||
|
return fieldValue, true
|
||||||
|
}
|
||||||
|
if len(field) < 2 || field[0] != '.' {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
field = field[1:] // trim '.' between field name and index
|
||||||
|
|
||||||
|
// get the numeric index
|
||||||
|
idx, err := strconv.Atoi(field)
|
||||||
|
if err != nil || idx < 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// access the indexed element and return it
|
||||||
|
switch v := fieldValue.(type) {
|
||||||
|
case []string:
|
||||||
|
if idx >= len(v) {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
return v[idx], true
|
||||||
|
case []net.IP:
|
||||||
|
if idx >= len(v) {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
return v[idx], true
|
||||||
|
case []*url.URL:
|
||||||
|
if idx >= len(v) {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
return v[idx], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch field {
|
switch field {
|
||||||
case "client.fingerprint":
|
case "client.fingerprint":
|
||||||
return fmt.Sprintf("%x", sha256.Sum256(cert.Raw)), true
|
return fmt.Sprintf("%x", sha256.Sum256(cert.Raw)), true
|
||||||
|
|
|
@ -147,6 +147,26 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
|
||||||
input: "{http.request.tls.client.subject}",
|
input: "{http.request.tls.client.subject}",
|
||||||
expect: "CN=client.localdomain",
|
expect: "CN=client.localdomain",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: "{http.request.tls.client.san.dns_names}",
|
||||||
|
expect: "[localhost]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "{http.request.tls.client.san.dns_names.0}",
|
||||||
|
expect: "localhost",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "{http.request.tls.client.san.dns_names.1}",
|
||||||
|
expect: "<empty>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "{http.request.tls.client.san.ips}",
|
||||||
|
expect: "[127.0.0.1]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "{http.request.tls.client.san.ips.0}",
|
||||||
|
expect: "127.0.0.1",
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
actual := repl.ReplaceAll(tc.input, "<empty>")
|
actual := repl.ReplaceAll(tc.input, "<empty>")
|
||||||
if actual != tc.expect {
|
if actual != tc.expect {
|
||||||
|
|
Loading…
Add table
Reference in a new issue