Compare commits

...

7 Commits

Author SHA1 Message Date
Mayuresh Chaubal 64c99ec2ce
Merge 902adf1599 into 534f4a9fb1 2025-10-02 08:30:28 +08:00
yangw 534f4a9fb1
fix: timeN function return final closure not be called (#21615)
VulnCheck / Analysis (push) Has been cancelled Details
2025-09-30 23:06:01 -07:00
jiuker 902adf1599
Merge branch 'master' into feature-sts-san-uri 2025-06-12 10:23:06 +08:00
Mayuresh Chaubal 4ab0449a35 cleaning value of tlsSubKey 2025-06-02 18:02:46 +02:00
Mayuresh Chaubal a0f7fdce4d fixing code comments 2025-06-02 18:02:46 +02:00
Mayuresh Chaubal 5d16e2b4bc added support for podman desktop 2025-06-02 18:02:46 +02:00
Mayuresh Chaubal 5666a30fb0 adding behavior with flag MINIO_IDENTITY_TLS_SUBJECT_USE_SANURI 2025-06-02 18:02:46 +02:00
3 changed files with 105 additions and 21 deletions

View File

@ -106,16 +106,14 @@ func (p *scannerMetrics) log(s scannerMetric, paths ...string) func(custom map[s
// time n scanner actions.
// Use for s < scannerMetricLastRealtime
func (p *scannerMetrics) timeN(s scannerMetric) func(n int) func() {
func (p *scannerMetrics) timeN(s scannerMetric) func(n int) {
startTime := time.Now()
return func(n int) func() {
return func() {
duration := time.Since(startTime)
return func(n int) {
duration := time.Since(startTime)
atomic.AddUint64(&p.operations[s], uint64(n))
if s < scannerMetricLastRealtime {
p.latency[s].add(duration)
}
atomic.AddUint64(&p.operations[s], uint64(n))
if s < scannerMetricLastRealtime {
p.latency[s].add(duration)
}
}
}

View File

@ -783,6 +783,24 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r *
writeSuccessResponseXML(w, encodedSuccessResponse)
}
func extractPolicyName(sanURI string) (string, error) {
parsedURL, err := url.Parse(sanURI)
if err != nil {
return "", err
}
key := parsedURL.Host + strings.ReplaceAll(parsedURL.Path, "/", "+")
if len(key) > 128 {
return "", errors.New("Policy URL " + key + " is more than 128 characters long.")
}
return key, nil
}
// AssumeRoleWithCertificate implements user authentication with client certificates.
// It verifies the client-provided X.509 certificate, maps the certificate to an S3 policy
// and returns temp. S3 credentials to the client.
@ -893,15 +911,44 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h
}
}
// We map the X.509 subject common name to the policy. So, a client
// with the common name "foo" will be associated with the policy "foo".
// Other mapping functions - e.g. public-key hash based mapping - are
// possible but not implemented.
//
// Group mapping is not possible with standard X.509 certificates.
if certificate.Subject.CommonName == "" {
writeSTSErrorResponse(ctx, w, ErrSTSMissingParameter, errors.New("certificate subject CN cannot be empty"))
return
var tlsSubKey string
if !globalIAMSys.STSTLSConfig.TLSSubjectUseSanURI {
// We map the X.509 subject common name to the policy. So, a client
// with the common name "foo" will be associated with the policy "foo".
// Other mapping functions - e.g. public-key hash based mapping - are
// possible but not implemented.
//
// Group mapping is not possible with standard X.509 certificates.
if certificate.Subject.CommonName == "" {
writeSTSErrorResponse(ctx, w, ErrSTSMissingParameter, errors.New("certificate subject CN cannot be empty"))
return
}
tlsSubKey = certificate.Subject.CommonName
} else {
// We map the X.509 san uri to the policy. So, a client
// with the san uri "http://myapp" will be associated with the policy "http://myapp".
// Other mapping functions - e.g. public-key hash based mapping - are
// possible but not implemented.
//
// Group mapping is not possible with standard X.509 certificates.
if len(certificate.URIs) == 0 {
writeSTSErrorResponse(ctx, w, ErrSTSMissingParameter, errors.New("SAN URI not present in the certificate"))
return
}
// Pick first SAN URI
// Extract Policy Name From SAN URI
// Set Policy Name as Subject Key
policyName, err := extractPolicyName(certificate.URIs[0].String())
if err != nil {
writeSTSErrorResponse(ctx, w, ErrSTSInvalidParameterValue, errors.New("Unable to convert from SAN URI to Policy Name"))
return
}
tlsSubKey = policyName
}
expiry, err := globalIAMSys.STSTLSConfig.GetExpiryDuration(r.Form.Get(stsDurationSeconds))
@ -919,13 +966,14 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h
}
// Associate any service accounts to the certificate CN
parentUser := "tls" + getKeySeparator() + certificate.Subject.CommonName
parentUser := "tls" + getKeySeparator() + tlsSubKey
claims[expClaim] = UTCNow().Add(expiry).Unix()
claims[subClaim] = certificate.Subject.CommonName
claims[subClaim] = tlsSubKey
claims[audClaim] = certificate.Subject.Organization
claims[issClaim] = certificate.Issuer.CommonName
claims[parentClaim] = parentUser
tokenRevokeType := r.Form.Get(stsRevokeTokenType)
if tokenRevokeType != "" {
claims[tokenRevokeTypeClaim] = tokenRevokeType
@ -943,7 +991,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h
}
tmpCredentials.ParentUser = parentUser
policyName := certificate.Subject.CommonName
policyName := tlsSubKey
updatedAt, err := globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, policyName)
if err != nil {
writeSTSErrorResponse(ctx, w, ErrSTSInternalError, err)

View File

@ -41,6 +41,22 @@ const (
// clients to obtain temp. credentials with arbitrary policy
// permissions - including admin permissions.
EnvIdentityTLSSkipVerify = "MINIO_IDENTITY_TLS_SKIP_VERIFY"
// EnvIdentityTLSSubjectSanURI is an environmental variable that is used to select
// Subject for verified certificate identity in JWT Claim.
// This claim is sent to Authorization Engine.
// If set to true, First URI will be used as subject instead of CommonName
// The URI will be converted into suitable policy name by following operations
// 1. remove protocol name (or scheme name) from URI
// 2. Replace all Path separators (ie /) from the Path in URI, this results in CleanedPath
// 3. Join Host+CleanedPath
// 4. If the above string becomes greater than 128 characters in length, then
// a proper error is thrown
// As example, http://my.domain:10000/my/app/path will be converted to
// my.domain:10000+my+app+path
// Valid values for this field are true and false
// By default, it will be false. Thus Common Name will be used
EnvIdentityTLSSubjectSanURI = "MINIO_IDENTITY_TLS_SUBJECT_USE_SANURI"
)
// Config contains the STS TLS configuration for generating temp.
@ -52,6 +68,11 @@ type Config struct {
// certificate verification. It should only be set for
// debugging or testing purposes.
InsecureSkipVerify bool `json:"skip_verify"`
// TLSSubjectUseSANUri, if set to true, uses first SAN URI from
// the client certificate as subject. This is done instead of
// using Common Name.
TLSSubjectUseSanURI bool `json:"use_san_uri"`
}
const (
@ -99,11 +120,18 @@ func Lookup(kvs config.KVS) (Config, error) {
if err != nil {
return Config{}, err
}
cfg.TLSSubjectUseSanURI, err = config.ParseBool(env.Get(EnvIdentityTLSSubjectSanURI, kvs.Get(tlsSubjectUseSanURI)))
if err != nil {
return Config{}, err
}
return cfg, nil
}
const (
skipVerify = "skip_verify"
skipVerify = "skip_verify"
tlsSubjectUseSanURI = "tls_subject_use_san_uri"
)
// DefaultKVS is the default K/V config system for
@ -113,6 +141,10 @@ var DefaultKVS = config.KVS{
Key: skipVerify,
Value: "off",
},
config.KV{
Key: tlsSubjectUseSanURI,
Value: "off",
},
}
// Help is the help and description for the STS API K/V configuration.
@ -123,4 +155,10 @@ var Help = config.HelpKVS{
Optional: true,
Type: "on|off",
},
config.HelpKV{
Key: tlsSubjectUseSanURI,
Description: `use cleaned value of first san uri from client certificate instead common name. cleaning results in stripping scheme, replacing path separator with plus sign in uri path and joining it with host name (default: 'off')`,
Optional: true,
Type: "on|off",
},
}