@marcel91200 / caddy Mirror · read-only / commits / 309c1fec
Fork 0

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 +)