15
package caddyhttp
16
17
import (
18
+ "crypto/sha256"
19
+ "crypto/tls"
20
+ "crypto/x509"
21
"fmt"
22
"net"
23
"net/http"
27
"strings"
28
29
"github.com/caddyserver/caddy/v2"
30
+ "github.com/caddyserver/caddy/v2/modules/caddytls"
31
)
32
33
func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.ResponseWriter) {
34
httpVars := func(key string) (string, bool) {
35
if req != nil {
36
// query string parameters
33
- if strings.HasPrefix(key, queryReplPrefix) {
34
- vals := req.URL.Query()[key[len(queryReplPrefix):]]
37
+ if strings.HasPrefix(key, reqURIQueryReplPrefix) {
38
+ vals := req.URL.Query()[key[len(reqURIQueryReplPrefix):]]
39
// always return true, since the query param might
40
// be present only in some requests
41
return strings.Join(vals, ","), true
51
}
52
53
// cookies
50
- if strings.HasPrefix(key, cookieReplPrefix) {
51
- name := key[len(cookieReplPrefix):]
54
+ if strings.HasPrefix(key, reqCookieReplPrefix) {
55
+ name := key[len(reqCookieReplPrefix):]
56
for _, cookie := range req.Cookies() {
57
if strings.EqualFold(name, cookie.Name) {
58
// always return true, since the cookie might
62
}
63
}
64
65
+ // http.request.tls.
66
+ if strings.HasPrefix(key, reqTLSReplPrefix) {
67
+ return getReqTLSReplacement(req, key)
68
+ }
69
+
70
switch key {
71
case "http.request.method":
72
return req.Method, true
138
}
139
140
// hostname labels
132
- if strings.HasPrefix(key, hostLabelReplPrefix) {
133
- idxStr := key[len(hostLabelReplPrefix):]
141
+ if strings.HasPrefix(key, reqHostLabelsReplPrefix) {
142
+ idxStr := key[len(reqHostLabelsReplPrefix):]
143
idx, err := strconv.Atoi(idxStr)
144
if err != nil {
145
return "", false
159
}
160
161
// path parts
153
- if strings.HasPrefix(key, pathPartsReplPrefix) {
154
- idxStr := key[len(pathPartsReplPrefix):]
162
+ if strings.HasPrefix(key, reqURIPathReplPrefix) {
163
+ idxStr := key[len(reqURIPathReplPrefix):]
164
idx, err := strconv.Atoi(idxStr)
165
if err != nil {
166
return "", false
217
repl.Map(httpVars)
218
}
219
220
+func getReqTLSReplacement(req *http.Request, key string) (string, bool) {
221
+ if req == nil || req.TLS == nil {
222
+ return "", false
223
+ }
224
+
225
+ if len(key) < len(reqTLSReplPrefix) {
226
+ return "", false
227
+ }
228
+
229
+ field := strings.ToLower(key[len(reqTLSReplPrefix):])
230
+
231
+ if strings.HasPrefix(field, "client.") {
232
+ cert := getTLSPeerCert(req.TLS)
233
+ if cert == nil {
234
+ return "", false
235
+ }
236
+
237
+ switch field {
238
+ case "client.fingerprint":
239
+ return fmt.Sprintf("%x", sha256.Sum256(cert.Raw)), true
240
+ case "client.issuer":
241
+ return cert.Issuer.String(), true
242
+ case "client.serial":
243
+ return fmt.Sprintf("%x", cert.SerialNumber), true
244
+ case "client.subject":
245
+ return cert.Subject.String(), true
246
+ default:
247
+ return "", false
248
+ }
249
+ }
250
+
251
+ switch field {
252
+ case "version":
253
+ return caddytls.ProtocolName(req.TLS.Version), true
254
+ case "cipher_suite":
255
+ return tls.CipherSuiteName(req.TLS.CipherSuite), true
256
+ case "resumed":
257
+ if req.TLS.DidResume {
258
+ return "true", true
259
+ }
260
+ return "false", true
261
+ case "proto":
262
+ return req.TLS.NegotiatedProtocol, true
263
+ case "proto_mutual":
264
+ if req.TLS.NegotiatedProtocolIsMutual {
265
+ return "true", true
266
+ }
267
+ return "false", true
268
+ case "server_name":
269
+ return req.TLS.ServerName, true
270
+ default:
271
+ return "", false
272
+ }
273
+}
274
+
275
+// getTLSPeerCert retrieves the first peer certificate from a TLS session.
276
+// Returns nil if no peer cert is in use.
277
+func getTLSPeerCert(cs *tls.ConnectionState) *x509.Certificate {
278
+ if len(cs.PeerCertificates) == 0 {
279
+ return nil
280
+ }
281
+ return cs.PeerCertificates[0]
282
+}
283
+
284
const (
212
- queryReplPrefix = "http.request.uri.query."
213
- reqHeaderReplPrefix = "http.request.header."
214
- cookieReplPrefix = "http.request.cookie."
215
- hostLabelReplPrefix = "http.request.host.labels."
216
- pathPartsReplPrefix = "http.request.uri.path."
217
- varsReplPrefix = "http.vars."
218
- respHeaderReplPrefix = "http.response.header."
285
+ reqCookieReplPrefix = "http.request.cookie."
286
+ reqHeaderReplPrefix = "http.request.header."
287
+ reqHostLabelsReplPrefix = "http.request.host.labels."
288
+ reqTLSReplPrefix = "http.request.tls."
289
+ reqURIPathReplPrefix = "http.request.uri.path."
290
+ reqURIQueryReplPrefix = "http.request.uri.query."
291
+ respHeaderReplPrefix = "http.response.header."
292
+ varsReplPrefix = "http.vars."
293
)