Skip to content

Commit

Permalink
Merge pull request #813 from lpabon/oidc2
Browse files Browse the repository at this point in the history
OpenID Connect support
  • Loading branch information
lpabon authored Jan 11, 2019
2 parents f0a39e9 + c422d1c commit 573c33b
Show file tree
Hide file tree
Showing 74 changed files with 15,915 additions and 278 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ build: packr

install: packr $(OSDSANITY)-install
go install -tags "$(TAGS)" $(PKGS)
go install github.com/libopenstorage/openstorage/cmd/osd-token-generator

$(OSDSANITY):
@$(MAKE) -C cmd/osd-sanity
Expand Down
4 changes: 2 additions & 2 deletions api/server/sdk/rest_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func (s *sdkRestGateway) Start() error {
go func() {
ready <- true
var err error
if s.config.Tls != nil {
err = s.server.ListenAndServeTLS(s.config.Tls.CertFile, s.config.Tls.KeyFile)
if s.config.Security.Tls != nil {
err = s.server.ListenAndServeTLS(s.config.Security.Tls.CertFile, s.config.Security.Tls.KeyFile)
} else {
err = s.server.ListenAndServe()
}
Expand Down
16 changes: 10 additions & 6 deletions api/server/sdk/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ func newTestServer(t *testing.T) *testServer {
AlertsFilterDeleter: tester.a,
AccessOutput: ioutil.Discard,
AuditOutput: ioutil.Discard,
Tls: &TLSConfig{
CertFile: "test_certs/server-cert.pem",
KeyFile: "test_certs/server-key.pem",
Security: &SecurityConfig{
Tls: &TLSConfig{
CertFile: "test_certs/server-cert.pem",
KeyFile: "test_certs/server-key.pem",
},
},
})
assert.Nil(t, err)
Expand Down Expand Up @@ -243,9 +245,11 @@ func TestSdkWithNoVolumeDriverThenAddOne(t *testing.T) {
AlertsFilterDeleter: alert,
AccessOutput: ioutil.Discard,
AuditOutput: ioutil.Discard,
Tls: &TLSConfig{
CertFile: "test_certs/server-cert.pem",
KeyFile: "test_certs/server-key.pem",
Security: &SecurityConfig{
Tls: &TLSConfig{
CertFile: "test_certs/server-cert.pem",
KeyFile: "test_certs/server-key.pem",
},
},
})
assert.Nil(t, err)
Expand Down
66 changes: 36 additions & 30 deletions api/server/sdk/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ type TLSConfig struct {
KeyFile string
}

// SecurityConfig provides configuration for SDK auth
type SecurityConfig struct {
// Role implementation
Role role.RoleManager
// Tls configuration
Tls *TLSConfig
// Authenticators per issuer. You can register multple authenticators
// based on the "iss" string in the string. For example:
// map[string]auth.Authenticator {
// "https://accounts.google.com": googleOidc,
// "openstorage-sdk-auth: selfSigned,
// }
Authenticators map[string]auth.Authenticator
}

// ServerConfig provides the configuration to the SDK server
type ServerConfig struct {
// Net is the transport for gRPC: unix, tcp, etc.
Expand Down Expand Up @@ -82,15 +97,10 @@ type ServerConfig struct {
DriverName string
// (optional) Cluster interface
Cluster cluster.Cluster
// Role implementation
// TODO: Group all security configurations under one struct
Role role.RoleManager
// AlertsFilterDeleter
AlertsFilterDeleter alerts.FilterDeleter
// Authentication configuration
Auth *auth.JwtAuthConfig
// Tls configuration
Tls *TLSConfig
// Security configuration
Security *SecurityConfig
}

// Server is an implementation of the gRPC SDK interface
Expand All @@ -117,11 +127,10 @@ type logger struct {
type sdkGrpcServer struct {
*grpcserver.GrpcServer

restPort string
lock sync.RWMutex
name string
authenticator auth.Authenticator
config ServerConfig
restPort string
lock sync.RWMutex
name string
config ServerConfig

// Loggers
log *logrus.Entry
Expand Down Expand Up @@ -157,6 +166,11 @@ func New(config *ServerConfig) (*Server, error) {
return nil, fmt.Errorf("Must provide configuration")
}

// If no security set, initialize the object as empty
if config.Security == nil {
config.Security = &SecurityConfig{}
}

// Check if the socket is provided to enable the REST gateway to communicate
// to the unix domain socket
if len(config.Socket) == 0 {
Expand Down Expand Up @@ -196,7 +210,6 @@ func New(config *ServerConfig) (*Server, error) {
udsConfig := *config
udsConfig.Net = "unix"
udsConfig.Address = config.Socket
udsConfig.Tls = nil
udsServer, err := newSdkGrpcServer(&udsConfig)
if err != nil {
return nil, err
Expand Down Expand Up @@ -295,21 +308,13 @@ func newSdkGrpcServer(config *ServerConfig) (*sdkGrpcServer, error) {
}

// Setup authentication
var authenticator auth.Authenticator
if config.Auth != nil {
authenticator, err = auth.New(config.Auth)
if err != nil {
return nil, err
}
for issuer, _ := range config.Security.Authenticators {
log.Infof("Authentication enabled for issuer: %s", issuer)

// Check the necessary security config options are set
if config.Role == nil {
if config.Security.Role == nil {
return nil, fmt.Errorf("Must supply role manager when authentication enabled")
}

log.Info(name + " authentication enabled")
} else {
log.Info(name + " authentication disabled")
}

// Create gRPC server
Expand All @@ -329,7 +334,6 @@ func newSdkGrpcServer(config *ServerConfig) (*sdkGrpcServer, error) {
config: *config,
name: name,
log: log,
authenticator: authenticator,
clusterHandler: config.Cluster,
driverHandler: d,
alertHandler: config.AlertsFilterDeleter,
Expand Down Expand Up @@ -365,7 +369,7 @@ func newSdkGrpcServer(config *ServerConfig) (*sdkGrpcServer, error) {
s.clusterPairServer = &ClusterPairServer{
server: s,
}
s.roleServer = config.Role
s.roleServer = config.Security.Role

return s, nil
}
Expand All @@ -376,8 +380,10 @@ func (s *sdkGrpcServer) Start() error {

// Setup https if certs have been provided
opts := make([]grpc.ServerOption, 0)
if s.config.Tls != nil {
creds, err := credentials.NewServerTLSFromFile(s.config.Tls.CertFile, s.config.Tls.KeyFile)
if s.config.Net != "unix" && s.config.Security.Tls != nil {
creds, err := credentials.NewServerTLSFromFile(
s.config.Security.Tls.CertFile,
s.config.Security.Tls.KeyFile)
if err != nil {
return fmt.Errorf("Failed to create credentials from cert files: %v", err)
}
Expand All @@ -388,7 +394,7 @@ func (s *sdkGrpcServer) Start() error {
}

// Setup authentication and authorization using interceptors if auth is enabled
if s.config.Auth != nil {
if len(s.config.Security.Authenticators) != 0 {
opts = append(opts, grpc.UnaryInterceptor(
grpc_middleware.ChainUnaryServer(
s.rwlockIntercepter,
Expand Down Expand Up @@ -421,7 +427,7 @@ func (s *sdkGrpcServer) Start() error {
api.RegisterOpenStorageAlertsServer(grpcServer, s.alertsServer)
api.RegisterOpenStorageClusterPairServer(grpcServer, s.clusterPairServer)

if s.config.Role != nil {
if s.config.Security.Role != nil {
api.RegisterOpenStorageRoleServer(grpcServer, s.roleServer)
}
return grpcServer
Expand Down
62 changes: 37 additions & 25 deletions api/server/sdk/server_interceptors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ package sdk

import (
"context"
"encoding/json"
"time"

sdk_auth "github.com/libopenstorage/openstorage/pkg/auth"
"github.com/pborman/uuid"
"github.com/sirupsen/logrus"
"github.com/libopenstorage/openstorage/pkg/auth"

grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"github.com/pborman/uuid"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -62,22 +61,36 @@ func (s *sdkGrpcServer) rwlockIntercepter(

// Authenticate user and add authorization information back in the context
func (s *sdkGrpcServer) auth(ctx context.Context) (context.Context, error) {
// Obtain token from metadata in the context
token, err := grpc_auth.AuthFromMD(ctx, ContextMetadataTokenKey)
if err != nil {
return nil, err
}

// Authenticate user
claims, err := s.authenticator.AuthenticateToken(token)
// Determine issuer
issuer, err := auth.TokenIssuer(token)
if err != nil {
return nil, status.Error(codes.PermissionDenied, err.Error())
return nil, status.Errorf(codes.PermissionDenied, "Unable to obtain issuer from token: %v", err)
}

// Add authorization information back into the context so that other
// functions can get access to this information
ctx = context.WithValue(ctx, InterceptorContextTokenKey, claims)

return ctx, nil
// Authenticate user
if authenticator, ok := s.config.Security.Authenticators[issuer]; ok {
var claims *auth.Claims
claims, err = authenticator.AuthenticateToken(ctx, token)
if err == nil {
// Add authorization information back into the context so that other
// functions can get access to this information
ctx = auth.ContextSaveUserInfo(ctx, &auth.UserInfo{
Username: authenticator.Username(claims),
Claims: *claims,
})
return ctx, nil
} else {
return nil, status.Error(codes.PermissionDenied, err.Error())
}
} else {
return nil, status.Errorf(codes.PermissionDenied, "%s is not a trusted issuer", issuer)
}
}

func (s *sdkGrpcServer) loggerServerInterceptor(
Expand Down Expand Up @@ -113,28 +126,27 @@ func (s *sdkGrpcServer) authorizationServerInterceptor(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
claims, ok := ctx.Value(InterceptorContextTokenKey).(*sdk_auth.Claims)
if !ok {
return nil, status.Errorf(codes.Internal, "Authorization called without token")
userinfo, err := auth.NewUserInfoFromContext(ctx)
if err != nil {
return nil, err
}
claims := &userinfo.Claims

// Setup auditor log
claimsJSON, err := json.Marshal(claims)
if err != nil {
logrus.Warningf("Unable to unmarshal claims: %v", err)
}
log := logrus.New()
log.Out = s.auditLogOutput
logger := log.WithFields(logrus.Fields{
"name": claims.Name,
"email": claims.Email,
"role": claims.Role,
"claims": string(claimsJSON),
"method": info.FullMethod,
"username": userinfo.Username,
"subject": claims.Subject,
"name": claims.Name,
"email": claims.Email,
"roles": claims.Roles,
"groups": claims.Groups,
"method": info.FullMethod,
})

// Authorize
if err := s.roleServer.Verify(ctx, claims.Role, info.FullMethod); err != nil {
if err := s.roleServer.Verify(ctx, claims.Roles, info.FullMethod); err != nil {
logger.Infof("Access denied")
return nil, status.Errorf(
codes.PermissionDenied,
Expand Down
19 changes: 13 additions & 6 deletions api/server/testutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ func newTestServerSdk(t *testing.T) *testServer {
assert.NoError(t, err)

os.Remove(testSdkSock)
selfsignedJwt, err := auth.NewJwtAuth(&auth.JwtAuthConfig{
SharedSecret: []byte(testSharedSecret),
})
assert.NoError(t, err)
tester.sdk, err = sdk.New(&sdk.ServerConfig{
DriverName: "fake",
Net: "tcp",
Expand All @@ -138,9 +142,11 @@ func newTestServerSdk(t *testing.T) *testServer {
Socket: testSdkSock,
AccessOutput: ioutil.Discard,
AuditOutput: ioutil.Discard,
Role: rm,
Auth: &auth.JwtAuthConfig{
SharedSecret: []byte(testSharedSecret),
Security: &sdk.SecurityConfig{
Role: rm,
Authenticators: map[string]auth.Authenticator{
"testcode": selfsignedJwt,
},
},
})
assert.Nil(t, err)
Expand Down Expand Up @@ -255,9 +261,10 @@ func (s *testServer) Stop() {

func createToken(name, role, secret string) (string, error) {
claims := &sdkauth.Claims{
Name: name,
Email: "test@openstorage",
Role: role,
Issuer: "testcode",
Name: name,
Email: "test@openstorage",
Roles: []string{role},
}
signature := &sdkauth.Signature{
Key: []byte(secret),
Expand Down
Loading

0 comments on commit 573c33b

Please sign in to comment.