Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
logging: Implement Caddyfile support for filter encoder (#3578)
* logging: Implement Caddyfile support for filter encoder * logging: Add support for parsing IP masks from strings wip * logging: Implement Caddyfile support for ip_mask * logging: Get rid of unnecessary logic to allow strings, not that useful * logging: Add adapt test
Francis Lavoie committed
Sep 15, 2020 at 14:37 UTC
309c1fec622c08bdc68a43de64fc0777f590d309
3 files changed
+225
-9
caddytest/integration/caddyfile_adapt/log_filters.txt
new
+69
@@ -0,0 +1,69 @@
1
+:80
2
+
3
+log {
4
+ output stdout
5
+ format filter {
6
+ wrap console
7
+ fields {
8
+ request>headers>Authorization delete
9
+ request>headers>Server delete
10
+ request>remote_addr ip_mask {
11
+ ipv4 24
12
+ ipv6 32
13
+ }
14
+ }
15
+ }
16
+}
17
+----------
18
+{
19
+ "logging": {
20
+ "logs": {
21
+ "default": {
22
+ "exclude": [
23
+ "http.log.access.log0"
24
+ ]
25
+ },
26
+ "log0": {
27
+ "writer": {
28
+ "output": "stdout"
29
+ },
30
+ "encoder": {
31
+ "fields": {
32
+ "request\u003eheaders\u003eAuthorization": {
33
+ "filter": "delete"
34
+ },
35
+ "request\u003eheaders\u003eServer": {
36
+ "filter": "delete"
37
+ },
38
+ "request\u003eremote_addr": {
39
+ "filter": "ip_mask",
40
+ "ipv4_cidr": 24,
41
+ "ipv6_cidr": 32
42
+ }
43
+ },
44
+ "format": "filter",
45
+ "wrap": {
46
+ "format": "console"
47
+ }
48
+ },
49
+ "include": [
50
+ "http.log.access.log0"
51
+ ]
52
+ }
53
+ }
54
+ },
55
+ "apps": {
56
+ "http": {
57
+ "servers": {
58
+ "srv0": {
59
+ "listen": [
60
+ ":80"
61
+ ],
62
+ "logs": {
63
+ "default_logger_name": "log0"
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+}
\ No newline at end of file
modules/logging/filterencoder.go
+77
@@ -20,6 +20,8 @@ import (
20
"time"
21
22
"github.com/caddyserver/caddy/v2"
23
+ "github.com/caddyserver/caddy/v2/caddyconfig"
24
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
25
"go.uber.org/zap"
26
"go.uber.org/zap/buffer"
27
"go.uber.org/zap/zapcore"
@@ -94,6 +96,80 @@ func (fe *FilterEncoder) Provision(ctx caddy.Context) error {
96
return nil
97
}
98
99
+// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
100
+//
101
+// filter {
102
+// wrap <another encoder>
103
+// fields {
104
+// <field> <filter> {
105
+// <filter options>
106
+// }
107
+// }
108
+// }
109
+func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
110
+ for d.Next() {
111
+ for d.NextBlock(0) {
112
+ switch d.Val() {
113
+ case "wrap":
114
+ if !d.NextArg() {
115
+ return d.ArgErr()
116
+ }
117
+ moduleName := d.Val()
118
+ mod, err := caddy.GetModule("caddy.logging.encoders." + moduleName)
119
+ if err != nil {
120
+ return d.Errf("getting log encoder module named '%s': %v", moduleName, err)
121
+ }
122
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
123
+ if !ok {
124
+ return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod)
125
+ }
126
+ err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
127
+ if err != nil {
128
+ return err
129
+ }
130
+ enc, ok := unm.(zapcore.Encoder)
131
+ if !ok {
132
+ return d.Errf("module %s is not a zapcore.Encoder", mod)
133
+ }
134
+ fe.WrappedRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, nil)
135
+
136
+ case "fields":
137
+ for d.NextBlock(1) {
138
+ field := d.Val()
139
+ if !d.NextArg() {
140
+ return d.ArgErr()
141
+ }
142
+ filterName := d.Val()
143
+ mod, err := caddy.GetModule("caddy.logging.encoders.filter." + filterName)
144
+ if err != nil {
145
+ return d.Errf("getting log filter module named '%s': %v", filterName, err)
146
+ }
147
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
148
+ if !ok {
149
+ return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod)
150
+ }
151
+ err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
152
+ if err != nil {
153
+ return err
154
+ }
155
+ f, ok := unm.(LogFieldFilter)
156
+ if !ok {
157
+ return d.Errf("module %s is not a LogFieldFilter", mod)
158
+ }
159
+ if fe.FieldsRaw == nil {
160
+ fe.FieldsRaw = make(map[string]json.RawMessage)
161
+ }
162
+ fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(f, "filter", filterName, nil)
163
+ }
164
+
165
+ default:
166
+ return d.Errf("unrecognized subdirective %s", d.Val())
167
+ }
168
+ }
169
+ }
170
+ return nil
171
+}
172
+
173
// AddArray is part of the zapcore.ObjectEncoder interface.
174
// Array elements do not get filtered.
175
func (fe FilterEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
@@ -330,4 +406,5 @@ func (mom logObjectMarshalerWrapper) MarshalLogObject(_ zapcore.ObjectEncoder) e
406
var (
407
_ zapcore.Encoder = (*FilterEncoder)(nil)
408
_ zapcore.ObjectMarshaler = (*logObjectMarshalerWrapper)(nil)
409
+ _ caddyfile.Unmarshaler = (*FilterEncoder)(nil)
410
)
modules/logging/filters.go
+79
-9
@@ -16,8 +16,10 @@ package logging
16
17
import (
18
"net"
19
+ "strconv"
20
21
"github.com/caddyserver/caddy/v2"
22
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
23
"go.uber.org/zap/zapcore"
24
)
25
@@ -44,6 +46,11 @@ func (DeleteFilter) CaddyModule() caddy.ModuleInfo {
46
}
47
}
48
49
+// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
50
+func (DeleteFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
51
+ return nil
52
+}
53
+
54
// Filter filters the input field.
55
func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field {
56
in.Type = zapcore.SkipType
@@ -53,11 +60,14 @@ func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field {
60
// IPMaskFilter is a Caddy log field filter that
61
// masks IP addresses.
62
type IPMaskFilter struct {
56
- // The IPv4 range in CIDR notation.
57
- IPv4CIDR int `json:"ipv4_cidr,omitempty"`
63
+ // The IPv4 mask, as an subnet size CIDR.
64
+ IPv4MaskRaw int `json:"ipv4_cidr,omitempty"`
65
+
66
+ // The IPv6 mask, as an subnet size CIDR.
67
+ IPv6MaskRaw int `json:"ipv6_cidr,omitempty"`
68
59
- // The IPv6 range in CIDR notation.
60
- IPv6CIDR int `json:"ipv6_cidr,omitempty"`
69
+ v4Mask net.IPMask
70
+ v6Mask net.IPMask
71
}
72
73
// CaddyModule returns the Caddy module information.
@@ -68,6 +78,58 @@ func (IPMaskFilter) CaddyModule() caddy.ModuleInfo {
78
}
79
}
80
81
+// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
82
+func (m *IPMaskFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
83
+ for d.Next() {
84
+ for d.NextBlock(0) {
85
+ switch d.Val() {
86
+ case "ipv4":
87
+ if !d.NextArg() {
88
+ return d.ArgErr()
89
+ }
90
+ val, err := strconv.Atoi(d.Val())
91
+ if err != nil {
92
+ return d.Errf("error parsing %s: %v", d.Val(), err)
93
+ }
94
+ m.IPv4MaskRaw = val
95
+
96
+ case "ipv6":
97
+ if !d.NextArg() {
98
+ return d.ArgErr()
99
+ }
100
+ val, err := strconv.Atoi(d.Val())
101
+ if err != nil {
102
+ return d.Errf("error parsing %s: %v", d.Val(), err)
103
+ }
104
+ m.IPv6MaskRaw = val
105
+
106
+ default:
107
+ return d.Errf("unrecognized subdirective %s", d.Val())
108
+ }
109
+ }
110
+ }
111
+ return nil
112
+}
113
+
114
+// Provision parses m's IP masks, from integers.
115
+func (m *IPMaskFilter) Provision(ctx caddy.Context) error {
116
+ parseRawToMask := func(rawField int, bitLen int) net.IPMask {
117
+ if rawField == 0 {
118
+ return nil
119
+ }
120
+
121
+ // we assume the int is a subnet size CIDR
122
+ // e.g. "16" being equivalent to masking the last
123
+ // two bytes of an ipv4 address, like "255.255.0.0"
124
+ return net.CIDRMask(rawField, bitLen)
125
+ }
126
+
127
+ m.v4Mask = parseRawToMask(m.IPv4MaskRaw, 32)
128
+ m.v6Mask = parseRawToMask(m.IPv6MaskRaw, 128)
129
+
130
+ return nil
131
+}
132
+
133
// Filter filters the input field.
134
func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
135
host, port, err := net.SplitHostPort(in.String)
@@ -78,13 +140,10 @@ func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
140
if ipAddr == nil {
141
return in
142
}
81
- bitLen := 32
82
- cidrPrefix := m.IPv4CIDR
143
+ mask := m.v4Mask
144
if ipAddr.To16() != nil {
84
- bitLen = 128
85
- cidrPrefix = m.IPv6CIDR
145
+ mask = m.v6Mask
146
}
87
- mask := net.CIDRMask(cidrPrefix, bitLen)
147
masked := ipAddr.Mask(mask)
148
if port == "" {
149
in.String = masked.String()
@@ -93,3 +152,14 @@ func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
152
}
153
return in
154
}
155
+
156
+// Interface guards
157
+var (
158
+ _ LogFieldFilter = (*DeleteFilter)(nil)
159
+ _ LogFieldFilter = (*IPMaskFilter)(nil)
160
+
161
+ _ caddyfile.Unmarshaler = (*DeleteFilter)(nil)
162
+ _ caddyfile.Unmarshaler = (*IPMaskFilter)(nil)
163
+
164
+ _ caddy.Provisioner = (*IPMaskFilter)(nil)
165
+)