add authorizing fileId write access
need to secure upload/update/delete for benchmark/filer/mount need to add secure grpc
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/topology"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type MasterServer struct {
|
||||
@@ -47,6 +48,10 @@ func NewMasterServer(r *mux.Router, port int, metaFolder string,
|
||||
whiteList []string,
|
||||
) *MasterServer {
|
||||
|
||||
LoadConfiguration("security", false)
|
||||
v := viper.GetViper()
|
||||
signingKey := v.GetString("jwt.signing.key")
|
||||
|
||||
var preallocateSize int64
|
||||
if preallocate {
|
||||
preallocateSize = int64(volumeSizeLimitMB) * (1 << 20)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/operation"
|
||||
"github.com/chrislusf/seaweedfs/weed/security"
|
||||
"github.com/chrislusf/seaweedfs/weed/stats"
|
||||
"github.com/chrislusf/seaweedfs/weed/storage"
|
||||
)
|
||||
@@ -40,12 +41,23 @@ func (ms *MasterServer) lookupVolumeId(vids []string, collection string) (volume
|
||||
return
|
||||
}
|
||||
|
||||
// Takes one volumeId only, can not do batch lookup
|
||||
// If "fileId" is provided, this returns the fileId location and a JWT to update or delete the file.
|
||||
// If "volumeId" is provided, this only returns the volumeId location
|
||||
func (ms *MasterServer) dirLookupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vid := r.FormValue("volumeId")
|
||||
commaSep := strings.Index(vid, ",")
|
||||
if commaSep > 0 {
|
||||
vid = vid[0:commaSep]
|
||||
if vid != "" {
|
||||
// backward compatible
|
||||
commaSep := strings.Index(vid, ",")
|
||||
if commaSep > 0 {
|
||||
vid = vid[0:commaSep]
|
||||
}
|
||||
}
|
||||
fileId := r.FormValue("fileId")
|
||||
if fileId != "" {
|
||||
commaSep := strings.Index(fileId, ",")
|
||||
if commaSep > 0 {
|
||||
vid = fileId[0:commaSep]
|
||||
}
|
||||
}
|
||||
vids := []string{vid}
|
||||
collection := r.FormValue("collection") //optional, but can be faster if too many collections
|
||||
@@ -54,6 +66,8 @@ func (ms *MasterServer) dirLookupHandler(w http.ResponseWriter, r *http.Request)
|
||||
httpStatus := http.StatusOK
|
||||
if location.Error != "" {
|
||||
httpStatus = http.StatusNotFound
|
||||
} else {
|
||||
ms.maybeAddJwtAuthorization(w, fileId)
|
||||
}
|
||||
writeJsonQuiet(w, r, httpStatus, location)
|
||||
}
|
||||
@@ -88,8 +102,17 @@ func (ms *MasterServer) dirAssignHandler(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
fid, count, dn, err := ms.Topo.PickForWrite(requestedCount, option)
|
||||
if err == nil {
|
||||
ms.maybeAddJwtAuthorization(w, fid)
|
||||
writeJsonQuiet(w, r, http.StatusOK, operation.AssignResult{Fid: fid, Url: dn.Url(), PublicUrl: dn.PublicUrl, Count: count})
|
||||
} else {
|
||||
writeJsonQuiet(w, r, http.StatusNotAcceptable, operation.AssignResult{Error: err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MasterServer) maybeAddJwtAuthorization(w http.ResponseWriter, fileId string) {
|
||||
encodedJwt := security.GenJwt(ms.guard.SigningKey, fileId)
|
||||
if encodedJwt == "" {
|
||||
return
|
||||
}
|
||||
w.Header().Set("Authorization", "BEARER "+string(encodedJwt))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/security"
|
||||
"github.com/chrislusf/seaweedfs/weed/storage"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type VolumeServer struct {
|
||||
@@ -31,6 +32,12 @@ func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string,
|
||||
whiteList []string,
|
||||
fixJpgOrientation bool,
|
||||
readRedirect bool) *VolumeServer {
|
||||
|
||||
LoadConfiguration("security", false)
|
||||
v := viper.GetViper()
|
||||
signingKey := v.GetString("jwt.signing.key")
|
||||
enableUiAccess := v.GetBool("access.ui")
|
||||
|
||||
vs := &VolumeServer{
|
||||
pulseSeconds: pulseSeconds,
|
||||
dataCenter: dataCenter,
|
||||
@@ -42,14 +49,17 @@ func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string,
|
||||
vs.MasterNodes = masterNodes
|
||||
vs.store = storage.NewStore(port, ip, publicUrl, folders, maxCounts, vs.needleMapKind)
|
||||
|
||||
vs.guard = security.NewGuard(whiteList, "")
|
||||
vs.guard = security.NewGuard(whiteList, signingKey)
|
||||
|
||||
handleStaticResources(adminMux)
|
||||
adminMux.HandleFunc("/ui/index.html", vs.uiStatusHandler)
|
||||
adminMux.HandleFunc("/status", vs.guard.WhiteList(vs.statusHandler))
|
||||
adminMux.HandleFunc("/stats/counter", vs.guard.WhiteList(statsCounterHandler))
|
||||
adminMux.HandleFunc("/stats/memory", vs.guard.WhiteList(statsMemoryHandler))
|
||||
adminMux.HandleFunc("/stats/disk", vs.guard.WhiteList(vs.statsDiskHandler))
|
||||
if signingKey == "" || enableUiAccess {
|
||||
// only expose the volume server details for safe environments
|
||||
adminMux.HandleFunc("/ui/index.html", vs.uiStatusHandler)
|
||||
adminMux.HandleFunc("/status", vs.guard.WhiteList(vs.statusHandler))
|
||||
adminMux.HandleFunc("/stats/counter", vs.guard.WhiteList(statsCounterHandler))
|
||||
adminMux.HandleFunc("/stats/memory", vs.guard.WhiteList(statsMemoryHandler))
|
||||
adminMux.HandleFunc("/stats/disk", vs.guard.WhiteList(vs.statsDiskHandler))
|
||||
}
|
||||
adminMux.HandleFunc("/", vs.privateStoreHandler)
|
||||
if publicMux != adminMux {
|
||||
// separated admin and public port
|
||||
@@ -69,5 +79,5 @@ func (vs *VolumeServer) Shutdown() {
|
||||
}
|
||||
|
||||
func (vs *VolumeServer) jwt(fileId string) security.EncodedJwt {
|
||||
return security.GenJwt(vs.guard.SecretKey, fileId)
|
||||
return security.GenJwt(vs.guard.SigningKey, fileId)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package weed_server
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/security"
|
||||
"github.com/chrislusf/seaweedfs/weed/stats"
|
||||
)
|
||||
|
||||
@@ -45,3 +47,32 @@ func (vs *VolumeServer) publicReadOnlyHandler(w http.ResponseWriter, r *http.Req
|
||||
vs.GetOrHeadHandler(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *VolumeServer) maybeCheckJwtAuthorization(r *http.Request, vid, fid string) bool {
|
||||
|
||||
if len(vs.guard.SigningKey) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
tokenStr := security.GetJwt(r)
|
||||
if tokenStr == "" {
|
||||
glog.V(1).Infof("missing jwt from %s", r.RemoteAddr)
|
||||
return false
|
||||
}
|
||||
|
||||
token, err := security.DecodeJwt(vs.guard.SigningKey, tokenStr)
|
||||
if err != nil {
|
||||
glog.V(1).Infof("jwt verification error from %s: %v", r.RemoteAddr, err)
|
||||
return false
|
||||
}
|
||||
if !token.Valid {
|
||||
glog.V(1).Infof("jwt invalid from %s: %v", r.RemoteAddr, tokenStr)
|
||||
return false
|
||||
}
|
||||
|
||||
if sc, ok := token.Claims.(*security.SeaweedFileIdClaims); ok {
|
||||
return sc.Fid == vid+","+fid
|
||||
}
|
||||
glog.V(1).Infof("unexpected jwt from %s: %v", r.RemoteAddr, tokenStr)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -20,13 +20,20 @@ func (vs *VolumeServer) PostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
writeJsonError(w, r, http.StatusBadRequest, e)
|
||||
return
|
||||
}
|
||||
vid, _, _, _, _ := parseURLPath(r.URL.Path)
|
||||
|
||||
vid, fid, _, _, _ := parseURLPath(r.URL.Path)
|
||||
volumeId, ve := storage.NewVolumeId(vid)
|
||||
if ve != nil {
|
||||
glog.V(0).Infoln("NewVolumeId error:", ve)
|
||||
writeJsonError(w, r, http.StatusBadRequest, ve)
|
||||
return
|
||||
}
|
||||
|
||||
if !vs.maybeCheckJwtAuthorization(r, vid, fid) {
|
||||
writeJsonError(w, r, http.StatusUnauthorized, errors.New("wrong jwt"))
|
||||
return
|
||||
}
|
||||
|
||||
needle, originalSize, ne := storage.CreateNeedleFromRequest(r, vs.FixJpgOrientation)
|
||||
if ne != nil {
|
||||
writeJsonError(w, r, http.StatusBadRequest, ne)
|
||||
@@ -56,6 +63,11 @@ func (vs *VolumeServer) DeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
volumeId, _ := storage.NewVolumeId(vid)
|
||||
n.ParsePath(fid)
|
||||
|
||||
if !vs.maybeCheckJwtAuthorization(r, vid, fid) {
|
||||
writeJsonError(w, r, http.StatusUnauthorized, errors.New("wrong jwt"))
|
||||
return
|
||||
}
|
||||
|
||||
// glog.V(2).Infof("volume %s deleting %s", vid, n)
|
||||
|
||||
cookie := n.Cookie
|
||||
|
||||
Reference in New Issue
Block a user