From 3c71c5c80c11ec295d8e845940ea93a948b9a2b9 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 9 Dec 2015 15:38:40 -0800 Subject: [PATCH] s3cmd: Handle support for s3cmd. --- README.md | 25 +++++++++++++++++++++---- api-errors.go | 20 ++++++++++++++------ api-headers.go | 2 ++ generic-handlers.go | 21 +++++++++++++++++++++ routers.go | 3 ++- signature-handler.go | 4 +++- 6 files changed, 63 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b6555559e..3773f274c 100644 --- a/README.md +++ b/README.md @@ -126,11 +126,28 @@ Listening on http://172.30.2.17:9000 Please follow the documentation here - [Using aws-sdk-go with Minio server](./AWS-SDK-GO.md) #### How to use s3cmd with Minio? -
-Even with Signature version '4' enabled, 's3cmd' falls back to Signature version '2' for listing your buckets. Since minio server is only Signature version '4' listing your buckets with Signature version '2' fails. We have no immediate plans on supporting Signature version '2'. Please follow https://github.com/minio/minio/issues/987 to know more on this issue. -
-`s3cmd` is currently not supported. +Edit the following fields in your s3cmd configuration file. ~/.s3cfg + +``` +host_base = localhost:9000 +host_bucket = localhost:9000 +access_key = YOUR_ACCESS_KEY_HERE +secret_key = YOUR_SECRET_KEY_HERE +``` + +To list your buckets. +``` +$ s3cmd --region us-east-1 ls s3:// +2015-12-09 16:12 s3://testbbucket +``` + +To list contents inside buckets. +``` +$ s3cmd --region us-east-1 ls s3://testbucket/ + DIR s3://testbucket/test/ +2015-12-09 16:05 138504 s3://testbucket/newfile +``` ## Contribute to Minio Project Please follow Minio [Contributor's Guide](./CONTRIBUTING.md) diff --git a/api-errors.go b/api-errors.go index a75c60752..b1d5b76b2 100644 --- a/api-errors.go +++ b/api-errors.go @@ -30,12 +30,14 @@ type APIError struct { // APIErrorResponse - error response format type APIErrorResponse struct { - XMLName xml.Name `xml:"Error" json:"-"` - Code string - Message string - Resource string - RequestID string `xml:"RequestId"` - HostID string `xml:"HostId"` + XMLName xml.Name `xml:"Error" json:"-"` + Code string + Message string + Key string + BucketName string + Resource string + RequestID string `xml:"RequestId"` + HostID string `xml:"HostId"` } // Error codes, non exhaustive list - http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html @@ -70,6 +72,7 @@ const ( InvalidPartOrder AuthorizationHeaderMalformed MalformedPOSTRequest + SignatureVersionNotSupported BucketNotEmpty RootPathFull ) @@ -221,6 +224,11 @@ var errorCodeResponse = map[int]APIError{ Description: "The body of your POST request is not well-formed multipart/form-data.", HTTPStatusCode: http.StatusBadRequest, }, + SignatureVersionNotSupported: { + Code: "InvalidRequest", + Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.", + HTTPStatusCode: http.StatusBadRequest, + }, BucketNotEmpty: { Code: "BucketNotEmpty", Description: "The bucket you tried to delete is not empty.", diff --git a/api-headers.go b/api-headers.go index c56b3a6eb..295a49b85 100644 --- a/api-headers.go +++ b/api-headers.go @@ -57,6 +57,7 @@ func setCommonHeaders(w http.ResponseWriter, contentLength int) { func encodeErrorResponse(response interface{}) []byte { var bytesBuffer bytes.Buffer // write common headers + bytesBuffer.WriteString(xml.Header) e := xml.NewEncoder(&bytesBuffer) e.Encode(response) return bytesBuffer.Bytes() @@ -92,6 +93,7 @@ func setObjectHeaders(w http.ResponseWriter, metadata fs.ObjectMetadata, content func encodeSuccessResponse(response interface{}) []byte { var bytesBuffer bytes.Buffer + bytesBuffer.WriteString(xml.Header) e := xml.NewEncoder(&bytesBuffer) e.Encode(response) return bytesBuffer.Bytes() diff --git a/generic-handlers.go b/generic-handlers.go index 0cf9f2654..fbd5981f3 100644 --- a/generic-handlers.go +++ b/generic-handlers.go @@ -46,6 +46,10 @@ type resourceHandler struct { handler http.Handler } +type ignoreSignatureV2RequestHandler struct { + handler http.Handler +} + func parseDate(req *http.Request) (time.Time, error) { amzDate := req.Header.Get(http.CanonicalHeaderKey("x-amz-date")) switch { @@ -128,6 +132,23 @@ func CorsHandler(h http.Handler) http.Handler { return c.Handler(h) } +// IgnoreSignatureV2RequestHandler - +// Verify if authorization header has signature version '2', reject it cleanly. +func IgnoreSignatureV2RequestHandler(h http.Handler) http.Handler { + return ignoreSignatureV2RequestHandler{h} +} + +// Ignore signature version '2' ServerHTTP() wrapper. +func (h ignoreSignatureV2RequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if _, ok := r.Header["Authorization"]; ok { + if !strings.HasPrefix(r.Header.Get("Authorization"), authHeaderPrefix) { + writeErrorResponse(w, r, SignatureVersionNotSupported, r.URL.Path) + return + } + } + h.handler.ServeHTTP(w, r) +} + // IgnoreResourcesHandler - // Ignore resources handler is wrapper handler used for API request resource validation // Since we do not support all the S3 queries, it is necessary for us to throw back a diff --git a/routers.go b/routers.go index e94756e7b..758bbf849 100644 --- a/routers.go +++ b/routers.go @@ -80,9 +80,10 @@ func getNewCloudStorageAPI(conf cloudServerConfig) CloudStorageAPI { func getCloudStorageAPIHandler(api CloudStorageAPI) http.Handler { var mwHandlers = []MiddlewareHandler{ + CorsHandler, TimeValidityHandler, IgnoreResourcesHandler, - CorsHandler, + IgnoreSignatureV2RequestHandler, } if !api.Anonymous { mwHandlers = append(mwHandlers, SignatureHandler) diff --git a/signature-handler.go b/signature-handler.go index b809fbdc1..93457de65 100644 --- a/signature-handler.go +++ b/signature-handler.go @@ -37,7 +37,9 @@ func SignatureHandler(h http.Handler) http.Handler { func isRequestSignatureV4(req *http.Request) bool { if _, ok := req.Header["Authorization"]; ok { - return ok + if strings.HasPrefix(req.Header.Get("Authorization"), authHeaderPrefix) { + return ok + } } return false }