Add audit logging for S3 and Web handlers (#6571)

This PR brings an additional logger implementation
called AuditLog which logs to http targets

The intention is to use AuditLog to log all incoming
requests, this is used as a mechanism by external log
collection entities for processing Minio requests.
This commit is contained in:
Harshavardhana 2018-10-12 12:25:59 -07:00 committed by kannappanr
parent 143e7fe300
commit b0c9ae7490
17 changed files with 232 additions and 17 deletions

View File

@ -21,6 +21,7 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
) )
@ -56,6 +57,8 @@ type accessControlPolicy struct {
func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketACL") ctx := newContext(r, w, "GetBucketACL")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -103,6 +106,8 @@ func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.
func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetObjectACL") ctx := newContext(r, w, "GetObjectACL")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]

View File

@ -21,6 +21,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/crypto"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
) )
@ -58,6 +59,8 @@ func validateListObjectsArgs(prefix, marker, delimiter string, maxKeys int) APIE
func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListObjectsV2") ctx := newContext(r, w, "ListObjectsV2")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -137,6 +140,8 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListObjectsV1") ctx := newContext(r, w, "ListObjectsV1")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]

View File

@ -89,6 +89,8 @@ func initFederatorBackend(objLayer ObjectLayer) {
func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketLocation") ctx := newContext(r, w, "GetBucketLocation")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -137,6 +139,8 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListMultipartUploads") ctx := newContext(r, w, "ListMultipartUploads")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -184,6 +188,8 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListBuckets") ctx := newContext(r, w, "ListBuckets")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -384,6 +390,8 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutBucket") ctx := newContext(r, w, "PutBucket")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -460,6 +468,8 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PostPolicyBucket") ctx := newContext(r, w, "PostPolicyBucket")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -671,6 +681,12 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
default: default:
writeSuccessNoContent(w) writeSuccessNoContent(w)
} }
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
// HeadBucketHandler - HEAD Bucket // HeadBucketHandler - HEAD Bucket
@ -682,6 +698,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "HeadBucket") ctx := newContext(r, w, "HeadBucket")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -712,6 +730,8 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re
func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "DeleteBucket") ctx := newContext(r, w, "DeleteBucket")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]

View File

@ -44,6 +44,8 @@ var errNoSuchNotifications = errors.New("The specified bucket does not have buck
func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketNotification") ctx := newContext(r, w, "GetBucketNotification")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucketName := vars["bucket"] bucketName := vars["bucket"]
@ -96,6 +98,8 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutBucketNotification") ctx := newContext(r, w, "PutBucketNotification")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -156,6 +160,8 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListenBucketNotification") ctx := newContext(r, w, "ListenBucketNotification")
defer logger.AuditLog(ctx, r)
// Validate if bucket exists. // Validate if bucket exists.
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {

View File

@ -40,6 +40,8 @@ const (
func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutBucketPolicy") ctx := newContext(r, w, "PutBucketPolicy")
defer logger.AuditLog(ctx, r)
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -101,6 +103,8 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "DeleteBucketPolicy") ctx := newContext(r, w, "DeleteBucketPolicy")
defer logger.AuditLog(ctx, r)
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -137,6 +141,8 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketPolicy") ctx := newContext(r, w, "GetBucketPolicy")
defer logger.AuditLog(ctx, r)
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)

View File

@ -49,10 +49,17 @@ func checkUpdate(mode string) {
// Load logger targets based on user's configuration // Load logger targets based on user's configuration
func loadLoggers() { func loadLoggers() {
if endpoint, ok := os.LookupEnv("MINIO_LOGGER_HTTP_ENDPOINT"); ok {
// Enable http logging through ENV, this is specifically added gateway audit logging.
logger.AddTarget(logger.NewHTTP(endpoint, NewCustomHTTPTransport()))
return
}
if globalServerConfig.Logger.Console.Enabled { if globalServerConfig.Logger.Console.Enabled {
// Enable console logging // Enable console logging
logger.AddTarget(logger.NewConsole()) logger.AddTarget(logger.NewConsole())
} }
for _, l := range globalServerConfig.Logger.HTTP { for _, l := range globalServerConfig.Logger.HTTP {
if l.Enabled { if l.Enabled {
// Enable http logging // Enable http logging

View File

@ -224,7 +224,16 @@ func FromMinioClientListBucketResultToV2Info(bucket string, result minio.ListBuc
} }
} }
// ToMinioClientMetadata converts metadata to map[string][]string // ToMinioClientObjectInfoMetadata convertes metadata to map[string][]string
func ToMinioClientObjectInfoMetadata(metadata map[string]string) map[string][]string {
mm := make(map[string][]string, len(metadata))
for k, v := range metadata {
mm[http.CanonicalHeaderKey(k)] = []string{v}
}
return mm
}
// ToMinioClientMetadata converts metadata to map[string]string
func ToMinioClientMetadata(metadata map[string]string) map[string]string { func ToMinioClientMetadata(metadata map[string]string) map[string]string {
mm := make(map[string]string) mm := make(map[string]string)
for k, v := range metadata { for k, v := range metadata {

View File

@ -228,6 +228,12 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
globalServerConfig = srvCfg globalServerConfig = srvCfg
globalServerConfigMu.Unlock() globalServerConfigMu.Unlock()
// Load logger subsystem
loadLoggers()
// This is only to uniquely identify each gateway deployments.
logger.SetDeploymentID(os.Getenv("MINIO_GATEWAY_DEPLOYMENT_ID"))
var cacheConfig = globalServerConfig.GetCacheConfig() var cacheConfig = globalServerConfig.GetCacheConfig()
if len(cacheConfig.Drives) > 0 { if len(cacheConfig.Drives) > 0 {
var err error var err error

View File

@ -71,6 +71,9 @@ ENVIRONMENT VARIABLES:
MINIO_CACHE_EXPIRY: Cache expiry duration in days. MINIO_CACHE_EXPIRY: Cache expiry duration in days.
MINIO_CACHE_MAXUSE: Maximum permitted usage of the cache in percentage (0-100). MINIO_CACHE_MAXUSE: Maximum permitted usage of the cache in percentage (0-100).
LOGGER:
MINIO_LOGGER_HTTP_ENDPOINT: HTTP endpoint URL to log all incoming requests.
EXAMPLES: EXAMPLES:
1. Start minio gateway server for AWS S3 backend. 1. Start minio gateway server for AWS S3 backend.
$ export MINIO_ACCESS_KEY=accesskey $ export MINIO_ACCESS_KEY=accesskey
@ -82,7 +85,13 @@ EXAMPLES:
$ export MINIO_SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG $ export MINIO_SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
$ {{.HelpName}} https://play.minio.io:9000 $ {{.HelpName}} https://play.minio.io:9000
3. Start minio gateway server for AWS S3 backend with edge caching enabled. 3. Start minio gateway server for AWS S3 backend logging all requests to http endpoint.
$ export MINIO_ACCESS_KEY=Q3AM3UQ867SPQQA43P2F
$ export MINIO_SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
$ export MINIO_LOGGER_HTTP_ENDPOINT="http://localhost:8000/"
$ {{.HelpName}} https://play.minio.io:9000
4. Start minio gateway server for AWS S3 backend with edge caching enabled.
$ export MINIO_ACCESS_KEY=accesskey $ export MINIO_ACCESS_KEY=accesskey
$ export MINIO_SECRET_KEY=secretkey $ export MINIO_SECRET_KEY=secretkey
$ export MINIO_CACHE_DRIVES="/mnt/drive1;/mnt/drive2;/mnt/drive3;/mnt/drive4" $ export MINIO_CACHE_DRIVES="/mnt/drive1;/mnt/drive2;/mnt/drive3;/mnt/drive4"
@ -401,6 +410,9 @@ func (l *s3Objects) PutObject(ctx context.Context, bucket string, object string,
if err != nil { if err != nil {
return objInfo, minio.ErrorRespToObjectError(err, bucket, object) return objInfo, minio.ErrorRespToObjectError(err, bucket, object)
} }
// On success, populate the key & metadata so they are present in the notification
oi.Key = object
oi.Metadata = minio.ToMinioClientObjectInfoMetadata(metadata)
return minio.FromMinioClientObjectInfo(bucket, oi), nil return minio.FromMinioClientObjectInfo(bucket, oi), nil
} }

View File

@ -27,7 +27,11 @@ import (
// in plain or json format to the standard output. // in plain or json format to the standard output.
type ConsoleTarget struct{} type ConsoleTarget struct{}
func (c *ConsoleTarget) send(entry logEntry) error { func (c *ConsoleTarget) send(e interface{}) error {
entry, ok := e.(logEntry)
if !ok {
return fmt.Errorf("Uexpected log entry structure %#v", e)
}
if jsonFlag { if jsonFlag {
logJSON, err := json.Marshal(&entry) logJSON, err := json.Marshal(&entry)
if err != nil { if err != nil {

View File

@ -32,7 +32,8 @@ import (
// is returned to the caller. // is returned to the caller.
type HTTPTarget struct { type HTTPTarget struct {
// Channel of log entries // Channel of log entries
logCh chan logEntry logCh chan interface{}
// HTTP(s) endpoint // HTTP(s) endpoint
endpoint string endpoint string
client http.Client client http.Client
@ -72,14 +73,14 @@ func NewHTTP(endpoint string, transport *http.Transport) LoggingTarget {
client: http.Client{ client: http.Client{
Transport: transport, Transport: transport,
}, },
logCh: make(chan logEntry, 10000), logCh: make(chan interface{}, 10000),
} }
h.startHTTPLogger() h.startHTTPLogger()
return &h return &h
} }
func (h *HTTPTarget) send(entry logEntry) error { func (h *HTTPTarget) send(entry interface{}) error {
select { select {
case h.logCh <- entry: case h.logCh <- entry:
default: default:

View File

@ -21,6 +21,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"go/build" "go/build"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -341,6 +342,55 @@ func logIf(ctx context.Context, err error) {
} }
} }
type auditEntry struct {
DeploymentID string `json:"deploymentid,omitempty"`
Time string `json:"time"`
API *api `json:"api,omitempty"`
RemoteHost string `json:"remotehost,omitempty"`
RequestID string `json:"requestID,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// AuditLog - logs audit logs to all targets.
func AuditLog(ctx context.Context, r *http.Request) {
if Disable {
return
}
req := GetReqInfo(ctx)
if req == nil {
req = &ReqInfo{API: "SYSTEM"}
}
API := "SYSTEM"
if req.API != "" {
API = req.API
}
tags := make(map[string]string)
for _, entry := range req.GetTags() {
tags[entry.Key] = entry.Val
}
entry := auditEntry{
DeploymentID: deploymentID,
RemoteHost: req.RemoteHost,
RequestID: req.RequestID,
UserAgent: req.UserAgent,
Time: time.Now().UTC().Format(time.RFC3339Nano),
API: &api{Name: API, Args: &args{Bucket: req.BucketName, Object: req.ObjectName}},
Metadata: tags,
}
// Send audit logs only to http targets.
for _, t := range Targets {
if _, ok := t.(*HTTPTarget); ok {
t.send(entry)
}
}
}
// ErrCritical is the value panic'd whenever CriticalIf is called. // ErrCritical is the value panic'd whenever CriticalIf is called.
var ErrCritical struct{} var ErrCritical struct{}
@ -450,10 +500,9 @@ func (f fatalMsg) pretty(msg string, args ...interface{}) {
os.Exit(1) os.Exit(1)
} }
var info infoMsg type infoMsg struct{}
type infoMsg struct { var info infoMsg
}
func (i infoMsg) json(msg string, args ...interface{}) { func (i infoMsg) json(msg string, args ...interface{}) {
logJSON, err := json.Marshal(&logEntry{ logJSON, err := json.Marshal(&logEntry{

View File

@ -20,7 +20,7 @@ package logger
// a single log entry and send it to the log target // a single log entry and send it to the log target
// e.g. send the log to a http server // e.g. send the log to a http server
type LoggingTarget interface { type LoggingTarget interface {
send(entry logEntry) error send(entry interface{}) error
} }
// Targets is the set of enabled loggers // Targets is the set of enabled loggers

View File

@ -448,6 +448,7 @@ func (sys *NotificationSys) Send(args eventArgs) []event.TargetIDErr {
sys.RLock() sys.RLock()
targetIDSet := sys.bucketRulesMap[args.BucketName].Match(args.EventName, args.Object.Name) targetIDSet := sys.bucketRulesMap[args.BucketName].Match(args.EventName, args.Object.Name)
sys.RUnlock() sys.RUnlock()
if len(targetIDSet) == 0 { if len(targetIDSet) == 0 {
return nil return nil
} }

View File

@ -78,6 +78,8 @@ func setHeadGetRespHeaders(w http.ResponseWriter, reqParams url.Values) {
func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "SelectObject") ctx := newContext(r, w, "SelectObject")
defer logger.AuditLog(ctx, r)
// Fetch object stat info. // Fetch object stat info.
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
@ -273,6 +275,12 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
} }
} }
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
// GetObjectHandler - GET Object // GetObjectHandler - GET Object
@ -282,6 +290,8 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetObject") ctx := newContext(r, w, "GetObject")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -437,6 +447,12 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
Host: host, Host: host,
Port: port, Port: port,
}) })
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
// HeadObjectHandler - HEAD Object // HeadObjectHandler - HEAD Object
@ -445,6 +461,8 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "HeadObject") ctx := newContext(r, w, "HeadObject")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponseHeadersOnly(w, ErrServerNotInitialized) writeErrorResponseHeadersOnly(w, ErrServerNotInitialized)
@ -578,6 +596,12 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
Host: host, Host: host,
Port: port, Port: port,
}) })
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
// Extract metadata relevant for an CopyObject operation based on conditional // Extract metadata relevant for an CopyObject operation based on conditional
@ -618,6 +642,8 @@ func getCpObjMetadataFromHeader(ctx context.Context, r *http.Request, userMeta m
func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "CopyObject") ctx := newContext(r, w, "CopyObject")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -951,6 +977,12 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
Host: host, Host: host,
Port: port, Port: port,
}) })
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
// PutObjectHandler - PUT Object // PutObjectHandler - PUT Object
@ -964,6 +996,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutObject") ctx := newContext(r, w, "PutObject")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -1203,6 +1237,12 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
Host: host, Host: host,
Port: port, Port: port,
}) })
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
/// Multipart objectAPIHandlers /// Multipart objectAPIHandlers
@ -1216,6 +1256,8 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "NewMultipartUpload") ctx := newContext(r, w, "NewMultipartUpload")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -1308,6 +1350,8 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "CopyObjectPart") ctx := newContext(r, w, "CopyObjectPart")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -1530,6 +1574,8 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutObjectPart") ctx := newContext(r, w, "PutObjectPart")
defer logger.AuditLog(ctx, r)
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
@ -1770,6 +1816,8 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "AbortMultipartUpload") ctx := newContext(r, w, "AbortMultipartUpload")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -1809,6 +1857,8 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter,
func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListObjectParts") ctx := newContext(r, w, "ListObjectParts")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -1849,6 +1899,8 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "CompleteMultipartUpload") ctx := newContext(r, w, "CompleteMultipartUpload")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -1955,6 +2007,12 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
Host: host, Host: host,
Port: port, Port: port,
}) })
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
/// Delete objectAPIHandlers /// Delete objectAPIHandlers
@ -1963,6 +2021,8 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "DeleteObject") ctx := newContext(r, w, "DeleteObject")
defer logger.AuditLog(ctx, r)
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]

View File

@ -391,7 +391,7 @@ func newContext(r *http.Request, w http.ResponseWriter, api string) context.Cont
reqInfo := &logger.ReqInfo{ reqInfo := &logger.ReqInfo{
RequestID: w.Header().Get(responseRequestIDKey), RequestID: w.Header().Get(responseRequestIDKey),
RemoteHost: handlers.GetSourceIP(r), RemoteHost: handlers.GetSourceIP(r),
UserAgent: r.Header.Get("user-agent"), UserAgent: r.UserAgent(),
API: api, API: api,
BucketName: bucket, BucketName: bucket,
ObjectName: object, ObjectName: object,

View File

@ -640,6 +640,10 @@ func (web *webAPIHandlers) CreateURLToken(r *http.Request, args *WebGenericArgs,
// Upload - file upload handler. // Upload - file upload handler.
func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "WebUpload")
defer logger.AuditLog(ctx, r)
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeWebErrorResponse(w, errServerNotInitialized) writeWebErrorResponse(w, errServerNotInitialized)
@ -741,13 +745,13 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
opts := ObjectOptions{} opts := ObjectOptions{}
// Deny if WORM is enabled // Deny if WORM is enabled
if globalWORMEnabled { if globalWORMEnabled {
if _, err = objectAPI.GetObjectInfo(context.Background(), bucket, object, opts); err == nil { if _, err = objectAPI.GetObjectInfo(ctx, bucket, object, opts); err == nil {
writeWebErrorResponse(w, errMethodNotAllowed) writeWebErrorResponse(w, errMethodNotAllowed)
return return
} }
} }
objInfo, err := putObject(context.Background(), bucket, object, hashReader, metadata, opts) objInfo, err := putObject(ctx, bucket, object, hashReader, metadata, opts)
if err != nil { if err != nil {
writeWebErrorResponse(w, err) writeWebErrorResponse(w, err)
return return
@ -769,10 +773,20 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
Host: host, Host: host,
Port: port, Port: port,
}) })
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
// Download - file download handler. // Download - file download handler.
func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) { func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "WebDownload")
defer logger.AuditLog(ctx, r)
var wg sync.WaitGroup var wg sync.WaitGroup
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
@ -827,7 +841,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
getObjectInfo = web.CacheAPI().GetObjectInfo getObjectInfo = web.CacheAPI().GetObjectInfo
getObject = web.CacheAPI().GetObject getObject = web.CacheAPI().GetObject
} }
objInfo, err := getObjectInfo(context.Background(), bucket, object, opts) objInfo, err := getObjectInfo(ctx, bucket, object, opts)
if err != nil { if err != nil {
writeWebErrorResponse(w, err) writeWebErrorResponse(w, err)
return return
@ -897,7 +911,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
// Add content disposition. // Add content disposition.
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", path.Base(object))) w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", path.Base(object)))
if err = getObject(context.Background(), bucket, object, 0, -1, httpWriter, "", opts); err != nil { if err = getObject(ctx, bucket, object, 0, -1, httpWriter, "", opts); err != nil {
httpWriter.Close() httpWriter.Close()
if objInfo.IsCompressed() { if objInfo.IsCompressed() {
wg.Wait() wg.Wait()
@ -933,6 +947,12 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
Host: host, Host: host,
Port: port, Port: port,
}) })
for k, v := range objInfo.UserDefined {
logger.GetReqInfo(ctx).SetTags(k, v)
}
logger.GetReqInfo(ctx).SetTags("etag", objInfo.ETag)
} }
// DownloadZipArgs - Argument for downloading a bunch of files as a zip file. // DownloadZipArgs - Argument for downloading a bunch of files as a zip file.
@ -952,6 +972,10 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
host, port = "", "" host, port = "", ""
} }
ctx := newContext(r, w, "WebDownloadZip")
defer logger.AuditLog(ctx, r)
var wg sync.WaitGroup var wg sync.WaitGroup
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
@ -1030,7 +1054,7 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
for _, object := range args.Objects { for _, object := range args.Objects {
// Writes compressed object file to the response. // Writes compressed object file to the response.
zipit := func(objectName string) error { zipit := func(objectName string) error {
info, err := getObjectInfo(context.Background(), args.BucketName, objectName, opts) info, err := getObjectInfo(ctx, args.BucketName, objectName, opts)
if err != nil { if err != nil {
return err return err
} }
@ -1103,7 +1127,7 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
} }
} }
httpWriter := ioutil.WriteOnClose(writer) httpWriter := ioutil.WriteOnClose(writer)
if err = getObject(context.Background(), args.BucketName, objectName, 0, length, httpWriter, "", opts); err != nil { if err = getObject(ctx, args.BucketName, objectName, 0, length, httpWriter, "", opts); err != nil {
httpWriter.Close() httpWriter.Close()
if info.IsCompressed() { if info.IsCompressed() {
// Wait for decompression go-routine to retire. // Wait for decompression go-routine to retire.