aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yml2
-rw-r--r--docker/Dockerfile.go_build2
-rw-r--r--docker/Dockerfile.go_build_large2
-rw-r--r--docker/Makefile17
-rw-r--r--docker/compose/dev.env0
-rw-r--r--docker/compose/local-dev-compose.yml28
-rw-r--r--docker/compose/tls.env14
-rw-r--r--go.mod1
-rw-r--r--note/SeaweedFS_Architecture.pngbin75867 -> 92018 bytes
-rw-r--r--note/SeaweedFS_Cluster_Backup.pngbin0 -> 87999 bytes
-rw-r--r--note/SeaweedFS_XDR.pngbin0 -> 63281 bytes
-rw-r--r--weed/command/master.go1
-rw-r--r--weed/command/scaffold.go7
-rw-r--r--weed/filer/stream.go6
-rw-r--r--weed/s3api/filer_util.go4
-rw-r--r--weed/s3api/s3api_object_handlers.go17
-rw-r--r--weed/s3api/s3api_object_handlers_postpolicy.go2
-rw-r--r--weed/security/tls.go66
-rw-r--r--weed/server/filer_server_handlers_read.go4
19 files changed, 150 insertions, 23 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0681f3dac..2a5e66a6e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -40,6 +40,7 @@ jobs:
goarch: ${{ matrix.goarch }}
release_tag: dev
overwrite: true
+ pre_command: export CGO_ENABLED=0
build_flags: -tags 5BytesOffset # optional, default is
ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}}
# Where to run `go build .`
@@ -55,6 +56,7 @@ jobs:
goarch: ${{ matrix.goarch }}
release_tag: dev
overwrite: true
+ pre_command: export CGO_ENABLED=0
ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}}
# Where to run `go build .`
project_path: weed
diff --git a/docker/Dockerfile.go_build b/docker/Dockerfile.go_build
index 64105ee29..1adf0f5ef 100644
--- a/docker/Dockerfile.go_build
+++ b/docker/Dockerfile.go_build
@@ -6,7 +6,7 @@ ARG BRANCH=${BRANCH:-master}
RUN cd /go/src/github.com/chrislusf/seaweedfs && git checkout $BRANCH
RUN cd /go/src/github.com/chrislusf/seaweedfs/weed \
&& export LDFLAGS="-X github.com/chrislusf/seaweedfs/weed/util.COMMIT=$(git rev-parse --short HEAD)" \
- && go install -ldflags "${LDFLAGS}"
+ && CGO_ENABLED=0 go install -ldflags "-extldflags -static ${LDFLAGS}"
FROM alpine AS final
LABEL author="Chris Lu"
diff --git a/docker/Dockerfile.go_build_large b/docker/Dockerfile.go_build_large
index 39f45cbde..48af3381d 100644
--- a/docker/Dockerfile.go_build_large
+++ b/docker/Dockerfile.go_build_large
@@ -6,7 +6,7 @@ ARG BRANCH=${BRANCH:-master}
RUN cd /go/src/github.com/chrislusf/seaweedfs && git checkout $BRANCH
RUN cd /go/src/github.com/chrislusf/seaweedfs/weed \
&& export LDFLAGS="-X github.com/chrislusf/seaweedfs/weed/util.COMMIT=$(git rev-parse --short HEAD)" \
- && go install -tags 5BytesOffset -ldflags "${LDFLAGS}"
+ && CGO_ENABLED=0 go install -tags 5BytesOffset -ldflags "-extldflags -static ${LDFLAGS}"
FROM alpine AS final
LABEL author="Chris Lu"
diff --git a/docker/Makefile b/docker/Makefile
index 67ee9acdf..345eac272 100644
--- a/docker/Makefile
+++ b/docker/Makefile
@@ -5,7 +5,7 @@ all: gen
gen: dev
build:
- cd ../weed; GOOS=linux go build; mv weed ../docker/
+ cd ../weed; CGO_ENABLED=0 GOOS=linux go build -ldflags "-extldflags -static"; mv weed ../docker/
docker build --no-cache -t chrislusf/seaweedfs:local -f Dockerfile.local .
rm ./weed
@@ -15,6 +15,9 @@ s3tests_build:
dev: build
docker-compose -f compose/local-dev-compose.yml -p seaweedfs up
+dev_tls: build certstrap
+ ENV_FILE="tls.env" docker-compose -f compose/local-dev-compose.yml -p seaweedfs up
+
dev_mount: build
docker-compose -f compose/local-mount-compose.yml -p seaweedfs up
@@ -41,3 +44,15 @@ filer_etcd: build
clean:
rm ./weed
+
+certstrap:
+ go get github.com/square/certstrap
+ certstrap --depot-path compose/tls init --passphrase "" --common-name "SeaweedFS CA" || true
+ certstrap --depot-path compose/tls request-cert --passphrase "" --common-name volume01.dev || true
+ certstrap --depot-path compose/tls request-cert --passphrase "" --common-name master01.dev || true
+ certstrap --depot-path compose/tls request-cert --passphrase "" --common-name filer01.dev || true
+ certstrap --depot-path compose/tls request-cert --passphrase "" --common-name client01.dev || true
+ certstrap --depot-path compose/tls sign --CA "SeaweedFS CA" volume01.dev || true
+ certstrap --depot-path compose/tls sign --CA "SeaweedFS CA" master01.dev || true
+ certstrap --depot-path compose/tls sign --CA "SeaweedFS CA" filer01.dev || true
+ certstrap --depot-path compose/tls sign --CA "SeaweedFS CA" client01.dev || true \ No newline at end of file
diff --git a/docker/compose/dev.env b/docker/compose/dev.env
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/docker/compose/dev.env
diff --git a/docker/compose/local-dev-compose.yml b/docker/compose/local-dev-compose.yml
index 18cccab3e..05103a7fc 100644
--- a/docker/compose/local-dev-compose.yml
+++ b/docker/compose/local-dev-compose.yml
@@ -6,33 +6,49 @@ services:
ports:
- 9333:9333
- 19333:19333
- command: "master -ip=master"
+ command: "-v=1 master -ip=master"
+ volumes:
+ - ./tls:/etc/seaweedfs/tls
+ env_file:
+ - ${ENV_FILE:-dev.env}
volume:
image: chrislusf/seaweedfs:local
ports:
- 8080:8080
- 18080:18080
- command: "volume -mserver=master:9333 -port=8080 -ip=volume -preStopSeconds=1"
+ command: "-v=1 volume -mserver=master:9333 -port=8080 -ip=volume -preStopSeconds=1"
depends_on:
- master
+ volumes:
+ - ./tls:/etc/seaweedfs/tls
+ env_file:
+ - ${ENV_FILE:-dev.env}
filer:
image: chrislusf/seaweedfs:local
ports:
- 8888:8888
- 18888:18888
- command: 'filer -master="master:9333"'
+ command: '-v=1 filer -master="master:9333"'
depends_on:
- master
- volume
+ volumes:
+ - ./tls:/etc/seaweedfs/tls
+ env_file:
+ - ${ENV_FILE:-dev.env}
s3:
image: chrislusf/seaweedfs:local
ports:
- 8333:8333
- command: 's3 -filer="filer:8888"'
+ command: '-v=1 s3 -filer="filer:8888"'
depends_on:
- master
- volume
- filer
+ volumes:
+ - ./tls:/etc/seaweedfs/tls
+ env_file:
+ - ${ENV_FILE:-dev.env}
mount:
image: chrislusf/seaweedfs:local
privileged: true
@@ -40,6 +56,10 @@ services:
- SYS_ADMIN
mem_limit: 4096m
command: '-v=4 mount -filer="filer:8888" -dirAutoCreate -dir=/mnt/seaweedfs -cacheCapacityMB=100 -concurrentWriters=128'
+ volumes:
+ - ./tls:/etc/seaweedfs/tls
+ env_file:
+ - ${ENV_FILE:-dev.env}
depends_on:
- master
- volume
diff --git a/docker/compose/tls.env b/docker/compose/tls.env
new file mode 100644
index 000000000..a82954c4f
--- /dev/null
+++ b/docker/compose/tls.env
@@ -0,0 +1,14 @@
+WEED_GRPC_CA=/etc/seaweedfs/tls/SeaweedFS_CA.crt
+WEED_GRPC_ALLOWED_WILDCARD_DOMAIN=".dev"
+WEED_GRPC_MASTER_CERT=/etc/seaweedfs/tls/master01.dev.crt
+WEED_GRPC_MASTER_KEY=/etc/seaweedfs/tls/master01.dev.key
+WEED_GRPC_VOLUME_CERT=/etc/seaweedfs/tls/volume01.dev.crt
+WEED_GRPC_VOLUME_KEY=/etc/seaweedfs/tls/volume01.dev.key
+WEED_GRPC_FILER_CERT=/etc/seaweedfs/tls/filer01.dev.crt
+WEED_GRPC_FILER_KEY=/etc/seaweedfs/tls/filer01.dev.key
+WEED_GRPC_CLIENT_CERT=/etc/seaweedfs/tls/client01.dev.crt
+WEED_GRPC_CLIENT_KEY=/etc/seaweedfs/tls/client01.dev.key
+WEED_GRPC_MASTER_ALLOWED_COMMONNAMES="volume01.dev,master01.dev,filer01.dev,client01.dev"
+WEED_GRPC_VOLUME_ALLOWED_COMMONNAMES="volume01.dev,master01.dev,filer01.dev,client01.dev"
+WEED_GRPC_FILER_ALLOWED_COMMONNAMES="volume01.dev,master01.dev,filer01.dev,client01.dev"
+WEED_GRPC_CLIENT_ALLOWED_COMMONNAMES="volume01.dev,master01.dev,filer01.dev,client01.dev" \ No newline at end of file
diff --git a/go.mod b/go.mod
index 3c75a9cb5..f7b869281 100644
--- a/go.mod
+++ b/go.mod
@@ -38,6 +38,7 @@ require (
github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.7.4
github.com/gorilla/websocket v1.4.1 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.11.0 // indirect
github.com/jcmturner/gofork v1.0.0 // indirect
github.com/json-iterator/go v1.1.10
diff --git a/note/SeaweedFS_Architecture.png b/note/SeaweedFS_Architecture.png
index de27a5f53..f960dc3e1 100644
--- a/note/SeaweedFS_Architecture.png
+++ b/note/SeaweedFS_Architecture.png
Binary files differ
diff --git a/note/SeaweedFS_Cluster_Backup.png b/note/SeaweedFS_Cluster_Backup.png
new file mode 100644
index 000000000..d8f2e19ff
--- /dev/null
+++ b/note/SeaweedFS_Cluster_Backup.png
Binary files differ
diff --git a/note/SeaweedFS_XDR.png b/note/SeaweedFS_XDR.png
new file mode 100644
index 000000000..24e468a62
--- /dev/null
+++ b/note/SeaweedFS_XDR.png
Binary files differ
diff --git a/weed/command/master.go b/weed/command/master.go
index d569919cd..fb58cfefd 100644
--- a/weed/command/master.go
+++ b/weed/command/master.go
@@ -138,7 +138,6 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) {
if err != nil {
glog.Fatalf("master failed to listen on grpc port %d: %v", grpcPort, err)
}
- // Create your protocol servers.
grpcS := pb.NewGrpcServer(security.LoadServerTLS(util.GetViper(), "grpc.master"))
master_pb.RegisterSeaweedServer(grpcS, ms)
protobuf.RegisterRaftServer(grpcS, raftServer)
diff --git a/weed/command/scaffold.go b/weed/command/scaffold.go
index c2d53e4bd..07d448042 100644
--- a/weed/command/scaffold.go
+++ b/weed/command/scaffold.go
@@ -440,22 +440,28 @@ expires_after_seconds = 10 # seconds
# the host name is not checked, so the PERM files can be shared.
[grpc]
ca = ""
+# Set wildcard domain for enable TLS authentication by common names
+allowed_wildcard_domain = "" # .mycompany.com
[grpc.volume]
cert = ""
key = ""
+allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.master]
cert = ""
key = ""
+allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.filer]
cert = ""
key = ""
+allowed_commonNames = "" # comma-separated SSL certificate common names
[grpc.msg_broker]
cert = ""
key = ""
+allowed_commonNames = "" # comma-separated SSL certificate common names
# use this for any place needs a grpc client
# i.e., "weed backup|benchmark|filer.copy|filer.replicate|mount|s3|upload"
@@ -463,7 +469,6 @@ key = ""
cert = ""
key = ""
-
# volume server https options
# Note: work in progress!
# this does not work with other clients, e.g., "weed filer|mount" etc, yet.
diff --git a/weed/filer/stream.go b/weed/filer/stream.go
index 6a87a2b7d..573ab65e8 100644
--- a/weed/filer/stream.go
+++ b/weed/filer/stream.go
@@ -15,7 +15,7 @@ import (
func StreamContent(masterClient wdclient.HasLookupFileIdFunction, w io.Writer, chunks []*filer_pb.FileChunk, offset int64, size int64) error {
- // fmt.Printf("start to stream content for chunks: %+v\n", chunks)
+ glog.V(9).Infof("start to stream content for chunks: %+v\n", chunks)
chunkViews := ViewFromChunks(masterClient.GetLookupFileIdFunction(), chunks, offset, size)
fileId2Url := make(map[string][]string)
@@ -26,6 +26,9 @@ func StreamContent(masterClient wdclient.HasLookupFileIdFunction, w io.Writer, c
if err != nil {
glog.V(1).Infof("operation LookupFileId %s failed, err: %v", chunkView.FileId, err)
return err
+ } else if len(urlStrings) == 0 {
+ glog.Errorf("operation LookupFileId %s failed, err: urls not found", chunkView.FileId)
+ return fmt.Errorf("operation LookupFileId %s failed, err: urls not found", chunkView.FileId)
}
fileId2Url[chunkView.FileId] = urlStrings
}
@@ -39,6 +42,7 @@ func StreamContent(masterClient wdclient.HasLookupFileIdFunction, w io.Writer, c
glog.Errorf("read chunk: %v", err)
return fmt.Errorf("read chunk: %v", err)
}
+
_, err = w.Write(data)
if err != nil {
glog.Errorf("write chunk: %v", err)
diff --git a/weed/s3api/filer_util.go b/weed/s3api/filer_util.go
index 3626ece98..12a74126a 100644
--- a/weed/s3api/filer_util.go
+++ b/weed/s3api/filer_util.go
@@ -31,6 +31,10 @@ func (s3a *S3ApiServer) list(parentDirectoryPath, prefix, startFrom string, incl
return nil
}, startFrom, inclusive, limit)
+ if len(entries) == 0 {
+ isLast = true
+ }
+
return
}
diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go
index 03b8b3628..610daef9f 100644
--- a/weed/s3api/s3api_object_handlers.go
+++ b/weed/s3api/s3api_object_handlers.go
@@ -9,6 +9,7 @@ import (
"io"
"io/ioutil"
"net/http"
+ "net/url"
"sort"
"strings"
@@ -70,7 +71,7 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request)
return
}
} else {
- uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
+ uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object))
etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader)
@@ -85,6 +86,14 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request)
writeSuccessResponseEmpty(w)
}
+func urlPathEscape(object string) string {
+ var escapedParts []string
+ for _, part := range strings.Split(object, "/") {
+ escapedParts = append(escapedParts, url.PathEscape(part))
+ }
+ return strings.Join(escapedParts, "/")
+}
+
func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
bucket, object := getBucketAndObject(r)
@@ -95,7 +104,7 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request)
}
destUrl := fmt.Sprintf("http://%s%s/%s%s",
- s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
+ s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object))
s3a.proxyToFiler(w, r, destUrl, passThroughResponse)
@@ -106,7 +115,7 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request
bucket, object := getBucketAndObject(r)
destUrl := fmt.Sprintf("http://%s%s/%s%s",
- s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
+ s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object))
s3a.proxyToFiler(w, r, destUrl, passThroughResponse)
@@ -117,7 +126,7 @@ func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Reque
bucket, object := getBucketAndObject(r)
destUrl := fmt.Sprintf("http://%s%s/%s%s?recursive=true",
- s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
+ s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object))
s3a.proxyToFiler(w, r, destUrl, func(proxyResponse *http.Response, w http.ResponseWriter) {
for k, v := range proxyResponse.Header {
diff --git a/weed/s3api/s3api_object_handlers_postpolicy.go b/weed/s3api/s3api_object_handlers_postpolicy.go
index 044e732db..035302ae6 100644
--- a/weed/s3api/s3api_object_handlers_postpolicy.go
+++ b/weed/s3api/s3api_object_handlers_postpolicy.go
@@ -110,7 +110,7 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
}
}
- uploadUrl := fmt.Sprintf("http://%s%s/%s/%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
+ uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object))
etag, errCode := s3a.putToFiler(r, uploadUrl, fileBody)
diff --git a/weed/security/tls.go b/weed/security/tls.go
index b4bf84e2d..7d3ffcdca 100644
--- a/weed/security/tls.go
+++ b/weed/security/tls.go
@@ -1,10 +1,16 @@
package security
import (
+ "context"
"crypto/tls"
"crypto/x509"
"github.com/chrislusf/seaweedfs/weed/util"
+ grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/peer"
+ "google.golang.org/grpc/status"
"io/ioutil"
+ "strings"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@@ -12,21 +18,29 @@ import (
"github.com/chrislusf/seaweedfs/weed/glog"
)
-func LoadServerTLS(config *util.ViperProxy, component string) grpc.ServerOption {
+type Authenticator struct {
+ AllowedWildcardDomain string
+ AllowedCommonNames map[string]bool
+}
+
+func LoadServerTLS(config *util.ViperProxy, component string) (grpc.ServerOption, grpc.ServerOption) {
if config == nil {
- return nil
+ return nil, nil
}
// load cert/key, ca cert
cert, err := tls.LoadX509KeyPair(config.GetString(component+".cert"), config.GetString(component+".key"))
if err != nil {
- glog.V(1).Infof("load cert/key error: %v", err)
- return nil
+ glog.V(1).Infof("load cert: %s / key: %s error: %v",
+ config.GetString(component+".cert"),
+ config.GetString(component+".key"),
+ err)
+ return nil, nil
}
caCert, err := ioutil.ReadFile(config.GetString("grpc.ca"))
if err != nil {
- glog.V(1).Infof("read ca cert file error: %v", err)
- return nil
+ glog.V(1).Infof("read ca cert file %s error: %v", config.GetString("grpc.ca"), err)
+ return nil, nil
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
@@ -36,7 +50,20 @@ func LoadServerTLS(config *util.ViperProxy, component string) grpc.ServerOption
ClientAuth: tls.RequireAndVerifyClientCert,
})
- return grpc.Creds(ta)
+ allowedCommonNames := config.GetString(component + ".allowed_commonNames")
+ allowedWildcardDomain := config.GetString("grpc.allowed_wildcard_domain")
+ if allowedCommonNames != "" || allowedWildcardDomain != "" {
+ allowedCommonNamesMap := make(map[string]bool)
+ for _, s := range strings.Split(allowedCommonNames, ",") {
+ allowedCommonNamesMap[s] = true
+ }
+ auther := Authenticator{
+ AllowedCommonNames: allowedCommonNamesMap,
+ AllowedWildcardDomain: allowedWildcardDomain,
+ }
+ return grpc.Creds(ta), grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(auther.Authenticate))
+ }
+ return grpc.Creds(ta), nil
}
func LoadClientTLS(config *util.ViperProxy, component string) grpc.DialOption {
@@ -70,3 +97,28 @@ func LoadClientTLS(config *util.ViperProxy, component string) grpc.DialOption {
})
return grpc.WithTransportCredentials(ta)
}
+
+func (a Authenticator) Authenticate(ctx context.Context) (newCtx context.Context, err error) {
+ p, ok := peer.FromContext(ctx)
+ if !ok {
+ return ctx, status.Error(codes.Unauthenticated, "no peer found")
+ }
+
+ tlsAuth, ok := p.AuthInfo.(credentials.TLSInfo)
+ if !ok {
+ return ctx, status.Error(codes.Unauthenticated, "unexpected peer transport credentials")
+ }
+ if len(tlsAuth.State.VerifiedChains) == 0 || len(tlsAuth.State.VerifiedChains[0]) == 0 {
+ return ctx, status.Error(codes.Unauthenticated, "could not verify peer certificate")
+ }
+
+ commonName := tlsAuth.State.VerifiedChains[0][0].Subject.CommonName
+ if a.AllowedWildcardDomain != "" && strings.HasSuffix(commonName, a.AllowedWildcardDomain) {
+ return ctx, nil
+ }
+ if _, ok := a.AllowedCommonNames[commonName]; ok {
+ return ctx, nil
+ }
+
+ return ctx, status.Errorf(codes.Unauthenticated, "invalid subject common name: %s", commonName)
+}
diff --git a/weed/server/filer_server_handlers_read.go b/weed/server/filer_server_handlers_read.go
index e210f4bdf..892a732f7 100644
--- a/weed/server/filer_server_handlers_read.go
+++ b/weed/server/filer_server_handlers_read.go
@@ -156,7 +156,9 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request,
}
if offset+size <= int64(len(entry.Content)) {
_, err := writer.Write(entry.Content[offset : offset+size])
- glog.Errorf("failed to write entry content: %v", err)
+ if err != nil {
+ glog.Errorf("failed to write entry content: %v", err)
+ }
return err
}
return filer.StreamContent(fs.filer.MasterClient, writer, entry.Chunks, offset, size)