|
@ -1,6 +1,7 @@ |
|
|
package s3err |
|
|
package s3err |
|
|
|
|
|
|
|
|
import ( |
|
|
import ( |
|
|
|
|
|
"encoding/json" |
|
|
"fmt" |
|
|
"fmt" |
|
|
"github.com/chrislusf/seaweedfs/weed/glog" |
|
|
"github.com/chrislusf/seaweedfs/weed/glog" |
|
|
xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http" |
|
|
xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http" |
|
@ -16,28 +17,28 @@ type AccessLogExtend struct { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
type AccessLog struct { |
|
|
type AccessLog struct { |
|
|
Bucket string `json:"bucket"` // awsexamplebucket1
|
|
|
|
|
|
Time time.Time `json:"time"` // [06/Feb/2019:00:00:38 +0000]
|
|
|
|
|
|
RemoteIP string `json:"remote_ip,omitempty"` // 192.0.2.3
|
|
|
|
|
|
Requester string `json:"requester,omitempty"` // IAM user id
|
|
|
|
|
|
RequestID string `json:"request_id,omitempty"` // 3E57427F33A59F07
|
|
|
|
|
|
Operation string `json:"operation,omitempty"` // REST.HTTP_method.resource_type REST.PUT.OBJECT
|
|
|
|
|
|
Key string `json:"Key,omitempty"` // /photos/2019/08/puppy.jpg
|
|
|
|
|
|
ErrorCode string `json:"error_code,omitempty"` |
|
|
|
|
|
HostId string `json:"host_id,omitempty"` |
|
|
|
|
|
HostHeader string `json:"host_header,omitempty"` // s3.us-west-2.amazonaws.com
|
|
|
|
|
|
SignatureVersion string `json:"signature_version,omitempty"` |
|
|
|
|
|
|
|
|
Bucket string `msg:"bucket" json:"bucket"` // awsexamplebucket1
|
|
|
|
|
|
Time int64 `msg:"time" json:"time"` // [06/Feb/2019:00:00:38 +0000]
|
|
|
|
|
|
RemoteIP string `msg:"remote_ip" json:"remote_ip,omitempty"` // 192.0.2.3
|
|
|
|
|
|
Requester string `msg:"requester" json:"requester,omitempty"` // IAM user id
|
|
|
|
|
|
RequestID string `msg:"request_id" json:"request_id,omitempty"` // 3E57427F33A59F07
|
|
|
|
|
|
Operation string `msg:"operation" json:"operation,omitempty"` // REST.HTTP_method.resource_type REST.PUT.OBJECT
|
|
|
|
|
|
Key string `msg:"key" json:"key,omitempty"` // /photos/2019/08/puppy.jpg
|
|
|
|
|
|
ErrorCode string `msg:"error_code" json:"error_code,omitempty"` |
|
|
|
|
|
HostId string `msg:"host_id" json:"host_id,omitempty"` |
|
|
|
|
|
HostHeader string `msg:"host_header" json:"host_header,omitempty"` // s3.us-west-2.amazonaws.com
|
|
|
|
|
|
UserAgent string `msg:"user_agent" json:"user_agent,omitempty"` |
|
|
|
|
|
HTTPStatus int `msg:"status" json:"status,omitempty"` |
|
|
|
|
|
SignatureVersion string `msg:"signature_version" json:"signature_version,omitempty"` |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
type AccessLogHTTP struct { |
|
|
type AccessLogHTTP struct { |
|
|
RequestURI string `json:"request_uri,omitempty"` // "GET /awsexamplebucket1/photos/2019/08/puppy.jpg?x-foo=bar HTTP/1.1"
|
|
|
RequestURI string `json:"request_uri,omitempty"` // "GET /awsexamplebucket1/photos/2019/08/puppy.jpg?x-foo=bar HTTP/1.1"
|
|
|
HTTPStatus int `json:"HTTP_status,omitempty"` |
|
|
|
|
|
BytesSent string `json:"bytes_sent,omitempty"` |
|
|
BytesSent string `json:"bytes_sent,omitempty"` |
|
|
ObjectSize string `json:"object_size,omitempty"` |
|
|
ObjectSize string `json:"object_size,omitempty"` |
|
|
TotalTime time.Duration `json:"total_time,omitempty"` |
|
|
|
|
|
TurnAroundTime time.Duration `json:"turn_around_time,omitempty"` |
|
|
|
|
|
|
|
|
TotalTime int `json:"total_time,omitempty"` |
|
|
|
|
|
TurnAroundTime int `json:"turn_around_time,omitempty"` |
|
|
Referer string `json:"Referer,omitempty"` |
|
|
Referer string `json:"Referer,omitempty"` |
|
|
UserAgent string `json:"user_agent,omitempty"` |
|
|
|
|
|
VersionId string `json:"version_id,omitempty"` |
|
|
VersionId string `json:"version_id,omitempty"` |
|
|
CipherSuite string `json:"cipher_suite,omitempty"` |
|
|
CipherSuite string `json:"cipher_suite,omitempty"` |
|
|
AuthenticationType string `json:"auth_type,omitempty"` |
|
|
AuthenticationType string `json:"auth_type,omitempty"` |
|
@ -47,14 +48,21 @@ type AccessLogHTTP struct { |
|
|
const tag = "s3.access" |
|
|
const tag = "s3.access" |
|
|
|
|
|
|
|
|
var ( |
|
|
var ( |
|
|
logger *fluent.Fluent |
|
|
|
|
|
|
|
|
Logger *fluent.Fluent |
|
|
hostname = os.Getenv("HOSTNAME") |
|
|
hostname = os.Getenv("HOSTNAME") |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
func init() { |
|
|
|
|
|
|
|
|
func InitAuditLog(config string) { |
|
|
|
|
|
configContent, readErr := os.ReadFile(config) |
|
|
|
|
|
if readErr != nil { |
|
|
|
|
|
glog.Fatalf("fail to read fluent config %s : %v", config, readErr) |
|
|
|
|
|
} |
|
|
|
|
|
var fluentConfig fluent.Config |
|
|
|
|
|
if err := json.Unmarshal(configContent, &fluentConfig); err != nil { |
|
|
|
|
|
glog.Fatalf("fail to parse fluent config %s : %v", config, err) |
|
|
|
|
|
} |
|
|
var err error |
|
|
var err error |
|
|
|
|
|
|
|
|
logger, err = fluent.New(fluent.Config{}) |
|
|
|
|
|
|
|
|
Logger, err = fluent.New(fluentConfig) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
glog.Fatalf("fail to load fluent config: %v", err) |
|
|
glog.Fatalf("fail to load fluent config: %v", err) |
|
|
} |
|
|
} |
|
@ -64,16 +72,9 @@ func getREST(httpMetod string, resourceType string) string { |
|
|
return fmt.Sprintf("REST.%s.%s", httpMetod, resourceType) |
|
|
return fmt.Sprintf("REST.%s.%s", httpMetod, resourceType) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func getResourceType(object string, query string, metod string) (string, bool) { |
|
|
|
|
|
if len(object) > 0 { |
|
|
|
|
|
switch query { |
|
|
|
|
|
case "tagging": |
|
|
|
|
|
return getREST(metod, "OBJECTTAGGING"), true |
|
|
|
|
|
default: |
|
|
|
|
|
return getREST(metod, "OBJECT"), false |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
switch query { |
|
|
|
|
|
|
|
|
func getResourceType(object string, query_key string, metod string) (string, bool) { |
|
|
|
|
|
if object == "/" { |
|
|
|
|
|
switch query_key { |
|
|
case "delete": |
|
|
case "delete": |
|
|
return "BATCH.DELETE.OBJECT", true |
|
|
return "BATCH.DELETE.OBJECT", true |
|
|
case "tagging": |
|
|
case "tagging": |
|
@ -87,6 +88,13 @@ func getResourceType(object string, query string, metod string) (string, bool) { |
|
|
default: |
|
|
default: |
|
|
return getREST(metod, "BUCKET"), false |
|
|
return getREST(metod, "BUCKET"), false |
|
|
} |
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
switch query_key { |
|
|
|
|
|
case "tagging": |
|
|
|
|
|
return getREST(metod, "OBJECTTAGGING"), true |
|
|
|
|
|
default: |
|
|
|
|
|
return getREST(metod, "OBJECT"), false |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -94,40 +102,69 @@ func getOperation(object string, r *http.Request) string { |
|
|
queries := r.URL.Query() |
|
|
queries := r.URL.Query() |
|
|
var operation string |
|
|
var operation string |
|
|
var queryFound bool |
|
|
var queryFound bool |
|
|
for query, _ := range queries { |
|
|
|
|
|
if operation, queryFound = getResourceType(object, query, r.Method); queryFound { |
|
|
|
|
|
|
|
|
for key, _ := range queries { |
|
|
|
|
|
operation, queryFound = getResourceType(object, key, r.Method) |
|
|
|
|
|
if queryFound { |
|
|
return operation |
|
|
return operation |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if len(queries) == 0 { |
|
|
|
|
|
operation, _ = getResourceType(object, "", r.Method) |
|
|
|
|
|
} |
|
|
return operation |
|
|
return operation |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func GetAccessLog(r *http.Request, s3errCode ErrorCode) AccessLog { |
|
|
|
|
|
|
|
|
func GetAccessHttpLog(r *http.Request, statusCode int, s3errCode ErrorCode) AccessLogHTTP { |
|
|
|
|
|
return AccessLogHTTP{ |
|
|
|
|
|
RequestURI: r.RequestURI, |
|
|
|
|
|
Referer: r.Header.Get("Referer"), |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func GetAccessLog(r *http.Request, HTTPStatusCode int, s3errCode ErrorCode) *AccessLog { |
|
|
bucket, key := xhttp.GetBucketAndObject(r) |
|
|
bucket, key := xhttp.GetBucketAndObject(r) |
|
|
var errorCode string |
|
|
var errorCode string |
|
|
if s3errCode != ErrNone { |
|
|
if s3errCode != ErrNone { |
|
|
errorCode = GetAPIError(s3errCode).Code |
|
|
errorCode = GetAPIError(s3errCode).Code |
|
|
} |
|
|
} |
|
|
return AccessLog{ |
|
|
|
|
|
HostHeader: r.Header.Get("Host"), |
|
|
|
|
|
|
|
|
remoteIP := r.Header.Get("X-Real-IP") |
|
|
|
|
|
if len(remoteIP) == 0 { |
|
|
|
|
|
remoteIP = r.RemoteAddr |
|
|
|
|
|
} |
|
|
|
|
|
hostHeader := r.Header.Get("Host") |
|
|
|
|
|
if len(hostHeader) == 0 { |
|
|
|
|
|
hostHeader = r.URL.Hostname() |
|
|
|
|
|
} |
|
|
|
|
|
return &AccessLog{ |
|
|
|
|
|
HostHeader: hostHeader, |
|
|
RequestID: r.Header.Get("X-Request-ID"), |
|
|
RequestID: r.Header.Get("X-Request-ID"), |
|
|
RemoteIP: r.Header.Get("X-Real-IP"), |
|
|
|
|
|
|
|
|
RemoteIP: remoteIP, |
|
|
Requester: r.Header.Get(xhttp.AmzIdentityId), |
|
|
Requester: r.Header.Get(xhttp.AmzIdentityId), |
|
|
|
|
|
UserAgent: r.Header.Get("UserAgent"), |
|
|
HostId: hostname, |
|
|
HostId: hostname, |
|
|
Bucket: bucket, |
|
|
Bucket: bucket, |
|
|
Time: time.Now(), |
|
|
|
|
|
|
|
|
HTTPStatus: HTTPStatusCode, |
|
|
|
|
|
Time: time.Now().Unix(), |
|
|
Key: key, |
|
|
Key: key, |
|
|
Operation: getOperation(key, r), |
|
|
Operation: getOperation(key, r), |
|
|
ErrorCode: errorCode, |
|
|
ErrorCode: errorCode, |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func PostLog(r *http.Request, errorCode ErrorCode) { |
|
|
|
|
|
if logger == nil { |
|
|
|
|
|
|
|
|
func PostLog(r *http.Request, HTTPStatusCode int, errorCode ErrorCode) { |
|
|
|
|
|
if Logger == nil { |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
err := logger.Post(tag, GetAccessLog(r, errorCode)) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
glog.Error("Error while posting log: ", err) |
|
|
|
|
|
|
|
|
if err := Logger.Post(tag, *GetAccessLog(r, HTTPStatusCode, errorCode)); err != nil { |
|
|
|
|
|
glog.Warning("Error while posting log: ", err) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func PostAccessLog(log *AccessLog) { |
|
|
|
|
|
if Logger == nil || log == nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
if err := Logger.Post(tag, *log); err != nil { |
|
|
|
|
|
glog.Warning("Error while posting log: ", err) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |