caddy/modules/logging/filters.go

200 lines
5.0 KiB
Go

// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logging
import (
"net"
"strconv"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"go.uber.org/zap/zapcore"
)
func init() {
caddy.RegisterModule(DeleteFilter{})
caddy.RegisterModule(ReplaceFilter{})
caddy.RegisterModule(IPMaskFilter{})
}
// LogFieldFilter can filter (or manipulate)
// a field in a log entry.
type LogFieldFilter interface {
Filter(zapcore.Field) zapcore.Field
}
// DeleteFilter is a Caddy log field filter that
// deletes the field.
type DeleteFilter struct{}
// CaddyModule returns the Caddy module information.
func (DeleteFilter) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "caddy.logging.encoders.filter.delete",
New: func() caddy.Module { return new(DeleteFilter) },
}
}
// 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
return in
}
// ReplaceFilter is a Caddy log field filter that
// replaces the field with the indicated string.
type ReplaceFilter struct {
Value string `json:"value,omitempty"`
}
// CaddyModule returns the Caddy module information.
func (ReplaceFilter) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "caddy.logging.encoders.filter.replace",
New: func() caddy.Module { return new(ReplaceFilter) },
}
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (f *ReplaceFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
if d.NextArg() {
f.Value = d.Val()
}
}
return nil
}
// Filter filters the input field with the replacement value.
func (f *ReplaceFilter) Filter(in zapcore.Field) zapcore.Field {
in.Type = zapcore.StringType
in.String = f.Value
return in
}
// IPMaskFilter is a Caddy log field filter that
// masks IP addresses.
type IPMaskFilter struct {
// 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"`
v4Mask net.IPMask
v6Mask net.IPMask
}
// CaddyModule returns the Caddy module information.
func (IPMaskFilter) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "caddy.logging.encoders.filter.ip_mask",
New: func() caddy.Module { return new(IPMaskFilter) },
}
}
// 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)
if err != nil {
host = in.String // assume whole thing was IP address
}
ipAddr := net.ParseIP(host)
if ipAddr == nil {
return in
}
mask := m.v4Mask
if ipAddr.To4() == nil {
mask = m.v6Mask
}
masked := ipAddr.Mask(mask)
if port == "" {
in.String = masked.String()
} else {
in.String = net.JoinHostPort(masked.String(), port)
}
return in
}
// Interface guards
var (
_ LogFieldFilter = (*DeleteFilter)(nil)
_ LogFieldFilter = (*ReplaceFilter)(nil)
_ LogFieldFilter = (*IPMaskFilter)(nil)
_ caddyfile.Unmarshaler = (*DeleteFilter)(nil)
_ caddyfile.Unmarshaler = (*ReplaceFilter)(nil)
_ caddyfile.Unmarshaler = (*IPMaskFilter)(nil)
_ caddy.Provisioner = (*IPMaskFilter)(nil)
)