mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
Merge pull request #1872 from kmlebedev/grpc_auth
TLS allowed CommonNames
This commit is contained in:
commit
105fcd7bfc
|
@ -5,7 +5,7 @@ all: gen
|
||||||
gen: dev
|
gen: dev
|
||||||
|
|
||||||
build:
|
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 .
|
docker build --no-cache -t chrislusf/seaweedfs:local -f Dockerfile.local .
|
||||||
rm ./weed
|
rm ./weed
|
||||||
|
|
||||||
|
@ -15,6 +15,9 @@ s3tests_build:
|
||||||
dev: build
|
dev: build
|
||||||
docker-compose -f compose/local-dev-compose.yml -p seaweedfs up
|
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
|
dev_mount: build
|
||||||
docker-compose -f compose/local-mount-compose.yml -p seaweedfs up
|
docker-compose -f compose/local-mount-compose.yml -p seaweedfs up
|
||||||
|
|
||||||
|
@ -41,3 +44,15 @@ filer_etcd: build
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm ./weed
|
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
|
0
docker/compose/dev.env
Normal file
0
docker/compose/dev.env
Normal file
|
@ -6,33 +6,49 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- 9333:9333
|
- 9333:9333
|
||||||
- 19333:19333
|
- 19333:19333
|
||||||
command: "master -ip=master"
|
command: "-v=1 master -ip=master"
|
||||||
|
volumes:
|
||||||
|
- ./tls:/etc/seaweedfs/tls
|
||||||
|
env_file:
|
||||||
|
- ${ENV_FILE:-dev.env}
|
||||||
volume:
|
volume:
|
||||||
image: chrislusf/seaweedfs:local
|
image: chrislusf/seaweedfs:local
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
- 18080:18080
|
- 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:
|
depends_on:
|
||||||
- master
|
- master
|
||||||
|
volumes:
|
||||||
|
- ./tls:/etc/seaweedfs/tls
|
||||||
|
env_file:
|
||||||
|
- ${ENV_FILE:-dev.env}
|
||||||
filer:
|
filer:
|
||||||
image: chrislusf/seaweedfs:local
|
image: chrislusf/seaweedfs:local
|
||||||
ports:
|
ports:
|
||||||
- 8888:8888
|
- 8888:8888
|
||||||
- 18888:18888
|
- 18888:18888
|
||||||
command: 'filer -master="master:9333"'
|
command: '-v=1 filer -master="master:9333"'
|
||||||
depends_on:
|
depends_on:
|
||||||
- master
|
- master
|
||||||
- volume
|
- volume
|
||||||
|
volumes:
|
||||||
|
- ./tls:/etc/seaweedfs/tls
|
||||||
|
env_file:
|
||||||
|
- ${ENV_FILE:-dev.env}
|
||||||
s3:
|
s3:
|
||||||
image: chrislusf/seaweedfs:local
|
image: chrislusf/seaweedfs:local
|
||||||
ports:
|
ports:
|
||||||
- 8333:8333
|
- 8333:8333
|
||||||
command: 's3 -filer="filer:8888"'
|
command: '-v=1 s3 -filer="filer:8888"'
|
||||||
depends_on:
|
depends_on:
|
||||||
- master
|
- master
|
||||||
- volume
|
- volume
|
||||||
- filer
|
- filer
|
||||||
|
volumes:
|
||||||
|
- ./tls:/etc/seaweedfs/tls
|
||||||
|
env_file:
|
||||||
|
- ${ENV_FILE:-dev.env}
|
||||||
mount:
|
mount:
|
||||||
image: chrislusf/seaweedfs:local
|
image: chrislusf/seaweedfs:local
|
||||||
privileged: true
|
privileged: true
|
||||||
|
@ -40,6 +56,10 @@ services:
|
||||||
- SYS_ADMIN
|
- SYS_ADMIN
|
||||||
mem_limit: 4096m
|
mem_limit: 4096m
|
||||||
command: '-v=4 mount -filer="filer:8888" -dirAutoCreate -dir=/mnt/seaweedfs -cacheCapacityMB=100 -concurrentWriters=128'
|
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:
|
depends_on:
|
||||||
- master
|
- master
|
||||||
- volume
|
- volume
|
||||||
|
|
14
docker/compose/tls.env
Normal file
14
docker/compose/tls.env
Normal file
|
@ -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"
|
|
@ -138,7 +138,6 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatalf("master failed to listen on grpc port %d: %v", grpcPort, err)
|
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"))
|
grpcS := pb.NewGrpcServer(security.LoadServerTLS(util.GetViper(), "grpc.master"))
|
||||||
master_pb.RegisterSeaweedServer(grpcS, ms)
|
master_pb.RegisterSeaweedServer(grpcS, ms)
|
||||||
protobuf.RegisterRaftServer(grpcS, raftServer)
|
protobuf.RegisterRaftServer(grpcS, raftServer)
|
||||||
|
|
|
@ -440,22 +440,28 @@ expires_after_seconds = 10 # seconds
|
||||||
# the host name is not checked, so the PERM files can be shared.
|
# the host name is not checked, so the PERM files can be shared.
|
||||||
[grpc]
|
[grpc]
|
||||||
ca = ""
|
ca = ""
|
||||||
|
# Set wildcard domain for enable TLS authentication by common names
|
||||||
|
allowed_wildcard_domain = "" # .mycompany.com
|
||||||
|
|
||||||
[grpc.volume]
|
[grpc.volume]
|
||||||
cert = ""
|
cert = ""
|
||||||
key = ""
|
key = ""
|
||||||
|
allowed_commonNames = "" # comma-separated SSL certificate common names
|
||||||
|
|
||||||
[grpc.master]
|
[grpc.master]
|
||||||
cert = ""
|
cert = ""
|
||||||
key = ""
|
key = ""
|
||||||
|
allowed_commonNames = "" # comma-separated SSL certificate common names
|
||||||
|
|
||||||
[grpc.filer]
|
[grpc.filer]
|
||||||
cert = ""
|
cert = ""
|
||||||
key = ""
|
key = ""
|
||||||
|
allowed_commonNames = "" # comma-separated SSL certificate common names
|
||||||
|
|
||||||
[grpc.msg_broker]
|
[grpc.msg_broker]
|
||||||
cert = ""
|
cert = ""
|
||||||
key = ""
|
key = ""
|
||||||
|
allowed_commonNames = "" # comma-separated SSL certificate common names
|
||||||
|
|
||||||
# use this for any place needs a grpc client
|
# use this for any place needs a grpc client
|
||||||
# i.e., "weed backup|benchmark|filer.copy|filer.replicate|mount|s3|upload"
|
# i.e., "weed backup|benchmark|filer.copy|filer.replicate|mount|s3|upload"
|
||||||
|
@ -463,7 +469,6 @@ key = ""
|
||||||
cert = ""
|
cert = ""
|
||||||
key = ""
|
key = ""
|
||||||
|
|
||||||
|
|
||||||
# volume server https options
|
# volume server https options
|
||||||
# Note: work in progress!
|
# Note: work in progress!
|
||||||
# this does not work with other clients, e.g., "weed filer|mount" etc, yet.
|
# this does not work with other clients, e.g., "weed filer|mount" etc, yet.
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
package security
|
package security
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"github.com/chrislusf/seaweedfs/weed/util"
|
"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"
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
@ -12,21 +18,29 @@ import (
|
||||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
"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 {
|
if config == nil {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// load cert/key, ca cert
|
// load cert/key, ca cert
|
||||||
cert, err := tls.LoadX509KeyPair(config.GetString(component+".cert"), config.GetString(component+".key"))
|
cert, err := tls.LoadX509KeyPair(config.GetString(component+".cert"), config.GetString(component+".key"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(1).Infof("load cert/key error: %v", err)
|
glog.V(1).Infof("load cert: %s / key: %s error: %v",
|
||||||
return nil
|
config.GetString(component+".cert"),
|
||||||
|
config.GetString(component+".key"),
|
||||||
|
err)
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
caCert, err := ioutil.ReadFile(config.GetString("grpc.ca"))
|
caCert, err := ioutil.ReadFile(config.GetString("grpc.ca"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(1).Infof("read ca cert file error: %v", err)
|
glog.V(1).Infof("read ca cert file %s error: %v", config.GetString("grpc.ca"), err)
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
caCertPool := x509.NewCertPool()
|
caCertPool := x509.NewCertPool()
|
||||||
caCertPool.AppendCertsFromPEM(caCert)
|
caCertPool.AppendCertsFromPEM(caCert)
|
||||||
|
@ -36,7 +50,20 @@ func LoadServerTLS(config *util.ViperProxy, component string) grpc.ServerOption
|
||||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
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 {
|
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)
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue