aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Lu <chrislusf@users.noreply.github.com>2021-03-10 11:17:49 -0800
committerGitHub <noreply@github.com>2021-03-10 11:17:49 -0800
commit105fcd7bfcd10a185f38abe04a4158327e082a8e (patch)
treef31d944d47452fe10354cf2fe579b0a51026067e
parent737bde5ab7bcd83002127ccc0adf07839602f714 (diff)
parent348e21a08ccc52f6837613e7765e9d815850bd6c (diff)
downloadseaweedfs-105fcd7bfcd10a185f38abe04a4158327e082a8e.tar.xz
seaweedfs-105fcd7bfcd10a185f38abe04a4158327e082a8e.zip
Merge pull request #1872 from kmlebedev/grpc_auth
TLS allowed CommonNames
-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--weed/command/master.go1
-rw-r--r--weed/command/scaffold.go7
-rw-r--r--weed/security/tls.go66
7 files changed, 119 insertions, 14 deletions
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/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/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)
+}