Browse Source

set identity to request context

pull/7523/head
chrislu 2 weeks ago
parent
commit
539206bde8
  1. 4
      weed/s3api/auth_credentials.go
  2. 27
      weed/s3api/s3_constants/header.go
  3. 18
      weed/s3api/s3api_bucket_handlers.go
  4. 2
      weed/s3api/s3err/audit_fluent.go
  5. 15
      weed/server/filer_server_handlers_read.go

4
weed/s3api/auth_credentials.go

@ -421,8 +421,10 @@ func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) htt
glog.V(3).Infof("auth error: %v", errCode)
if errCode == s3err.ErrNone {
// Store the authenticated identity in request context (secure, cannot be spoofed)
if identity != nil && identity.Name != "" {
r.Header.Set(s3_constants.AmzIdentityId, identity.Name)
ctx := s3_constants.SetIdentityNameInContext(r.Context(), identity.Name)
r = r.WithContext(ctx)
}
f(w, r)
return

27
weed/s3api/s3_constants/header.go

@ -17,6 +17,7 @@
package s3_constants
import (
"context"
"net/http"
"strings"
@ -174,3 +175,29 @@ var PassThroughHeaders = map[string]string{
func IsSeaweedFSInternalHeader(headerKey string) bool {
return strings.HasPrefix(strings.ToLower(headerKey), SeaweedFSInternalPrefix)
}
// Context keys for storing authenticated identity information
type contextKey string
const (
contextKeyIdentityName contextKey = "s3-identity-name"
)
// SetIdentityNameInContext stores the authenticated identity name in the request context
// This is the secure way to propagate identity - headers can be spoofed, context cannot
func SetIdentityNameInContext(ctx context.Context, identityName string) context.Context {
if identityName != "" {
return context.WithValue(ctx, contextKeyIdentityName, identityName)
}
return ctx
}
// GetIdentityNameFromContext retrieves the authenticated identity name from the request context
// Returns empty string if no identity is set (unauthenticated request)
// This is the secure way to retrieve identity - never read from headers directly
func GetIdentityNameFromContext(r *http.Request) string {
if name, ok := r.Context().Value(contextKeyIdentityName).(string); ok {
return name
}
return ""
}

18
weed/s3api/s3api_bucket_handlers.go

@ -59,12 +59,9 @@ func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Reques
return
}
identityId := ""
if identity != nil {
identityId = identity.Name
}
// Note: For unauthenticated requests, identityId remains empty.
// We never read from request headers to prevent reflecting unvalidated user input.
// Get authenticated identity from context (secure, cannot be spoofed)
// For unauthenticated requests, this returns empty string
identityId := s3_constants.GetIdentityNameFromContext(r)
var listBuckets ListAllMyBucketsList
for _, entry := range entries {
@ -164,7 +161,8 @@ func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request)
}
// Check if bucket already exists and handle ownership/settings
currentIdentityId := r.Header.Get(s3_constants.AmzIdentityId)
// Get authenticated identity from context (secure, cannot be spoofed)
currentIdentityId := s3_constants.GetIdentityNameFromContext(r)
// Check collection existence first
collectionExists := false
@ -247,7 +245,8 @@ func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request)
}
fn := func(entry *filer_pb.Entry) {
if identityId := r.Header.Get(s3_constants.AmzIdentityId); identityId != "" {
// Get authenticated identity from context (secure, cannot be spoofed)
if identityId := s3_constants.GetIdentityNameFromContext(r); identityId != "" {
if entry.Extended == nil {
entry.Extended = make(map[string][]byte)
}
@ -576,7 +575,8 @@ func (s3a *S3ApiServer) hasAccess(r *http.Request, entry *filer_pb.Entry) bool {
return true
}
identityId := r.Header.Get(s3_constants.AmzIdentityId)
// Get authenticated identity from context (secure, cannot be spoofed)
identityId := s3_constants.GetIdentityNameFromContext(r)
if id, ok := entry.Extended[s3_constants.AmzIdentityId]; ok {
if identityId != string(id) {
glog.V(3).Infof("hasAccess: %s != %s (entry.Extended = %v)", identityId, id, entry.Extended)

2
weed/s3api/s3err/audit_fluent.go

@ -152,7 +152,7 @@ func GetAccessLog(r *http.Request, HTTPStatusCode int, s3errCode ErrorCode) *Acc
HostHeader: hostHeader,
RequestID: r.Header.Get("X-Request-ID"),
RemoteIP: remoteIP,
Requester: r.Header.Get(s3_constants.AmzIdentityId),
Requester: s3_constants.GetIdentityNameFromContext(r), // Get from context, not header (secure)
SignatureVersion: r.Header.Get(s3_constants.AmzAuthType),
UserAgent: r.Header.Get("user-agent"),
HostId: hostname,

15
weed/server/filer_server_handlers_read.go

@ -122,18 +122,21 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request)
writeJsonQuiet(w, r, http.StatusOK, entry)
return
}
if entry.Attr.Mime == "" || (entry.Attr.Mime == s3_constants.FolderMimeType && r.Header.Get(s3_constants.AmzIdentityId) == "") {
// Don't return directory meta if config value is set to true
if fs.option.ExposeDirectoryData == false {
// S3 API requests never reach filer HTTP handlers (they use gRPC + direct volume server access).
// For S3-created directories (FolderMimeType), return the directory object metadata itself
// rather than listing contents. This allows S3 clients to GET directory objects they created.
if entry.Attr.Mime == s3_constants.FolderMimeType {
// S3-created directory object - return metadata
w.Header().Set(s3_constants.SeaweedFSIsDirectoryKey, "true")
} else if entry.Attr.Mime == "" {
// Regular filer directory - show listing if enabled
if !fs.option.ExposeDirectoryData {
writeJsonError(w, r, http.StatusForbidden, errors.New("directory listing is disabled"))
return
}
// return index of directory for non s3 gateway
fs.listDirectoryHandler(w, r)
return
}
// inform S3 API this is a user created directory key object
w.Header().Set(s3_constants.SeaweedFSIsDirectoryKey, "true")
}
if isForDirectory && entry.Attr.Mime != s3_constants.FolderMimeType {

Loading…
Cancel
Save