summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--caddytest/integration/caddyfile_adapt/log_filters.txt69
-rw-r--r--modules/logging/filterencoder.go77
-rw-r--r--modules/logging/filters.go88
3 files changed, 225 insertions, 9 deletions
diff --git a/caddytest/integration/caddyfile_adapt/log_filters.txt b/caddytest/integration/caddyfile_adapt/log_filters.txt
new file mode 100644
index 0000000..549f4e6
--- /dev/null
+++ b/caddytest/integration/caddyfile_adapt/log_filters.txt
@@ -0,0 +1,69 @@
+:80
+
+log {
+ output stdout
+ format filter {
+ wrap console
+ fields {
+ request>headers>Authorization delete
+ request>headers>Server delete
+ request>remote_addr ip_mask {
+ ipv4 24
+ ipv6 32
+ }
+ }
+ }
+}
+----------
+{
+ "logging": {
+ "logs": {
+ "default": {
+ "exclude": [
+ "http.log.access.log0"
+ ]
+ },
+ "log0": {
+ "writer": {
+ "output": "stdout"
+ },
+ "encoder": {
+ "fields": {
+ "request\u003eheaders\u003eAuthorization": {
+ "filter": "delete"
+ },
+ "request\u003eheaders\u003eServer": {
+ "filter": "delete"
+ },
+ "request\u003eremote_addr": {
+ "filter": "ip_mask",
+ "ipv4_cidr": 24,
+ "ipv6_cidr": 32
+ }
+ },
+ "format": "filter",
+ "wrap": {
+ "format": "console"
+ }
+ },
+ "include": [
+ "http.log.access.log0"
+ ]
+ }
+ }
+ },
+ "apps": {
+ "http": {
+ "servers": {
+ "srv0": {
+ "listen": [
+ ":80"
+ ],
+ "logs": {
+ "default_logger_name": "log0"
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/logging/filterencoder.go b/modules/logging/filterencoder.go
index 7a8108c..d1c335f 100644
--- a/modules/logging/filterencoder.go
+++ b/modules/logging/filterencoder.go
@@ -20,6 +20,8 @@ import (
"time"
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"go.uber.org/zap"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
@@ -94,6 +96,80 @@ func (fe *FilterEncoder) Provision(ctx caddy.Context) error {
return nil
}
+// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
+//
+// filter {
+// wrap <another encoder>
+// fields {
+// <field> <filter> {
+// <filter options>
+// }
+// }
+// }
+func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ for d.NextBlock(0) {
+ switch d.Val() {
+ case "wrap":
+ if !d.NextArg() {
+ return d.ArgErr()
+ }
+ moduleName := d.Val()
+ mod, err := caddy.GetModule("caddy.logging.encoders." + moduleName)
+ if err != nil {
+ return d.Errf("getting log encoder module named '%s': %v", moduleName, err)
+ }
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
+ if !ok {
+ return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod)
+ }
+ err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
+ if err != nil {
+ return err
+ }
+ enc, ok := unm.(zapcore.Encoder)
+ if !ok {
+ return d.Errf("module %s is not a zapcore.Encoder", mod)
+ }
+ fe.WrappedRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, nil)
+
+ case "fields":
+ for d.NextBlock(1) {
+ field := d.Val()
+ if !d.NextArg() {
+ return d.ArgErr()
+ }
+ filterName := d.Val()
+ mod, err := caddy.GetModule("caddy.logging.encoders.filter." + filterName)
+ if err != nil {
+ return d.Errf("getting log filter module named '%s': %v", filterName, err)
+ }
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
+ if !ok {
+ return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod)
+ }
+ err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
+ if err != nil {
+ return err
+ }
+ f, ok := unm.(LogFieldFilter)
+ if !ok {
+ return d.Errf("module %s is not a LogFieldFilter", mod)
+ }
+ if fe.FieldsRaw == nil {
+ fe.FieldsRaw = make(map[string]json.RawMessage)
+ }
+ fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(f, "filter", filterName, nil)
+ }
+
+ default:
+ return d.Errf("unrecognized subdirective %s", d.Val())
+ }
+ }
+ }
+ return nil
+}
+
// AddArray is part of the zapcore.ObjectEncoder interface.
// Array elements do not get filtered.
func (fe FilterEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
@@ -330,4 +406,5 @@ func (mom logObjectMarshalerWrapper) MarshalLogObject(_ zapcore.ObjectEncoder) e
var (
_ zapcore.Encoder = (*FilterEncoder)(nil)
_ zapcore.ObjectMarshaler = (*logObjectMarshalerWrapper)(nil)
+ _ caddyfile.Unmarshaler = (*FilterEncoder)(nil)
)
diff --git a/modules/logging/filters.go b/modules/logging/filters.go
index cd3bbb1..3bc1e2c 100644
--- a/modules/logging/filters.go
+++ b/modules/logging/filters.go
@@ -16,8 +16,10 @@ package logging
import (
"net"
+ "strconv"
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"go.uber.org/zap/zapcore"
)
@@ -44,6 +46,11 @@ func (DeleteFilter) CaddyModule() caddy.ModuleInfo {
}
}
+// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
+func (DeleteFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ return nil
+}
+
// Filter filters the input field.
func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field {
in.Type = zapcore.SkipType
@@ -53,11 +60,14 @@ func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field {
// IPMaskFilter is a Caddy log field filter that
// masks IP addresses.
type IPMaskFilter struct {
- // The IPv4 range in CIDR notation.
- IPv4CIDR int `json:"ipv4_cidr,omitempty"`
+ // The IPv4 mask, as an subnet size CIDR.
+ IPv4MaskRaw int `json:"ipv4_cidr,omitempty"`
+
+ // The IPv6 mask, as an subnet size CIDR.
+ IPv6MaskRaw int `json:"ipv6_cidr,omitempty"`
- // The IPv6 range in CIDR notation.
- IPv6CIDR int `json:"ipv6_cidr,omitempty"`
+ v4Mask net.IPMask
+ v6Mask net.IPMask
}
// CaddyModule returns the Caddy module information.
@@ -68,6 +78,58 @@ func (IPMaskFilter) CaddyModule() caddy.ModuleInfo {
}
}
+// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
+func (m *IPMaskFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ for d.NextBlock(0) {
+ switch d.Val() {
+ case "ipv4":
+ if !d.NextArg() {
+ return d.ArgErr()
+ }
+ val, err := strconv.Atoi(d.Val())
+ if err != nil {
+ return d.Errf("error parsing %s: %v", d.Val(), err)
+ }
+ m.IPv4MaskRaw = val
+
+ case "ipv6":
+ if !d.NextArg() {
+ return d.ArgErr()
+ }
+ val, err := strconv.Atoi(d.Val())
+ if err != nil {
+ return d.Errf("error parsing %s: %v", d.Val(), err)
+ }
+ m.IPv6MaskRaw = val
+
+ default:
+ return d.Errf("unrecognized subdirective %s", d.Val())
+ }
+ }
+ }
+ return nil
+}
+
+// Provision parses m's IP masks, from integers.
+func (m *IPMaskFilter) Provision(ctx caddy.Context) error {
+ parseRawToMask := func(rawField int, bitLen int) net.IPMask {
+ if rawField == 0 {
+ return nil
+ }
+
+ // we assume the int is a subnet size CIDR
+ // e.g. "16" being equivalent to masking the last
+ // two bytes of an ipv4 address, like "255.255.0.0"
+ return net.CIDRMask(rawField, bitLen)
+ }
+
+ m.v4Mask = parseRawToMask(m.IPv4MaskRaw, 32)
+ m.v6Mask = parseRawToMask(m.IPv6MaskRaw, 128)
+
+ return nil
+}
+
// Filter filters the input field.
func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
host, port, err := net.SplitHostPort(in.String)
@@ -78,13 +140,10 @@ func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
if ipAddr == nil {
return in
}
- bitLen := 32
- cidrPrefix := m.IPv4CIDR
+ mask := m.v4Mask
if ipAddr.To16() != nil {
- bitLen = 128
- cidrPrefix = m.IPv6CIDR
+ mask = m.v6Mask
}
- mask := net.CIDRMask(cidrPrefix, bitLen)
masked := ipAddr.Mask(mask)
if port == "" {
in.String = masked.String()
@@ -93,3 +152,14 @@ func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
}
return in
}
+
+// Interface guards
+var (
+ _ LogFieldFilter = (*DeleteFilter)(nil)
+ _ LogFieldFilter = (*IPMaskFilter)(nil)
+
+ _ caddyfile.Unmarshaler = (*DeleteFilter)(nil)
+ _ caddyfile.Unmarshaler = (*IPMaskFilter)(nil)
+
+ _ caddy.Provisioner = (*IPMaskFilter)(nil)
+)