diff --git a/api/api.go b/api/api.go index e7fd60eab..232fa5453 100644 --- a/api/api.go +++ b/api/api.go @@ -18,6 +18,7 @@ import ( const ( Name = "name" Token = "token" + TokenSecret = "token_secret" SpecNodes = "nodes" SpecParent = "parent" SpecEphemeral = "ephemeral" diff --git a/api/server/docker.go b/api/server/docker.go index b99fc51fe..4e5e80804 100644 --- a/api/server/docker.go +++ b/api/server/docker.go @@ -13,6 +13,7 @@ import ( "github.com/libopenstorage/openstorage/api" "github.com/libopenstorage/openstorage/api/spec" "github.com/libopenstorage/openstorage/config" + osecrets "github.com/libopenstorage/openstorage/pkg/auth/secrets" "github.com/libopenstorage/openstorage/pkg/grpcserver" "github.com/libopenstorage/openstorage/pkg/options" "github.com/libopenstorage/openstorage/pkg/util" @@ -35,6 +36,8 @@ type driver struct { sdkUds string conn *grpc.ClientConn mu sync.Mutex + + secretsStore osecrets.Auth } type handshakeResp struct { @@ -73,11 +76,12 @@ type capabilitiesResponse struct { Capabilities capabilities } -func newVolumePlugin(name, sdkUds string) restServer { +func newVolumePlugin(name, sdkUds string, secretsStore osecrets.Auth) restServer { d := &driver{ - restBase: restBase{name: name, version: "0.3"}, - SpecHandler: spec.NewSpecHandler(), - sdkUds: sdkUds, + restBase: restBase{name: name, version: "0.3"}, + SpecHandler: spec.NewSpecHandler(), + sdkUds: sdkUds, + secretsStore: secretsStore, } return d } @@ -218,30 +222,60 @@ func (d *driver) handshake(w http.ResponseWriter, r *http.Request) { } func (d *driver) attachToken(ctx context.Context, request *volumeRequest) (context.Context, string) { - token, tokenInName := d.GetTokenFromString(request.Name) - if !tokenInName { - token = request.Opts[api.Token] + + // get token from name + tokenFromName, tokenInName := d.GetTokenFromString(request.Name) + if tokenInName { + return addTokenMetadata(ctx, tokenFromName), tokenFromName } - if len(token) == 0 { - return ctx, "" + + // get token from opts + tokenFromOpts := request.Opts[api.Token] + if tokenFromOpts != "" { + return addTokenMetadata(ctx, tokenFromOpts), tokenFromOpts } + // get token secret + secret, context, ok := d.GetTokenSecretFromString(request.Name) + if ok && d.secretsStore != nil { + token, err := d.secretsStore.GetToken(secret, context) + if err != nil { + return ctx, "" + } + + return addTokenMetadata(ctx, token), token + } + + return ctx, "" +} + +// addTokenMetadata adds the token to a given context's metadata +func addTokenMetadata(ctx context.Context, token string) context.Context { md := metadata.New(map[string]string{ "authorization": "bearer " + token, }) - return metadata.NewOutgoingContext(ctx, md), token + return metadata.NewOutgoingContext(ctx, md) } func (d *driver) attachTokenMount(ctx context.Context, request *mountRequest) (context.Context, string) { - token, _ := d.GetTokenFromString(request.Name) - if len(token) == 0 { - return ctx, "" + // get token from name + tokenFromName, tokenInName := d.GetTokenFromString(request.Name) + if tokenInName { + return addTokenMetadata(ctx, tokenFromName), tokenFromName } - md := metadata.New(map[string]string{ - "authorization": "bearer " + token, - }) - return metadata.NewOutgoingContext(ctx, md), token + // get token secret + secret, context, ok := d.GetTokenSecretFromString(request.Name) + if ok && d.secretsStore != nil { + token, err := d.secretsStore.GetToken(secret, context) + if err != nil { + return ctx, "" + } + + return addTokenMetadata(ctx, token), token + } + + return ctx, "" } func (d *driver) status(w http.ResponseWriter, r *http.Request) { diff --git a/api/server/server.go b/api/server/server.go index 9f4a49e17..757e8d760 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -58,6 +58,7 @@ func StartVolumeMgmtAPI( err error ) volMgmtApi := newVolumeAPI(name, sdkUds) + if auth { unixServer, portServer, err = startServerWithAuth( name, @@ -85,8 +86,19 @@ func StartVolumePluginAPI( name, sdkUds string, pluginBase string, pluginPort uint16, + authProviderType secrets.AuthTokenProviders, + authProvider osecrets.Secrets, ) error { - volPluginApi := newVolumePlugin(name, sdkUds) + var secretsStore secrets.Auth + var err error + if authProvider != nil { + secretsStore, err = secrets.NewAuth(authProviderType, authProvider) + if err != nil { + return err + } + } + + volPluginApi := newVolumePlugin(name, sdkUds, secretsStore) if _, _, err := startServer( name, pluginBase, diff --git a/api/spec/spec_handler.go b/api/spec/spec_handler.go index e7e4a33f1..71a759f60 100644 --- a/api/spec/spec_handler.go +++ b/api/spec/spec_handler.go @@ -53,6 +53,13 @@ type SpecHandler interface { // ("", false) GetTokenFromString(str string) (string, bool) + // GetTokenSecretFromString parses the token secret and context from the name. + // If the token was parsed, it returns: + // (tokenSecret, tokenSecretContext, true) + // If the token wasn't parsed, it returns: + // ("", "", false) + GetTokenSecretFromString(str string) (string, string, bool) + // SpecFromOpts parses in docker options passed in the the docker run // command of the form --opt name=value // source is populated if --opt parent= is specified. @@ -87,6 +94,7 @@ type SpecHandler interface { var ( nameRegex = regexp.MustCompile(api.Name + "=([0-9A-Za-z_-]+),?") tokenRegex = regexp.MustCompile(api.Token + "=([A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+/=]+),?") + tokenSecretRegex = regexp.MustCompile(api.TokenSecret + `=/*([0-9A-Za-z_-]+/+)*([0-9A-Za-z_-]+)/([0-9A-Za-z_-]+),?`) nodesRegex = regexp.MustCompile(api.SpecNodes + "=([A-Za-z0-9-_;]+),?") parentRegex = regexp.MustCompile(api.SpecParent + "=([A-Za-z]+),?") sizeRegex = regexp.MustCompile(api.SpecSize + "=([0-9A-Za-z]+),?") @@ -385,6 +393,17 @@ func (d *specHandler) GetTokenFromString(str string) (string, bool) { return token, ok } +func (d *specHandler) GetTokenSecretFromString(str string) (string, string, bool) { + submatches := tokenSecretRegex.FindStringSubmatch(str) + if len(submatches) < 4 { + return "", "", false + } + context := submatches[2] + secret := submatches[3] + + return secret, context, true +} + func (d *specHandler) SpecOptsFromString( str string, ) (bool, map[string]string, string) { diff --git a/api/spec/spec_handler_test.go b/api/spec/spec_handler_test.go index 79670a933..7a176b4b4 100644 --- a/api/spec/spec_handler_test.go +++ b/api/spec/spec_handler_test.go @@ -223,3 +223,44 @@ func TestCopyingLabelsFromSpecToLocator(t *testing.T) { require.Contains(t, locator.VolumeLabels, "hello") require.Contains(t, locator.VolumeLabels, "goodbye") } + +func TestGetTokenFromString(t *testing.T) { + s := NewSpecHandler() + + token := "abcd.xyz.123" + + tokenParsed, ok := s.GetTokenFromString(fmt.Sprintf("token=%s", token)) + require.Equal(t, token, tokenParsed) + require.Equal(t, ok, true) + + tokenParsed, ok = s.GetTokenFromString(fmt.Sprintf("toabcbn=%s", token)) + require.Equal(t, "", tokenParsed) + require.Equal(t, ok, false) + +} + +func TestGetTokenSecretFromString(t *testing.T) { + s := NewSpecHandler() + + secret := "token1" + context := "team1" + + sample := "name=abcd,token_secret=/px/secrets/alpha/" + context + "/" + secret + ",token=" + sample2 := "name=abcd,token_secret=padsasdax/secr//e/sdasda//ds/asda///sts/al//pha/" + context + "/" + secret + ",token=" + + secretParsed, contextParsed, ok := s.GetTokenSecretFromString(sample) + require.Equal(t, secret, secretParsed) + require.Equal(t, context, contextParsed) + require.Equal(t, ok, true) + + secretParsed, contextParsed, ok = s.GetTokenSecretFromString(sample2) + require.Equal(t, secret, secretParsed) + require.Equal(t, context, contextParsed) + require.Equal(t, ok, true) + + secretParsed, contextParsed, ok = s.GetTokenSecretFromString(fmt.Sprintf("toabcbn_secret=abcd")) + require.Equal(t, "", secretParsed) + require.Equal(t, "", contextParsed) + require.Equal(t, ok, false) + +} diff --git a/cmd/osd/main.go b/cmd/osd/main.go index aed483b5e..3a36ffc38 100644 --- a/cmd/osd/main.go +++ b/cmd/osd/main.go @@ -376,6 +376,8 @@ func start(c *cli.Context) error { d, sdksocket, volume.PluginAPIBase, uint16(pluginPort), + 0, + nil, ); err != nil { return fmt.Errorf("Unable to start plugin api server: %v", err) }