Commit c0efec52 authored by Toby Allen's avatar Toby Allen Committed by GitHub

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
parent a74320bf
......@@ -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},
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment