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

Allow use of GSM for communal credential secret #458

Merged
merged 17 commits into from
Jul 26, 2023

Conversation

sivaalamp
Copy link
Contributor

@sivaalamp sivaalamp commented Jul 17, 2023

This adds a feature flag to get the secret for the communal credential from google secret manager (GSM) instead of from a k8s secret.

It is off by default. You must set the following annotation in the VerticaDB CR:

kind: VerticaDB
metadata:
  name: v
  annotations:
    vertica.com/use-gcp-secret-manager: "true" 
spec:
  communal:
    credentialSecret: <full name of the secret in GSM>
...

The name of the GSM secret is taken from the existing spec.communal.credentialSecret field.

@CLAassistant
Copy link

CLAassistant commented Jul 17, 2023

CLA assistant check
All committers have signed the CLA.

// This is a feature flag for accessing the secrets configured in Google Secret Manager.
// This is set to true by default to let the verticadb running cluster to access the secrets.
// The value of this annotation is treated as a boolean.
GcpGsmAnnotation = "vertica.com/gcpgsmaccess"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest using '-' to make the annotation more readable:

Suggested change
GcpGsmAnnotation = "vertica.com/gcpgsmaccess"
GcpGsmAnnotation = "vertica.com/use-gcp-secret-manager"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the annotation

Comment on lines 60 to 61
// UseGCPSecretManager returns true nk all admin commands should use the vclusterOps
// library rather than admintools.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a copy/paste error

Suggested change
// UseGCPSecretManager returns true nk all admin commands should use the vclusterOps
// library rather than admintools.
// UseGCPSecretManager returns true if access to the communal secret should go
// through Google's secret manager rather the fetching the secret from k8s meta-data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the appropriate comments

@@ -37,6 +37,12 @@ const (
// treated as a boolean.
VClusterOpsAnnotation = "vertica.com/vcluster-ops"
VClusterOpsAnnotationTrue = "true"

// This is a feature flag for accessing the secrets configured in Google Secret Manager.
// This is set to true by default to let the verticadb running cluster to access the secrets.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is false, but it's picked when calling the helper function. I would suggest removing this comment line all together then.

Suggested change
// This is set to true by default to let the verticadb running cluster to access the secrets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value is changed to false now and removed the comment about the same

pkg/controllers/vdb/init_db.go Show resolved Hide resolved
@@ -269,12 +279,55 @@ func (g *GenericDatabaseInitializer) setKerberosAuthParms() {
g.ConfigurationParams.Set("KerberosEnableKeytabPermissionCheck", "0")
}

func (g *GenericDatabaseInitializer) setAuthFromGCSSecret(ctx context.Context) (string, ctrl.Result, error) {
name := "projects/otl-ot2-int-sb/secrets/new_vertica_secret/versions/1"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name should come from the vdb.Spec.Communal.CredentialSecret

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduced the annotations to read the project id and secret name


result, err := client.AccessSecretVersion(ctx, req)
if err != nil {
g.Log.Info("google: could not find default credentials")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the error message mean? What are default credentials

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with appropriate error message

@@ -65,6 +70,11 @@ type GenericDatabaseInitializer struct {
ConfigurationParams *vtypes.CiMap
}

type GcpSecret struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets not export this struct. Also, secret is too generic. Lets call out the fact that it's for credentials.

Suggested change
type GcpSecret struct {
type gcpCredentialSecret struct {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is obsolete now as I changed it to map[string]string{} and this struct is no more present

Comment on lines 74 to 75
Accesskey string
Secretkey string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to have specific field names, or could we have used something more generic like map[string]string instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the struct and introduced map

@@ -65,6 +70,11 @@ type GenericDatabaseInitializer struct {
ConfigurationParams *vtypes.CiMap
}

type GcpSecret struct {
Accesskey string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on other comments, this may not apply anymore. But I'm including it here so I don't forget... since we use struct to unmarshal JSON, we should have JSON style comments attached with it.

Suggested change
Accesskey string
Accesskey string ``json:"accesskey"`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is obsolete now as I changed it to map[string]string{} and this struct is no more present

}

var gs GcpSecret
err = json.Unmarshal([]byte(string(result.Payload.Data)), &gs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What data type is result.Payload.Data?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is an uint8[]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think uint8 is the same as byte. Can we just pass in the payload without a cast?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No casting is done


result, err := client.AccessSecretVersion(ctx, req)
if err != nil {
g.Log.Info("google: could not find default credentials")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is it supposed to find the credentials? By credentials do you mean credentials for the GCP account?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is my understanding that the host where the operator runs has a profile setup to allow it access to the GSM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the proper logs now

Comment on lines 278 to 280
name := "projects/" + g.Vdb.Annotations[meta.GcpProjectIDAnnotation] +
"/secrets/" + g.Vdb.Annotations[meta.GcpSecNameAnnontation] +
"/versions/" + g.Vdb.Annotations[meta.GcpSecVersionAnnotation]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't use annotations to come up with then secret name. We already have a field in the VerticaDB CR for the name and should use that: spec.communal.credentialSecret

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the new annotations and used the existing credentialSecret for GSM as well

crc32c := crc32.MakeTable(crc32.Castagnoli)
checksum := int64(crc32.Checksum(result.Payload.Data, crc32c))
if checksum != *result.Payload.DataCrc32C {
return "", ctrl.Result{}, err
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err is still nil here I believe.

pkg/controllers/vdb/init_db.go Show resolved Hide resolved
@@ -15,10 +15,16 @@ apiVersion: vertica.com/v1beta1
kind: VerticaDB
metadata:
name: verticadb-sample
annotations:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets not update this file. This is used to build example text in the CSV -- you see it on opeatorhub.io for instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no change in this file now

@spilchen spilchen changed the title Implement GSM for GCP secrets Allow use of GSM for communal credential secret Jul 20, 2023
// This is a feature flag for accessing the secrets configured in Google Secret Manager.
// The value of this annotation is treated as a boolean.
GcpGsmAnnotation = "vertica.com/use-gcp-secret-manager"
GcpGsmAnnotationTrue = "true"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these? I don't see it being used anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be supplied in the yaml file to return true from UseGCPSecretManager so that it fetches gsm secrets.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But do we need this const defined in the code here? Where do we use it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the GcpGsmAnnotationTrue constant, as we can directly supply value true from the yaml file

return res, err
if meta.UseGCPSecretManager(g.Vdb.Annotations) {
auth, res, err := g.setAuthFromGCSSecret(ctx)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check res. Lets use the helper we have:

Suggested change
if err != nil {
if verrors.IsReconcileAborted {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added as suggested

pkg/controllers/vdb/init_db.go Show resolved Hide resolved
crc32c := crc32.MakeTable(crc32.Castagnoli)
checksum := int64(crc32.Checksum(result.Payload.Data, crc32c))
if checksum != *result.Payload.DataCrc32C {
return "", ctrl.Result{}, err
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we talked about this on teams, but the code here looks wrong. I think my suggestion will solve it.

defer client.Close()

req := &secretmanagerpb.AccessSecretVersionRequest{
Name: name,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest removal of the name parameter:

Suggested change
Name: name,
Name: g.Vdb.Spec.Communal.CredentialSecret,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the name variable

@@ -15,12 +15,13 @@ apiVersion: vertica.com/v1beta1
kind: VerticaDB
metadata:
name: verticadb-sample

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nit: can we just undo the changes in here entirely. I know semantically nothing changed, I would prefer if this file didn't change at all

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No changes now in the file


err = json.Unmarshal([]byte(string(result.Payload.Data)), &GcpCred)
if err != nil {
return "", ctrl.Result{}, fmt.Errorf("checksum failure, expected %d but got %d", *result.Payload.DataCrc32C, checksum)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error text does make sense here. Is this a copy+paste error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved it already

pkg/controllers/vdb/init_db.go Show resolved Hide resolved
return "", ctrl.Result{Requeue: true}, nil
}

auth := fmt.Sprintf("%s:%s", strings.TrimSuffix(string(accessKey), "\n"), strings.TrimSuffix(string(secretKey), "\n"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to strip newlines from the secrets?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the configuration parameter takes the auth value in terms of accesskey:secretkey, which forming the auth parameter to format in the required format if there are any new lines those needs to be trimmed. I already have checked, if there are new lines in the keys, nothing will happen to the string.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But did you see a case where a newline was at the end of one of the keys?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The similar trimming is done in getCommunalAuth for the accesskey and secretkey. Followed the similar way of coding.

@spilchen
Copy link
Collaborator

@sivaalamp we are almost finished with the review. There are just two remaining comments to answer and/or address.

@spilchen spilchen merged commit bf12feb into vertica:main Jul 26, 2023
19 checks passed
spilchen pushed a commit that referenced this pull request Aug 1, 2023
This is similar to #458 except we read from GSM for the superuser
password secret. We use the same feature flag as before. Here is a
sample CR to enable it.

```
kind: VerticaDB
metadata:
  name: v
  annotations:
    vertica.com/use-gcp-secret-manager: "true" 
spec:
  superuserPasswordSecret: <full name of the secret in GSM>
...
```

The name of the GSM secret is taken from the existing
`spec.superuserPasswordSecret` field.

Note, if GSM is being used we can't use the canary query for health probes
as that depends on the superuser password being a k8s secret. Instead,
we check if vertica is running by seeing if anyone is listening on the 
vertica client port.

---------

Co-authored-by: Matt Spilchen <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants