mirror of
https://github.com/minio/minio.git
synced 2025-04-16 00:49:09 -04:00
ensure authenticated request bodies for Admin-API (#5984)
This commit adds a check to the server's admin-API such that it only accepts Admin-API requests with authenticated bodies. Further this commit updates the `madmin` package to always add the `X-Amz-Content-Sha256` header. This change improves the Admin-API security since the server does not accept unauthenticated request bodies anymore. After this commit `mc` must be updated to the new `madmin` api because requests over TLS connections will fail.
This commit is contained in:
parent
5282639f3c
commit
9fb94e6aa8
@ -114,7 +114,7 @@ func getRequestAuthType(r *http.Request) authType {
|
|||||||
// It does not accept presigned or JWT or anonymous requests.
|
// It does not accept presigned or JWT or anonymous requests.
|
||||||
func checkAdminRequestAuthType(r *http.Request, region string) APIErrorCode {
|
func checkAdminRequestAuthType(r *http.Request, region string) APIErrorCode {
|
||||||
s3Err := ErrAccessDenied
|
s3Err := ErrAccessDenied
|
||||||
if getRequestAuthType(r) == authTypeSigned { // we only support V4 (no presign)
|
if _, ok := r.Header["X-Amz-Content-Sha256"]; ok && getRequestAuthType(r) == authTypeSigned && !skipContentSha256Cksum(r) { // we only support V4 (no presign) with auth. body
|
||||||
s3Err = isReqAuthenticated(r, region)
|
s3Err = isReqAuthenticated(r, region)
|
||||||
}
|
}
|
||||||
if s3Err != ErrNone {
|
if s3Err != ErrNone {
|
||||||
|
@ -18,7 +18,6 @@ package madmin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -177,14 +176,8 @@ func (c *AdminClient) TraceOff() {
|
|||||||
type requestData struct {
|
type requestData struct {
|
||||||
customHeaders http.Header
|
customHeaders http.Header
|
||||||
queryValues url.Values
|
queryValues url.Values
|
||||||
|
relPath string // Url path relative to admin API base endpoint
|
||||||
// Url path relative to admin API base endpoint
|
content []byte
|
||||||
relPath string
|
|
||||||
|
|
||||||
contentBody io.Reader
|
|
||||||
contentLength int64
|
|
||||||
contentSHA256Bytes []byte
|
|
||||||
contentMD5Bytes []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out signature value from Authorization header.
|
// Filter out signature value from Authorization header.
|
||||||
@ -404,43 +397,17 @@ func (c AdminClient) newRequest(method string, reqData requestData) (req *http.R
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set content body if available.
|
|
||||||
if reqData.contentBody != nil {
|
|
||||||
req.Body = ioutil.NopCloser(reqData.contentBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set 'User-Agent' header for the request.
|
|
||||||
c.setUserAgent(req)
|
c.setUserAgent(req)
|
||||||
|
|
||||||
// Set all headers.
|
|
||||||
for k, v := range reqData.customHeaders {
|
for k, v := range reqData.customHeaders {
|
||||||
req.Header.Set(k, v[0])
|
req.Header.Set(k, v[0])
|
||||||
}
|
}
|
||||||
|
if length := len(reqData.content); length > 0 {
|
||||||
// set incoming content-length.
|
req.ContentLength = int64(length)
|
||||||
if reqData.contentLength > 0 {
|
|
||||||
req.ContentLength = reqData.contentLength
|
|
||||||
}
|
}
|
||||||
|
req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum256(reqData.content)))
|
||||||
|
req.Body = ioutil.NopCloser(bytes.NewReader(reqData.content))
|
||||||
|
|
||||||
shaHeader := unsignedPayload
|
|
||||||
if !c.secure {
|
|
||||||
if reqData.contentSHA256Bytes == nil {
|
|
||||||
shaHeader = hex.EncodeToString(sum256([]byte{}))
|
|
||||||
} else {
|
|
||||||
shaHeader = hex.EncodeToString(reqData.contentSHA256Bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Amz-Content-Sha256", shaHeader)
|
|
||||||
|
|
||||||
// set md5Sum for content protection.
|
|
||||||
if reqData.contentMD5Bytes != nil {
|
|
||||||
req.Header.Set("Content-Md5", base64.StdEncoding.EncodeToString(reqData.contentMD5Bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add signature version '4' authorization header.
|
|
||||||
req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "", location)
|
req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "", location)
|
||||||
|
|
||||||
// Return request.
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package madmin
|
package madmin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -66,8 +65,7 @@ func (adm *AdminClient) GetConfig() ([]byte, error) {
|
|||||||
|
|
||||||
// SetConfig - set config supplied as config.json for the setup.
|
// SetConfig - set config supplied as config.json for the setup.
|
||||||
func (adm *AdminClient) SetConfig(config io.Reader) (r SetConfigResult, err error) {
|
func (adm *AdminClient) SetConfig(config io.Reader) (r SetConfigResult, err error) {
|
||||||
// No TLS?
|
if !adm.secure { // No TLS?
|
||||||
if !adm.secure {
|
|
||||||
return r, fmt.Errorf("credentials/configuration cannot be updated over an insecure connection")
|
return r, fmt.Errorf("credentials/configuration cannot be updated over an insecure connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,10 +76,8 @@ func (adm *AdminClient) SetConfig(config io.Reader) (r SetConfigResult, err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
reqData := requestData{
|
reqData := requestData{
|
||||||
relPath: "/v1/config",
|
relPath: "/v1/config",
|
||||||
contentBody: bytes.NewReader(configBytes),
|
content: configBytes,
|
||||||
contentMD5Bytes: sumMD5(configBytes),
|
|
||||||
contentSHA256Bytes: sum256(configBytes),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute PUT on /minio/admin/v1/config to set config.
|
// Execute PUT on /minio/admin/v1/config to set config.
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package madmin
|
package madmin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -46,11 +45,8 @@ func (adm *AdminClient) SetCredentials(access, secret string) error {
|
|||||||
|
|
||||||
// Setup new request
|
// Setup new request
|
||||||
reqData := requestData{
|
reqData := requestData{
|
||||||
relPath: "/v1/config/credential",
|
relPath: "/v1/config/credential",
|
||||||
contentBody: bytes.NewReader(body),
|
content: body,
|
||||||
contentLength: int64(len(body)),
|
|
||||||
contentMD5Bytes: sumMD5(body),
|
|
||||||
contentSHA256Bytes: sum256(body),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute GET on bucket to list objects.
|
// Execute GET on bucket to list objects.
|
||||||
|
@ -18,10 +18,8 @@
|
|||||||
package madmin
|
package madmin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -194,23 +192,18 @@ func (adm *AdminClient) Heal(bucket, prefix string, healOpts HealOpts,
|
|||||||
|
|
||||||
// execute POST request to heal api
|
// execute POST request to heal api
|
||||||
queryVals := make(url.Values)
|
queryVals := make(url.Values)
|
||||||
var contentBody io.Reader
|
|
||||||
if clientToken != "" {
|
if clientToken != "" {
|
||||||
queryVals.Set("clientToken", clientToken)
|
queryVals.Set("clientToken", clientToken)
|
||||||
body = []byte{}
|
body = []byte{}
|
||||||
} else {
|
|
||||||
// Set a body only if clientToken is not given
|
|
||||||
contentBody = bytes.NewReader(body)
|
|
||||||
}
|
}
|
||||||
if forceStart {
|
if forceStart {
|
||||||
queryVals.Set("forceStart", "true")
|
queryVals.Set("forceStart", "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := adm.executeMethod("POST", requestData{
|
resp, err := adm.executeMethod("POST", requestData{
|
||||||
relPath: path,
|
relPath: path,
|
||||||
contentBody: contentBody,
|
content: body,
|
||||||
contentSHA256Bytes: sum256(body),
|
queryValues: queryVals,
|
||||||
queryValues: queryVals,
|
|
||||||
})
|
})
|
||||||
defer closeResponse(resp)
|
defer closeResponse(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package madmin
|
package madmin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -87,9 +86,8 @@ func (adm *AdminClient) ServiceSendAction(action ServiceActionValue) error {
|
|||||||
|
|
||||||
// Request API to Restart server
|
// Request API to Restart server
|
||||||
resp, err := adm.executeMethod("POST", requestData{
|
resp, err := adm.executeMethod("POST", requestData{
|
||||||
relPath: "/v1/service",
|
relPath: "/v1/service",
|
||||||
contentBody: bytes.NewReader(body),
|
content: body,
|
||||||
contentSHA256Bytes: sum256(body),
|
|
||||||
})
|
})
|
||||||
defer closeResponse(resp)
|
defer closeResponse(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user