mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
jwt for read access control
This commit is contained in:
parent
d344e0a035
commit
50aa769554
|
@ -277,8 +277,14 @@ directory = "/" # destination directory
|
|||
key = ""
|
||||
expires_after_seconds = 10 # seconds
|
||||
|
||||
# jwt for read is only supported with master+volume setup. Filer does not support this mode.
|
||||
[jwt.signing.read]
|
||||
key = ""
|
||||
expires_after_seconds = 10 # seconds
|
||||
|
||||
# all grpc tls authentications are mutual
|
||||
# the values for the following ca, cert, and key are paths to the PERM files.
|
||||
# the host name is not checked, so the PERM files can be shared.
|
||||
[grpc]
|
||||
ca = ""
|
||||
|
||||
|
|
|
@ -44,18 +44,26 @@ type Guard struct {
|
|||
whiteList []string
|
||||
SigningKey SigningKey
|
||||
ExpiresAfterSec int
|
||||
ReadSigningKey SigningKey
|
||||
ReadExpiresAfterSec int
|
||||
|
||||
isActive bool
|
||||
isWriteActive bool
|
||||
}
|
||||
|
||||
func NewGuard(whiteList []string, signingKey string, expiresAfterSec int) *Guard {
|
||||
g := &Guard{whiteList: whiteList, SigningKey: SigningKey(signingKey), ExpiresAfterSec: expiresAfterSec}
|
||||
g.isActive = len(g.whiteList) != 0 || len(g.SigningKey) != 0
|
||||
func NewGuard(whiteList []string, signingKey string, expiresAfterSec int, readSigningKey string, readExpiresAfterSec int) *Guard {
|
||||
g := &Guard{
|
||||
whiteList: whiteList,
|
||||
SigningKey: SigningKey(signingKey),
|
||||
ExpiresAfterSec: expiresAfterSec,
|
||||
ReadSigningKey: SigningKey(readSigningKey),
|
||||
ReadExpiresAfterSec: readExpiresAfterSec,
|
||||
}
|
||||
g.isWriteActive = len(g.whiteList) != 0 || len(g.SigningKey) != 0
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *Guard) WhiteList(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
|
||||
if !g.isActive {
|
||||
if !g.isWriteActive {
|
||||
//if no security needed, just skip all checking
|
||||
return f
|
||||
}
|
||||
|
|
|
@ -63,6 +63,10 @@ func NewMasterServer(r *mux.Router, port int, metaFolder string,
|
|||
v.SetDefault("jwt.signing.expires_after_seconds", 10)
|
||||
expiresAfterSec := v.GetInt("jwt.signing.expires_after_seconds")
|
||||
|
||||
readSigningKey := v.GetString("jwt.signing.read.key")
|
||||
v.SetDefault("jwt.signing.read.expires_after_seconds", 60)
|
||||
readExpiresAfterSec := v.GetInt("jwt.signing.read.expires_after_seconds")
|
||||
|
||||
var preallocateSize int64
|
||||
if preallocate {
|
||||
preallocateSize = int64(volumeSizeLimitMB) * (1 << 20)
|
||||
|
@ -83,7 +87,7 @@ func NewMasterServer(r *mux.Router, port int, metaFolder string,
|
|||
ms.vg = topology.NewDefaultVolumeGrowth()
|
||||
glog.V(0).Infoln("Volume Size Limit is", volumeSizeLimitMB, "MB")
|
||||
|
||||
ms.guard = security.NewGuard(whiteList, signingKey, expiresAfterSec)
|
||||
ms.guard = security.NewGuard(whiteList, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec)
|
||||
|
||||
if !disableHttp {
|
||||
handleStaticResources2(r)
|
||||
|
|
|
@ -67,7 +67,9 @@ func (ms *MasterServer) dirLookupHandler(w http.ResponseWriter, r *http.Request)
|
|||
if location.Error != "" {
|
||||
httpStatus = http.StatusNotFound
|
||||
} else {
|
||||
ms.maybeAddJwtAuthorization(w, fileId)
|
||||
forRead := r.FormValue("read")
|
||||
isRead := forRead == "yes"
|
||||
ms.maybeAddJwtAuthorization(w, fileId, !isRead)
|
||||
}
|
||||
writeJsonQuiet(w, r, httpStatus, location)
|
||||
}
|
||||
|
@ -102,17 +104,23 @@ 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)
|
||||
ms.maybeAddJwtAuthorization(w, fid, true)
|
||||
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, ms.guard.ExpiresAfterSec, fileId)
|
||||
func (ms *MasterServer) maybeAddJwtAuthorization(w http.ResponseWriter, fileId string, isWrite bool) {
|
||||
var encodedJwt security.EncodedJwt
|
||||
if isWrite {
|
||||
encodedJwt = security.GenJwt(ms.guard.SigningKey, ms.guard.ExpiresAfterSec, fileId)
|
||||
} else {
|
||||
encodedJwt = security.GenJwt(ms.guard.ReadSigningKey, ms.guard.ReadExpiresAfterSec, fileId)
|
||||
}
|
||||
if encodedJwt == "" {
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Authorization", "BEARER "+string(encodedJwt))
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string,
|
|||
expiresAfterSec := v.GetInt("jwt.signing.expires_after_seconds")
|
||||
enableUiAccess := v.GetBool("access.ui")
|
||||
|
||||
readSigningKey := v.GetString("jwt.signing.read.key")
|
||||
v.SetDefault("jwt.signing.read.expires_after_seconds", 60)
|
||||
readExpiresAfterSec := v.GetInt("jwt.signing.read.expires_after_seconds")
|
||||
|
||||
vs := &VolumeServer{
|
||||
pulseSeconds: pulseSeconds,
|
||||
dataCenter: dataCenter,
|
||||
|
@ -57,7 +61,7 @@ func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string,
|
|||
vs.SeedMasterNodes = masterNodes
|
||||
vs.store = storage.NewStore(vs.grpcDialOption, port, ip, publicUrl, folders, maxCounts, vs.needleMapKind)
|
||||
|
||||
vs.guard = security.NewGuard(whiteList, signingKey, expiresAfterSec)
|
||||
vs.guard = security.NewGuard(whiteList, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec)
|
||||
|
||||
handleStaticResources(adminMux)
|
||||
if signingKey == "" || enableUiAccess {
|
||||
|
|
|
@ -49,10 +49,22 @@ func (vs *VolumeServer) publicReadOnlyHandler(w http.ResponseWriter, r *http.Req
|
|||
}
|
||||
}
|
||||
|
||||
func (vs *VolumeServer) maybeCheckJwtAuthorization(r *http.Request, vid, fid string) bool {
|
||||
func (vs *VolumeServer) maybeCheckJwtAuthorization(r *http.Request, vid, fid string, isWrite bool) bool {
|
||||
|
||||
var signingKey security.SigningKey
|
||||
|
||||
if isWrite {
|
||||
if len(vs.guard.SigningKey) == 0 {
|
||||
return true
|
||||
} else {
|
||||
signingKey = vs.guard.SigningKey
|
||||
}
|
||||
}else {
|
||||
if len(vs.guard.ReadSigningKey) == 0 {
|
||||
return true
|
||||
} else {
|
||||
signingKey = vs.guard.ReadSigningKey
|
||||
}
|
||||
}
|
||||
|
||||
tokenStr := security.GetJwt(r)
|
||||
|
@ -61,7 +73,7 @@ func (vs *VolumeServer) maybeCheckJwtAuthorization(r *http.Request, vid, fid str
|
|||
return false
|
||||
}
|
||||
|
||||
token, err := security.DecodeJwt(vs.guard.SigningKey, tokenStr)
|
||||
token, err := security.DecodeJwt(signingKey, tokenStr)
|
||||
if err != nil {
|
||||
glog.V(1).Infof("jwt verification error from %s: %v", r.RemoteAddr, err)
|
||||
return false
|
||||
|
|
|
@ -3,6 +3,7 @@ package weed_server
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
|
@ -27,6 +28,12 @@ var fileNameEscaper = strings.NewReplacer("\\", "\\\\", "\"", "\\\"")
|
|||
func (vs *VolumeServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
n := new(needle.Needle)
|
||||
vid, fid, filename, ext, _ := parseURLPath(r.URL.Path)
|
||||
|
||||
if !vs.maybeCheckJwtAuthorization(r, vid, fid, false) {
|
||||
writeJsonError(w, r, http.StatusUnauthorized, errors.New("wrong jwt"))
|
||||
return
|
||||
}
|
||||
|
||||
volumeId, err := needle.NewVolumeId(vid)
|
||||
if err != nil {
|
||||
glog.V(2).Infoln("parsing error:", err, r.URL.Path)
|
||||
|
|
|
@ -29,7 +29,7 @@ func (vs *VolumeServer) PostHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if !vs.maybeCheckJwtAuthorization(r, vid, fid) {
|
||||
if !vs.maybeCheckJwtAuthorization(r, vid, fid, true) {
|
||||
writeJsonError(w, r, http.StatusUnauthorized, errors.New("wrong jwt"))
|
||||
return
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ func (vs *VolumeServer) DeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
volumeId, _ := needle.NewVolumeId(vid)
|
||||
n.ParsePath(fid)
|
||||
|
||||
if !vs.maybeCheckJwtAuthorization(r, vid, fid) {
|
||||
if !vs.maybeCheckJwtAuthorization(r, vid, fid, true) {
|
||||
writeJsonError(w, r, http.StatusUnauthorized, errors.New("wrong jwt"))
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue