mirror of
https://github.com/caddyserver/caddy.git
synced 2025-02-06 00:58:26 +03:00
Allow Masking of IP address in Logfile. (#1930)
* First working mask * IP Mask working with defaults and empty * add tests for ipmask * Store Mask as setup, some tidying, cleaner flow * Prevent mask from running when directive not present * use custom replacement to store masked ip
This commit is contained in:
parent
a74320bf4c
commit
c0efec52d9
4 changed files with 214 additions and 47 deletions
|
@ -18,6 +18,7 @@ import (
|
|||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -37,9 +38,12 @@ var remoteSyslogPrefixes = map[string]string{
|
|||
type Logger struct {
|
||||
Output string
|
||||
*log.Logger
|
||||
Roller *LogRoller
|
||||
writer io.Writer
|
||||
fileMu *sync.RWMutex
|
||||
Roller *LogRoller
|
||||
writer io.Writer
|
||||
fileMu *sync.RWMutex
|
||||
V4ipMask net.IPMask
|
||||
V6ipMask net.IPMask
|
||||
IPMaskExists bool
|
||||
}
|
||||
|
||||
// NewTestLogger creates logger suitable for testing purposes
|
||||
|
@ -64,6 +68,22 @@ func (l Logger) Printf(format string, args ...interface{}) {
|
|||
l.fileMu.RUnlock()
|
||||
}
|
||||
|
||||
func (l Logger) MaskIP(ip string) string {
|
||||
var reqIP net.IP
|
||||
// If unable to parse, simply return IP as provided.
|
||||
reqIP = net.ParseIP(ip)
|
||||
if reqIP == nil {
|
||||
return ip
|
||||
}
|
||||
|
||||
if reqIP.To4() != nil {
|
||||
return reqIP.Mask(l.V4ipMask).String()
|
||||
} else {
|
||||
return reqIP.Mask(l.V6ipMask).String()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Attach binds logger Start and Close functions to
|
||||
// controller's OnStartup and OnShutdown hooks.
|
||||
func (l *Logger) Attach(controller *caddy.Controller) {
|
||||
|
|
|
@ -17,6 +17,7 @@ package log
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
|
@ -66,6 +67,16 @@ func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||
|
||||
// Write log entries
|
||||
for _, e := range rule.Entries {
|
||||
|
||||
// Mask IP Address
|
||||
if e.Log.IPMaskExists {
|
||||
hostip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err == nil {
|
||||
maskedIP := e.Log.MaskIP(hostip)
|
||||
// Overwrite log value with Masked version
|
||||
rep.Set("remote", maskedIP)
|
||||
}
|
||||
}
|
||||
e.Log.Println(rep.Replace(e.Format))
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
|
@ -47,6 +48,10 @@ func logParse(c *caddy.Controller) ([]*Rule, error) {
|
|||
for c.Next() {
|
||||
args := c.RemainingArgs()
|
||||
|
||||
ip4Mask := net.IPMask(net.ParseIP(DefaultIP4Mask).To4())
|
||||
ip6Mask := net.IPMask(net.ParseIP(DefaultIP6Mask))
|
||||
ipMaskExists := false
|
||||
|
||||
var logRoller *httpserver.LogRoller
|
||||
logRoller = httpserver.DefaultLogRoller()
|
||||
|
||||
|
@ -54,14 +59,48 @@ func logParse(c *caddy.Controller) ([]*Rule, error) {
|
|||
what := c.Val()
|
||||
where := c.RemainingArgs()
|
||||
|
||||
// only support roller related options inside a block
|
||||
if !httpserver.IsLogRollerSubdirective(what) {
|
||||
if what == "ipmask" {
|
||||
|
||||
if len(where) == 0 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
if where[0] != "" {
|
||||
ip4MaskStr := where[0]
|
||||
ipv4 := net.ParseIP(ip4MaskStr).To4()
|
||||
|
||||
if ipv4 == nil {
|
||||
return nil, c.Err("IPv4 Mask not valid IP Mask Format")
|
||||
} else {
|
||||
ip4Mask = net.IPMask(ipv4)
|
||||
ipMaskExists = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(where) > 1 {
|
||||
|
||||
ip6MaskStr := where[1]
|
||||
ipv6 := net.ParseIP(ip6MaskStr)
|
||||
|
||||
if ipv6 == nil {
|
||||
return nil, c.Err("IPv6 Mask not valid IP Mask Format")
|
||||
} else {
|
||||
ip6Mask = net.IPMask(ipv6)
|
||||
ipMaskExists = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if httpserver.IsLogRollerSubdirective(what) {
|
||||
|
||||
if err := httpserver.ParseRoller(logRoller, what, where...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
if err := httpserver.ParseRoller(logRoller, what, where...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
path := "/"
|
||||
|
@ -89,8 +128,11 @@ func logParse(c *caddy.Controller) ([]*Rule, error) {
|
|||
|
||||
rules = appendEntry(rules, path, &Entry{
|
||||
Log: &httpserver.Logger{
|
||||
Output: output,
|
||||
Roller: logRoller,
|
||||
Output: output,
|
||||
Roller: logRoller,
|
||||
V4ipMask: ip4Mask,
|
||||
V6ipMask: ip6Mask,
|
||||
IPMaskExists: ipMaskExists,
|
||||
},
|
||||
Format: format,
|
||||
})
|
||||
|
@ -114,3 +156,10 @@ func appendEntry(rules []*Rule, pathScope string, entry *Entry) []*Rule {
|
|||
|
||||
return rules
|
||||
}
|
||||
|
||||
const (
|
||||
// IP Masks that have no effect on IP Address
|
||||
DefaultIP4Mask = "255.255.255.255"
|
||||
|
||||
DefaultIP6Mask = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
|
||||
)
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
|
@ -47,8 +47,10 @@ func TestSetup(t *testing.T) {
|
|||
}
|
||||
|
||||
expectedLogger := &httpserver.Logger{
|
||||
Output: DefaultLogFilename,
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: DefaultLogFilename,
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(myHandler.Rules[0].Entries[0].Log, expectedLogger) {
|
||||
|
@ -72,8 +74,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: DefaultLogFilename,
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: DefaultLogFilename,
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
|
@ -82,8 +86,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
|
@ -92,8 +98,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "syslog://127.0.0.1:5000",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "syslog://127.0.0.1:5000",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
|
@ -102,8 +110,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "syslog+tcp://127.0.0.1:5000",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "syslog+tcp://127.0.0.1:5000",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
|
@ -112,8 +122,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/api",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
|
@ -122,8 +134,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/serve",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "stdout",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "stdout",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
|
@ -132,8 +146,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/myapi",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: CommonLogFormat,
|
||||
}},
|
||||
|
@ -142,8 +158,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/myapi",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: "prefix " + CommonLogFormat + " suffix",
|
||||
}},
|
||||
|
@ -152,8 +170,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/test",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "accesslog.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "accesslog.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: CombinedLogFormat,
|
||||
}},
|
||||
|
@ -162,8 +182,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/test",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "accesslog.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "accesslog.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: "prefix " + CombinedLogFormat + " suffix",
|
||||
}},
|
||||
|
@ -173,8 +195,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/api1",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
|
@ -182,8 +206,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/api2",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "accesslog.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "accesslog.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: CombinedLogFormat,
|
||||
}},
|
||||
|
@ -193,8 +219,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/api3",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "stdout",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "stdout",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: "{host}",
|
||||
}},
|
||||
|
@ -202,8 +230,10 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/api4",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: "{when}",
|
||||
}},
|
||||
|
@ -224,7 +254,59 @@ func TestLogParse(t *testing.T) {
|
|||
MaxBackups: 3,
|
||||
Compress: true,
|
||||
LocalTime: true,
|
||||
}},
|
||||
},
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
}}},
|
||||
{`log access0.log {
|
||||
ipmask 255.255.255.0
|
||||
}`, false, []Rule{{
|
||||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "access0.log",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP("255.255.255.0").To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
IPMaskExists: true,
|
||||
},
|
||||
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
}}},
|
||||
{`log access1.log {
|
||||
ipmask "" ffff:ffff:ffff:ff00::
|
||||
}`, false, []Rule{{
|
||||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "access1.log",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ff00::")),
|
||||
IPMaskExists: true,
|
||||
},
|
||||
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
}}},
|
||||
{`log access2.log {
|
||||
ipmask 255.255.255.0 ffff:ffff:ffff:ff00::
|
||||
}`, false, []Rule{{
|
||||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "access2.log",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP("255.255.255.0").To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ff00::")),
|
||||
IPMaskExists: true,
|
||||
},
|
||||
|
||||
Format: DefaultLogFormat,
|
||||
}},
|
||||
}}},
|
||||
|
@ -233,14 +315,18 @@ func TestLogParse(t *testing.T) {
|
|||
PathScope: "/",
|
||||
Entries: []*Entry{{
|
||||
Log: &httpserver.Logger{
|
||||
Output: "stdout",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "stdout",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: "{host}",
|
||||
}, {
|
||||
Log: &httpserver.Logger{
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
Output: "log.txt",
|
||||
Roller: httpserver.DefaultLogRoller(),
|
||||
V4ipMask: net.IPMask(net.ParseIP(DefaultIP4Mask).To4()),
|
||||
V6ipMask: net.IPMask(net.ParseIP(DefaultIP6Mask)),
|
||||
},
|
||||
Format: "{when}",
|
||||
}},
|
||||
|
@ -248,6 +334,7 @@ func TestLogParse(t *testing.T) {
|
|||
{`log access.log { rotate_size 2 rotate_age 10 rotate_keep 3 }`, true, nil},
|
||||
{`log access.log { rotate_compress invalid }`, true, nil},
|
||||
{`log access.log { rotate_size }`, true, nil},
|
||||
{`log access.log { ipmask }`, true, nil},
|
||||
{`log access.log { invalid_option 1 }`, true, nil},
|
||||
{`log / acccess.log "{remote} - [{when}] "{method} {port}" {scheme} {mitm} "`, true, nil},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue