You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							183 lines
						
					
					
						
							5.7 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							183 lines
						
					
					
						
							5.7 KiB
						
					
					
				| package s3err | |
| 
 | |
| import ( | |
| 	"encoding/json" | |
| 	"fmt" | |
| 	"github.com/fluent/fluent-logger-golang/fluent" | |
| 	"github.com/seaweedfs/seaweedfs/weed/glog" | |
| 	"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" | |
| 	"net/http" | |
| 	"os" | |
| 	"time" | |
| ) | |
| 
 | |
| type AccessLogExtend struct { | |
| 	AccessLog | |
| 	AccessLogHTTP | |
| } | |
| 
 | |
| type AccessLog struct { | |
| 	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 { | |
| 	RequestURI         string `json:"request_uri,omitempty"` // "GET /awsexamplebucket1/photos/2019/08/puppy.jpg?x-foo=bar HTTP/1.1" | |
| 	BytesSent          string `json:"bytes_sent,omitempty"` | |
| 	ObjectSize         string `json:"object_size,omitempty"` | |
| 	TotalTime          int    `json:"total_time,omitempty"` | |
| 	TurnAroundTime     int    `json:"turn_around_time,omitempty"` | |
| 	Referer            string `json:"Referer,omitempty"` | |
| 	VersionId          string `json:"version_id,omitempty"` | |
| 	CipherSuite        string `json:"cipher_suite,omitempty"` | |
| 	AuthenticationType string `json:"auth_type,omitempty"` | |
| 	TLSVersion         string `json:"TLS_version,omitempty"` | |
| } | |
| 
 | |
| const tag = "s3.access" | |
| 
 | |
| var ( | |
| 	Logger      *fluent.Fluent | |
| 	hostname    = os.Getenv("HOSTNAME") | |
| 	environment = os.Getenv("ENVIRONMENT") | |
| ) | |
| 
 | |
| func InitAuditLog(config string) { | |
| 	configContent, readErr := os.ReadFile(config) | |
| 	if readErr != nil { | |
| 		glog.Errorf("fail to read fluent config %s : %v", config, readErr) | |
| 		return | |
| 	} | |
| 	fluentConfig := &fluent.Config{} | |
| 	if err := json.Unmarshal(configContent, fluentConfig); err != nil { | |
| 		glog.Errorf("fail to parse fluent config %s : %v", string(configContent), err) | |
| 		return | |
| 	} | |
| 	if len(fluentConfig.TagPrefix) == 0 && len(environment) > 0 { | |
| 		fluentConfig.TagPrefix = environment | |
| 	} | |
| 	fluentConfig.Async = true | |
| 	fluentConfig.AsyncResultCallback = func(data []byte, err error) { | |
| 		if err != nil { | |
| 			glog.Warning("Error while posting log: ", err) | |
| 		} | |
| 	} | |
| 	var err error | |
| 	Logger, err = fluent.New(*fluentConfig) | |
| 	if err != nil { | |
| 		glog.Errorf("fail to load fluent config: %v", err) | |
| 	} | |
| } | |
| 
 | |
| func getREST(httpMetod string, resourceType string) string { | |
| 	return fmt.Sprintf("REST.%s.%s", httpMetod, resourceType) | |
| } | |
| 
 | |
| func getResourceType(object string, query_key string, metod string) (string, bool) { | |
| 	if object == "/" { | |
| 		switch query_key { | |
| 		case "delete": | |
| 			return "BATCH.DELETE.OBJECT", true | |
| 		case "tagging": | |
| 			return getREST(metod, "OBJECTTAGGING"), true | |
| 		case "lifecycle": | |
| 			return getREST(metod, "LIFECYCLECONFIGURATION"), true | |
| 		case "acl": | |
| 			return getREST(metod, "ACCESSCONTROLPOLICY"), true | |
| 		case "policy": | |
| 			return getREST(metod, "BUCKETPOLICY"), true | |
| 		default: | |
| 			return getREST(metod, "BUCKET"), false | |
| 		} | |
| 	} else { | |
| 		switch query_key { | |
| 		case "tagging": | |
| 			return getREST(metod, "OBJECTTAGGING"), true | |
| 		default: | |
| 			return getREST(metod, "OBJECT"), false | |
| 		} | |
| 	} | |
| } | |
| 
 | |
| func getOperation(object string, r *http.Request) string { | |
| 	queries := r.URL.Query() | |
| 	var operation string | |
| 	var queryFound bool | |
| 	for key, _ := range queries { | |
| 		operation, queryFound = getResourceType(object, key, r.Method) | |
| 		if queryFound { | |
| 			return operation | |
| 		} | |
| 	} | |
| 	if len(queries) == 0 { | |
| 		operation, _ = getResourceType(object, "", r.Method) | |
| 	} | |
| 	return operation | |
| } | |
| 
 | |
| 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 := s3_constants.GetBucketAndObject(r) | |
| 	var errorCode string | |
| 	if s3errCode != ErrNone { | |
| 		errorCode = GetAPIError(s3errCode).Code | |
| 	} | |
| 	remoteIP := r.Header.Get("X-Real-IP") | |
| 	if len(remoteIP) == 0 { | |
| 		remoteIP = r.RemoteAddr | |
| 	} | |
| 	hostHeader := r.Header.Get("X-Forwarded-Host") | |
| 	if len(hostHeader) == 0 { | |
| 		hostHeader = r.Host | |
| 	} | |
| 	return &AccessLog{ | |
| 		HostHeader:       hostHeader, | |
| 		RequestID:        r.Header.Get("X-Request-ID"), | |
| 		RemoteIP:         remoteIP, | |
| 		Requester:        r.Header.Get(s3_constants.AmzIdentityId), | |
| 		SignatureVersion: r.Header.Get(s3_constants.AmzAuthType), | |
| 		UserAgent:        r.Header.Get("user-agent"), | |
| 		HostId:           hostname, | |
| 		Bucket:           bucket, | |
| 		HTTPStatus:       HTTPStatusCode, | |
| 		Time:             time.Now().Unix(), | |
| 		Key:              key, | |
| 		Operation:        getOperation(key, r), | |
| 		ErrorCode:        errorCode, | |
| 	} | |
| } | |
| 
 | |
| func PostLog(r *http.Request, HTTPStatusCode int, errorCode ErrorCode) { | |
| 	if Logger == nil { | |
| 		return | |
| 	} | |
| 	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 || len(log.Key) == 0 { | |
| 		return | |
| 	} | |
| 	if err := Logger.Post(tag, log); err != nil { | |
| 		glog.Warning("Error while posting log: ", err) | |
| 	} | |
| }
 |