Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remaining secrets providers for auth token_secret support #1196

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
Name = "name"
Token = "token"
TokenSecret = "token_secret"
TokenSecretNamespace = "token_secret_namespace"
SpecNodes = "nodes"
SpecParent = "parent"
SpecEphemeral = "ephemeral"
Expand Down Expand Up @@ -1141,3 +1142,10 @@ func (m *VolumeStateAction) IsMount() bool {
func (m *VolumeStateAction) IsUnMount() bool {
return m.GetMount() == VolumeActionParam_VOLUME_ACTION_PARAM_OFF
}

// TokenSecretContext contains all nessesary information to get a
// token secret from any provider
type TokenSecretContext struct {
SecretName string
SecretNamespace string
}
12 changes: 6 additions & 6 deletions api/server/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type driver struct {
conn *grpc.ClientConn
mu sync.Mutex

secretsStore osecrets.Auth
secretsStore *osecrets.Auth
}

type handshakeResp struct {
Expand Down Expand Up @@ -78,7 +78,7 @@ type capabilitiesResponse struct {
Capabilities capabilities
}

func newVolumePlugin(name, sdkUds string, secretsStore osecrets.Auth) restServer {
func newVolumePlugin(name, sdkUds string, secretsStore *osecrets.Auth) restServer {
d := &driver{
restBase: restBase{name: name, version: "0.3"},
SpecHandler: spec.NewSpecHandler(),
Expand Down Expand Up @@ -263,9 +263,9 @@ func (d *driver) parseTokenInput(name string, opts map[string]string) (string, e
}

// get token secret
secretPath, ok := d.GetTokenSecretFromString(name)
tokenSecretContext, ok := d.GetTokenSecretContextFromString(name)
if ok {
token, err := d.secretTokenFromStore(secretPath)
token, err := d.secretTokenFromStore(tokenSecretContext)
if err != nil {
return "", err
}
Expand All @@ -292,12 +292,12 @@ func addTokenMetadata(ctx context.Context, token string) context.Context {

// secretTokenFromStore pulls the token from the configured secret store for
// a given secret name and context.
func (d *driver) secretTokenFromStore(secret string) (string, error) {
func (d *driver) secretTokenFromStore(req *api.TokenSecretContext) (string, error) {
if d.secretsStore == nil {
return "", fmt.Errorf("A secret was passed in, but no secrets provider has been initialized")
}

token, err := d.secretsStore.GetToken(secret, "")
token, err := d.secretsStore.GetToken(req)
if err != nil {
return "", err
}
Expand Down
36 changes: 22 additions & 14 deletions api/server/middleware_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func NewAuthMiddleware(
}

type authMiddleware struct {
provider secrets.Auth
provider *secrets.Auth
}

func (a *authMiddleware) createWithAuth(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
Expand All @@ -67,14 +67,14 @@ func (a *authMiddleware) createWithAuth(w http.ResponseWriter, r *http.Request,

spec := dcReq.GetSpec()
locator := dcReq.GetLocator()
secretName, secretContext, err := a.parseSecret(spec.VolumeLabels, locator.VolumeLabels, true)
tokenSecretContext, err := a.parseSecret(spec.VolumeLabels, locator.VolumeLabels, true)
if err != nil {
a.log(locator.Name, fn).WithError(err).Error("failed to parse secret")
dcRes.VolumeResponse = &api.VolumeResponse{Error: "failed to parse secret: " + err.Error()}
json.NewEncoder(w).Encode(&dcRes)
return
}
if secretName == "" {
if tokenSecretContext.SecretName == "" {
errorMessage := "Access denied, no secret found in the annotations of the persistent volume claim" +
" or storage class parameters"
a.log(locator.Name, fn).Error(errorMessage)
Expand All @@ -84,7 +84,7 @@ func (a *authMiddleware) createWithAuth(w http.ResponseWriter, r *http.Request,
return
}

token, err := a.provider.GetToken(secretName, secretContext)
token, err := a.provider.GetToken(tokenSecretContext)
if err != nil {
a.log(locator.Name, fn).WithError(err).Error("failed to get token")
dcRes.VolumeResponse = &api.VolumeResponse{Error: "failed to get token: " + err.Error()}
Expand Down Expand Up @@ -213,14 +213,14 @@ func (a *authMiddleware) deleteWithAuth(w http.ResponseWriter, r *http.Request,
}

volumeResponse := &api.VolumeResponse{}
secretName, secretContext, err := a.parseSecret(vols[0].Spec.VolumeLabels, vols[0].Locator.VolumeLabels, false)
tokenSecretContext, err := a.parseSecret(vols[0].Spec.VolumeLabels, vols[0].Locator.VolumeLabels, false)
if err != nil {
a.log(volumeID, fn).WithError(err).Error("failed to parse secret")
volumeResponse.Error = "failed to parse secret: " + err.Error()
json.NewEncoder(w).Encode(volumeResponse)
return
}
if secretName == "" {
if tokenSecretContext.SecretName == "" {
errorMessage := fmt.Sprintf("Error, unable to get secret information from the volume."+
" You may need to re-add the following keys as volume labels to point to the secret: %s and %s",
secrets.SecretNameKey, secrets.SecretNamespaceKey)
Expand All @@ -231,7 +231,7 @@ func (a *authMiddleware) deleteWithAuth(w http.ResponseWriter, r *http.Request,
return
}

token, err := a.provider.GetToken(secretName, secretContext)
token, err := a.provider.GetToken(tokenSecretContext)
if err != nil {
a.log(volumeID, fn).WithError(err).Error("failed to get token")
volumeResponse.Error = "failed to get token: " + err.Error()
Expand Down Expand Up @@ -315,7 +315,7 @@ func (a *authMiddleware) parseParam(r *http.Request, param string) (string, erro
func (a *authMiddleware) parseSecret(
specLabels, locatorLabels map[string]string,
fetchCOLabels bool,
) (string, string, error) {
) (*api.TokenSecretContext, error) {
if a.provider.Type() == secrets.TypeK8s && fetchCOLabels {
// For k8s fetch the actual annotations
pvcName, ok := locatorLabels[PVCNameLabelKey]
Expand All @@ -331,33 +331,41 @@ func (a *authMiddleware) parseSecret(

pvc, err := k8s.Instance().GetPersistentVolumeClaim(pvcName, pvcNamespace)
if err != nil {
return "", "", err
return nil, err
}
secretName := pvc.ObjectMeta.Annotations[secrets.SecretNameKey]
secretNamespace := pvc.ObjectMeta.Annotations[secrets.SecretNamespaceKey]

if len(secretName) == 0 {
return parseSecretFromLabels(specLabels, locatorLabels)
}
secretNamespace := pvc.ObjectMeta.Annotations[secrets.SecretNamespaceKey]

return secretName, secretNamespace, nil
return &api.TokenSecretContext{
SecretName: secretName,
SecretNamespace: secretNamespace,
}, nil
}
return parseSecretFromLabels(specLabels, locatorLabels)
}

func parseSecretFromLabels(specLabels, locatorLabels map[string]string) (string, string, error) {
func parseSecretFromLabels(specLabels, locatorLabels map[string]string) (*api.TokenSecretContext, error) {
// Locator labels take precendence
secretName := locatorLabels[secrets.SecretNameKey]
secretNamespace := locatorLabels[secrets.SecretNamespaceKey]
if secretName == "" {
secretName = specLabels[secrets.SecretNameKey]
}
if secretName == "" {
return "", "", fmt.Errorf("secret name is empty")
return nil, fmt.Errorf("secret name is empty")
}
if secretNamespace == "" {
secretNamespace = specLabels[secrets.SecretNamespaceKey]
}
return secretName, secretNamespace, nil

return &api.TokenSecretContext{
SecretName: secretName,
SecretNamespace: secretNamespace,
}, nil
}

func (a *authMiddleware) log(id, fn string) *logrus.Entry {
Expand Down
2 changes: 1 addition & 1 deletion api/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func StartVolumePluginAPI(
authProviderType secrets.AuthTokenProviders,
authProvider osecrets.Secrets,
) error {
var secretsStore secrets.Auth
var secretsStore *secrets.Auth
var err error

// Only initialize secrets store if we have a valid auth provider.
Expand Down
25 changes: 16 additions & 9 deletions api/spec/spec_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ type SpecHandler interface {
// ("", false)
GetTokenFromString(str string) (string, bool)

// GetTokenSecretFromString parses the full token secret path from the name.
// If the token was parsed, it returns:
// (tokenSecret, true)
// If the token wasn't parsed, it returns:
// ("", false)
GetTokenSecretFromString(str string) (string, bool)
// GetTokenSecretContextFromString parses the full token secret request from the name.
// If the token secret was parsed, it returns:
// (tokenSecretContext, true)
// If the token secret wasn't parsed, it returns:
// (nil, false)
GetTokenSecretContextFromString(str string) (*api.TokenSecretContext, bool)

// SpecFromOpts parses in docker options passed in the the docker run
// command of the form --opt name=value
Expand Down Expand Up @@ -95,6 +95,7 @@ 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_/]+),?`)
tokenSecretNamespaceRegex = regexp.MustCompile(api.TokenSecretNamespace + `=/*([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]+),?")
Expand Down Expand Up @@ -393,15 +394,21 @@ func (d *specHandler) GetTokenFromString(str string) (string, bool) {
return token, ok
}

func (d *specHandler) GetTokenSecretFromString(str string) (string, bool) {
func (d *specHandler) GetTokenSecretContextFromString(str string) (*api.TokenSecretContext, bool) {
submatches := tokenSecretRegex.FindStringSubmatch(str)
if len(submatches) < 2 {
return "", false
// Must at least have a secret name. All other fields are optional,
// depending on the secrets provider configured.
return nil, false
}
secret := submatches[1]
secret = strings.TrimRight(secret, "/")

return secret, true
_, secretNamespace := d.getVal(tokenSecretNamespaceRegex, str)
return &api.TokenSecretContext{
SecretName: secret,
SecretNamespace: secretNamespace,
}, true
}

func (d *specHandler) SpecOptsFromString(
Expand Down
43 changes: 26 additions & 17 deletions api/spec/spec_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,38 +239,47 @@ func TestGetTokenFromString(t *testing.T) {

}

func TestGetTokenSecretFromString(t *testing.T) {
func TestGetTokenSecretContextFromString(t *testing.T) {
s := NewSpecHandler()

tt := []struct {
InputSecret string
ExpectedSecret string
Successful bool
InputName string
ExpectedRequest api.TokenSecretContext
Successful bool
}{
{
"/px/secrets/alpha/token1",
"px/secrets/alpha/token1",
"name=abcd,token_secret=/px/secrets/alpha/token1," +
"token_secret_namespace=ns2,abcd=sd,token_secret_public_data=y," +
"token_secret_custom_data=n",
api.TokenSecretContext{
SecretName: "px/secrets/alpha/token1",
SecretNamespace: "ns2",
},
true,
}, {
"abcd/secrets/alpha//token1/",
"abcd/secrets/alpha//token1",
"name=abcd,token_secret=abcd/secrets/alpha//token1/",
api.TokenSecretContext{
SecretName: "abcd/secrets/alpha//token1",
SecretNamespace: "",
},
true,
}, {
"simplekey",
"simplekey",
"name=abcd,token_secret=simplekey",
api.TokenSecretContext{
SecretName: "simplekey",
SecretNamespace: "",
},
true,
},
}

for _, tc := range tt {
str := "name=abcd,token_secret=" + tc.InputSecret + ",token="
secretParsed, ok := s.GetTokenSecretFromString(str)
require.Equal(t, tc.ExpectedSecret, secretParsed)
require.Equal(t, ok, tc.Successful)
secretParsed, ok := s.GetTokenSecretContextFromString(tc.InputName)
require.Equal(t, tc.Successful, ok)
require.Equal(t, tc.ExpectedRequest, *secretParsed)
}

secretParsed, ok := s.GetTokenSecretFromString(fmt.Sprintf("toabcbn_secret=abcd"))
require.Equal(t, "", secretParsed)
require.Equal(t, ok, false)
_, ok := s.GetTokenSecretContextFromString(fmt.Sprintf("toabcbn_secret=abcd"))
require.Equal(t, false, ok)

}
Loading