Add domain and subdomain support for MinioAPI

This change brings in domain and subdomain support

   - ./minio --domain "yourminiodomain.com"

This change brings in a much needed feature by keeping
bucketnames as part of your 'DNS' name.

All your existing applications can be migrated off from s3 to
Minio without little to no modifications.

NOTE: Setting up DNS for your `buckets` is out of scope of this feature
This commit is contained in:
Harshavardhana 2015-02-23 02:11:27 -08:00
parent 2d3b00b831
commit 51e80eaa6d
13 changed files with 236 additions and 148 deletions

View File

@ -41,6 +41,7 @@ func getStorageType(input string) server.StorageType {
func runCmd(c *cli.Context) { func runCmd(c *cli.Context) {
storageTypeStr := c.String("storage-type") storageTypeStr := c.String("storage-type")
domain := c.String("domain")
apiaddress := c.String("api-address") apiaddress := c.String("api-address")
webaddress := c.String("web-address") webaddress := c.String("web-address")
certFile := c.String("cert") certFile := c.String("cert")
@ -52,6 +53,7 @@ func runCmd(c *cli.Context) {
storageType := getStorageType(storageTypeStr) storageType := getStorageType(storageTypeStr)
var serverConfigs []server.ServerConfig var serverConfigs []server.ServerConfig
apiServerConfig := server.ServerConfig{ apiServerConfig := server.ServerConfig{
Domain: domain,
Address: apiaddress, Address: apiaddress,
Tls: tls, Tls: tls,
CertFile: certFile, CertFile: certFile,
@ -61,6 +63,7 @@ func runCmd(c *cli.Context) {
}, },
} }
webUiServerConfig := server.ServerConfig{ webUiServerConfig := server.ServerConfig{
Domain: domain,
Address: webaddress, Address: webaddress,
Tls: false, Tls: false,
CertFile: "", CertFile: "",
@ -79,6 +82,11 @@ func main() {
app.Name = "minio" app.Name = "minio"
app.Usage = "" app.Usage = ""
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
cli.StringFlag{
Name: "domain,d",
Value: "",
Usage: "address for incoming API requests",
},
cli.StringFlag{ cli.StringFlag{
Name: "api-address,a", Name: "api-address,a",
Value: ":9000", Value: ":9000",

View File

@ -40,7 +40,7 @@ var _ = Suite(&MySuite{})
func (s *MySuite) TestNonExistantObject(c *C) { func (s *MySuite) TestNonExistantObject(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -52,7 +52,7 @@ func (s *MySuite) TestNonExistantObject(c *C) {
func (s *MySuite) TestEmptyObject(c *C) { func (s *MySuite) TestEmptyObject(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -77,7 +77,7 @@ func (s *MySuite) TestEmptyObject(c *C) {
func (s *MySuite) TestObject(c *C) { func (s *MySuite) TestObject(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -100,7 +100,7 @@ func (s *MySuite) TestObject(c *C) {
func (s *MySuite) TestMultipleObjects(c *C) { func (s *MySuite) TestMultipleObjects(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -180,7 +180,7 @@ func (s *MySuite) TestMultipleObjects(c *C) {
func (s *MySuite) TestNotImplemented(c *C) { func (s *MySuite) TestNotImplemented(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -191,7 +191,7 @@ func (s *MySuite) TestNotImplemented(c *C) {
func (s *MySuite) TestHeader(c *C) { func (s *MySuite) TestHeader(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -214,7 +214,7 @@ func (s *MySuite) TestHeader(c *C) {
func (s *MySuite) TestPutBucket(c *C) { func (s *MySuite) TestPutBucket(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -239,7 +239,7 @@ func (s *MySuite) TestPutBucket(c *C) {
func (s *MySuite) TestPutObject(c *C) { func (s *MySuite) TestPutObject(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -289,7 +289,7 @@ func (s *MySuite) TestPutObject(c *C) {
func (s *MySuite) TestListBuckets(c *C) { func (s *MySuite) TestListBuckets(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -368,7 +368,7 @@ func verifyHeaders(c *C, header http.Header, date time.Time, size int, contentTy
func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) { func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -393,7 +393,7 @@ func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) {
func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) { func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()
@ -418,7 +418,7 @@ func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) {
func (s *MySuite) TestContentTypePersists(c *C) { func (s *MySuite) TestContentTypePersists(c *C) {
_, _, storage := inmemory.Start() _, _, storage := inmemory.Start()
httpHandler := HttpHandler(storage) httpHandler := HttpHandler("", storage)
testServer := httptest.NewServer(httpHandler) testServer := httptest.NewServer(httpHandler)
defer testServer.Close() defer testServer.Close()

View File

@ -1,116 +1,13 @@
package minioapi package minioapi
import ( import (
"encoding/json"
"log" "log"
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
mstorage "github.com/minio-io/minio/pkg/storage" mstorage "github.com/minio-io/minio/pkg/storage"
"github.com/minio-io/minio/pkg/utils/policy"
) )
func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
bucket := vars["bucket"]
acceptsContentType := getContentType(req)
policy, ok := policy.Parsepolicy(req.Body)
if ok == false {
error := errorCodeError(InvalidPolicyDocument)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
return
}
err := server.storage.StoreBucketPolicy(bucket, policy)
switch err := err.(type) {
case nil:
{
w.WriteHeader(http.StatusNoContent)
writeCommonHeaders(w, getContentString(acceptsContentType))
w.Header().Set("Connection", "keep-alive")
}
case mstorage.BucketNameInvalid:
{
error := errorCodeError(InvalidBucketName)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BucketNotFound:
{
error := errorCodeError(NoSuchBucket)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BackendCorrupted:
case mstorage.ImplementationError:
{
log.Println(err)
error := errorCodeError(InternalError)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
}
}
func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
bucket := vars["bucket"]
acceptsContentType := getContentType(req)
p, err := server.storage.GetBucketPolicy(bucket)
switch err := err.(type) {
case nil:
{
responsePolicy, ret := json.Marshal(p)
if ret != nil {
error := errorCodeError(InternalError)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
writeCommonHeaders(w, getContentString(acceptsContentType))
w.Header().Set("Connection", "keep-alive")
w.Write(responsePolicy)
}
case mstorage.BucketNameInvalid:
{
error := errorCodeError(InvalidBucketName)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BucketNotFound:
{
error := errorCodeError(NoSuchBucket)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BucketPolicyNotFound:
{
error := errorCodeError(NoSuchBucketPolicy)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BackendCorrupted:
case mstorage.ImplementationError:
{
log.Println(err)
error := errorCodeError(InternalError)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
}
}
func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req) vars := mux.Vars(req)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -224,7 +121,7 @@ func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Reques
} }
case mstorage.ImplementationError: case mstorage.ImplementationError:
{ {
// Embed errors log on serve side // Embed errors log on server side
log.Println(err) log.Println(err)
error := errorCodeError(InternalError) error := errorCodeError(InternalError)
errorResponse := getErrorResponse(error, bucket) errorResponse := getErrorResponse(error, bucket)

View File

@ -27,6 +27,11 @@ import (
mstorage "github.com/minio-io/minio/pkg/storage" mstorage "github.com/minio-io/minio/pkg/storage"
) )
// No encoder interface exists, so we create one.
type encoder interface {
Encode(v interface{}) error
}
// Write Common Header helpers // Write Common Header helpers
func writeCommonHeaders(w http.ResponseWriter, acceptsType string) { func writeCommonHeaders(w http.ResponseWriter, acceptsType string) {
w.Header().Set("Server", "Minio") w.Header().Set("Server", "Minio")

View File

@ -20,7 +20,7 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/gorilla/mux" x "github.com/gorilla/mux"
mstorage "github.com/minio-io/minio/pkg/storage" mstorage "github.com/minio-io/minio/pkg/storage"
"github.com/minio-io/minio/pkg/utils/config" "github.com/minio-io/minio/pkg/utils/config"
) )
@ -30,24 +30,11 @@ const (
) )
type minioApi struct { type minioApi struct {
domain string
storage mstorage.Storage storage mstorage.Storage
} }
// No encoder interface exists, so we create one. func pathMux(api minioApi, mux *x.Router) *x.Router {
type encoder interface {
Encode(v interface{}) error
}
func HttpHandler(storage mstorage.Storage) http.Handler {
mux := mux.NewRouter()
var api = minioApi{}
api.storage = storage
var conf = config.Config{}
if err := conf.SetupConfig(); err != nil {
log.Fatal(err)
}
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET") mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET") mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT") mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
@ -55,5 +42,48 @@ func HttpHandler(storage mstorage.Storage) http.Handler {
mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD") mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD")
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT") mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT")
return mux
}
func domainMux(api minioApi, mux *x.Router) *x.Router {
mux.HandleFunc("/",
api.listObjectsHandler).Host("{bucket}" + "." + api.domain).Methods("GET")
mux.HandleFunc("/{object:.*}",
api.getObjectHandler).Host("{bucket}" + "." + api.domain).Methods("GET")
mux.HandleFunc("/{object:.*}",
api.headObjectHandler).Host("{bucket}" + "." + api.domain).Methods("HEAD")
mux.HandleFunc("/{object:.*}",
api.putObjectHandler).Host("{bucket}" + "." + api.domain).Methods("PUT")
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
return mux
}
func getMux(api minioApi, mux *x.Router) *x.Router {
switch true {
case api.domain == "":
return pathMux(api, mux)
case api.domain != "":
s := mux.Host(api.domain).Subrouter()
return domainMux(api, s)
}
return nil
}
func HttpHandler(domain string, storage mstorage.Storage) http.Handler {
var mux *x.Router
var api = minioApi{}
api.storage = storage
api.domain = domain
r := x.NewRouter()
mux = getMux(api, r)
var conf = config.Config{}
if err := conf.SetupConfig(); err != nil {
log.Fatal(err)
}
return validateHandler(conf, ignoreResourcesHandler(mux)) return validateHandler(conf, ignoreResourcesHandler(mux))
} }

View File

@ -103,6 +103,12 @@ func (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Reques
bucket = vars["bucket"] bucket = vars["bucket"]
object = vars["object"] object = vars["object"]
resources := getBucketResources(req.URL.Query())
if resources.policy == true && object == "" {
server.putBucketPolicyHandler(w, req)
return
}
err := server.storage.StoreObject(bucket, object, "", req.Body) err := server.storage.StoreObject(bucket, object, "", req.Body)
switch err := err.(type) { switch err := err.(type) {
case nil: case nil:

View File

@ -0,0 +1,112 @@
package minioapi
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
mstorage "github.com/minio-io/minio/pkg/storage"
"github.com/minio-io/minio/pkg/utils/policy"
)
func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
bucket := vars["bucket"]
acceptsContentType := getContentType(req)
policy, ok := policy.Parsepolicy(req.Body)
if ok == false {
error := errorCodeError(InvalidPolicyDocument)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
return
}
err := server.storage.StoreBucketPolicy(bucket, policy)
switch err := err.(type) {
case nil:
{
w.WriteHeader(http.StatusNoContent)
writeCommonHeaders(w, getContentString(acceptsContentType))
w.Header().Set("Connection", "keep-alive")
}
case mstorage.BucketNameInvalid:
{
error := errorCodeError(InvalidBucketName)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BucketNotFound:
{
error := errorCodeError(NoSuchBucket)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BackendCorrupted:
case mstorage.ImplementationError:
{
log.Println(err)
error := errorCodeError(InternalError)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
}
}
func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
bucket := vars["bucket"]
acceptsContentType := getContentType(req)
p, err := server.storage.GetBucketPolicy(bucket)
switch err := err.(type) {
case nil:
{
responsePolicy, ret := json.Marshal(p)
if ret != nil {
error := errorCodeError(InternalError)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
writeCommonHeaders(w, getContentString(acceptsContentType))
w.Header().Set("Connection", "keep-alive")
w.Write(responsePolicy)
}
case mstorage.BucketNameInvalid:
{
error := errorCodeError(InvalidBucketName)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BucketNotFound:
{
error := errorCodeError(NoSuchBucket)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BucketPolicyNotFound:
{
error := errorCodeError(NoSuchBucketPolicy)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
case mstorage.BackendCorrupted:
case mstorage.ImplementationError:
{
log.Println(err)
error := errorCodeError(InternalError)
errorResponse := getErrorResponse(error, bucket)
w.WriteHeader(error.HttpStatusCode)
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
}
}
}

View File

@ -27,7 +27,7 @@ type HttpServerConfig struct {
TLS bool TLS bool
CertFile string CertFile string
KeyFile string KeyFile string
Websocket bool Websocket bool // implement it - TODO
} }
type HttpServer struct{} type HttpServer struct{}

View File

@ -31,6 +31,7 @@ import (
) )
type ServerConfig struct { type ServerConfig struct {
Domain string
Address string Address string
Tls bool Tls bool
CertFile string CertFile string
@ -78,7 +79,7 @@ func getHttpChannels(configs []ServerConfig) (ctrlChans []chan<- string, statusC
ctrlChans, statusChans, storage = getStorageChannels(k.StorageType) ctrlChans, statusChans, storage = getStorageChannels(k.StorageType)
// start minio api in a web server, pass storage driver into it // start minio api in a web server, pass storage driver into it
ctrlChan, statusChan, _ = httpserver.Start(minioapi.HttpHandler(storage), httpConfig) ctrlChan, statusChan, _ = httpserver.Start(minioapi.HttpHandler(config.Domain, storage), httpConfig)
ctrlChans = append(ctrlChans, ctrlChan) ctrlChans = append(ctrlChans, ctrlChan)
statusChans = append(statusChans, statusChan) statusChans = append(statusChans, statusChan)

View File

@ -127,14 +127,15 @@ func (storage *storage) GetBucketPolicy(bucket string) (interface{}, error) {
// get policy path // get policy path
bucketPolicy := path.Join(storage.root, bucket+"_policy.json") bucketPolicy := path.Join(storage.root, bucket+"_policy.json")
filestat, err := os.Stat(bucketPolicy) filestat, err := os.Stat(bucketPolicy)
if filestat.IsDir() {
return policy.BucketPolicy{}, mstorage.BackendCorrupted{Path: bucketPolicy}
}
if os.IsNotExist(err) { if os.IsNotExist(err) {
return policy.BucketPolicy{}, mstorage.BucketPolicyNotFound{Bucket: bucket} return policy.BucketPolicy{}, mstorage.BucketPolicyNotFound{Bucket: bucket}
} }
if filestat.IsDir() {
return policy.BucketPolicy{}, mstorage.BackendCorrupted{Path: bucketPolicy}
}
file, err := os.OpenFile(bucketPolicy, os.O_RDONLY, 0666) file, err := os.OpenFile(bucketPolicy, os.O_RDONLY, 0666)
defer file.Close() defer file.Close()
if err != nil { if err != nil {
@ -170,10 +171,13 @@ func (storage *storage) StoreBucketPolicy(bucket string, policy interface{}) err
// get policy path // get policy path
bucketPolicy := path.Join(storage.root, bucket+"_policy.json") bucketPolicy := path.Join(storage.root, bucket+"_policy.json")
filestat, _ := os.Stat(bucketPolicy) filestat, ret := os.Stat(bucketPolicy)
if !os.IsNotExist(ret) {
if filestat.IsDir() { if filestat.IsDir() {
return mstorage.BackendCorrupted{Path: bucketPolicy} return mstorage.BackendCorrupted{Path: bucketPolicy}
} }
}
file, err := os.OpenFile(bucketPolicy, os.O_WRONLY|os.O_CREATE, 0600) file, err := os.OpenFile(bucketPolicy, os.O_WRONLY|os.O_CREATE, 0600)
defer file.Close() defer file.Close()
if err != nil { if err != nil {

View File

@ -61,7 +61,8 @@ func IsValidBucket(bucket string) bool {
if match, _ := regexp.MatchString("\\.\\.", bucket); match == true { if match, _ := regexp.MatchString("\\.\\.", bucket); match == true {
return false return false
} }
match, _ := regexp.MatchString("^[a-zA-Z][a-zA-Z0-9\\.\\-]+[a-zA-Z0-9]$", bucket) // We don't support buckets with '.' in them
match, _ := regexp.MatchString("^[a-zA-Z][a-zA-Z0-9\\-]+[a-zA-Z0-9]$", bucket)
return match return match
} }

View File

@ -155,6 +155,11 @@ var subResList = []string{"acl", "lifecycle", "location", "logging", "notificati
// <HTTP-Request-URI, from the protocol name up to the query string> + // <HTTP-Request-URI, from the protocol name up to the query string> +
// [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"]; // [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
func writeCanonicalizedResource(buf *bytes.Buffer, req *http.Request) { func writeCanonicalizedResource(buf *bytes.Buffer, req *http.Request) {
bucket := bucketFromHostname(req)
if bucket != "" {
buf.WriteByte('/')
buf.WriteString(bucket)
}
buf.WriteString(req.URL.Path) buf.WriteString(req.URL.Path)
if req.URL.RawQuery != "" { if req.URL.RawQuery != "" {
n := 0 n := 0
@ -176,3 +181,17 @@ func writeCanonicalizedResource(buf *bytes.Buffer, req *http.Request) {
} }
} }
} }
func bucketFromHostname(req *http.Request) string {
host := req.Host
if host == "" {
host = req.URL.Host
}
host = strings.TrimSpace(host)
hostParts := strings.Split(host, ".")
if len(hostParts) > 1 {
return hostParts[0]
}
return ""
}

View File

@ -31,8 +31,8 @@ const (
// TODO support canonical user // TODO support canonical user
const ( const (
AwsPrincipal = "arn:aws:iam::Account-ID:user/" AwsPrincipal = "arn:aws:iam::"
MinioPrincipal = "minio::Account-ID:user/" MinioPrincipal = "minio::"
) )
var SupportedActionMap = map[string]bool{ var SupportedActionMap = map[string]bool{
@ -55,10 +55,13 @@ var SupportedEffectMap = map[string]bool{
func isValidAction(action []string) bool { func isValidAction(action []string) bool {
var ok bool = false var ok bool = false
for _, a := range action { for _, a := range action {
if SupportedActionMap[a] { if !SupportedActionMap[a] {
goto error
}
}
ok = true ok = true
}
} error:
return ok return ok
} }
@ -104,6 +107,7 @@ func isValidPrincipal(principal string) bool {
if len(username) == 0 { if len(username) == 0 {
ok = false ok = false
} }
case strings.HasPrefix(principal, MinioPrincipal): case strings.HasPrefix(principal, MinioPrincipal):
username := strings.SplitAfter(principal, MinioPrincipal)[1] username := strings.SplitAfter(principal, MinioPrincipal)[1]
ok = true ok = true
@ -160,6 +164,7 @@ func Parsepolicy(data io.Reader) (BucketPolicy, bool) {
if len(statement.Resource) == 0 { if len(statement.Resource) == 0 {
goto error goto error
} }
if !isValidResource(statement.Resource) { if !isValidResource(statement.Resource) {
goto error goto error
} }