-
Notifications
You must be signed in to change notification settings - Fork 11
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 startup logic to enable Push-to-File #377
Changes from all commits
5ba1b31
112fb53
4c814a9
fa6ee2d
e4f9f9d
3409843
61a55ae
f1818dc
bc9fffa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,22 +6,21 @@ import ( | |
"os" | ||
"time" | ||
|
||
"github.com/cenkalti/backoff" | ||
"github.com/cyberark/conjur-authn-k8s-client/pkg/access_token/memory" | ||
"github.com/cyberark/conjur-authn-k8s-client/pkg/authenticator" | ||
authnConfigProvider "github.com/cyberark/conjur-authn-k8s-client/pkg/authenticator/config" | ||
"github.com/cyberark/conjur-authn-k8s-client/pkg/log" | ||
|
||
"github.com/cyberark/secrets-provider-for-k8s/pkg/log/messages" | ||
"github.com/cyberark/secrets-provider-for-k8s/pkg/secrets" | ||
"github.com/cyberark/secrets-provider-for-k8s/pkg/secrets/annotations" | ||
"github.com/cyberark/secrets-provider-for-k8s/pkg/secrets/clients/conjur" | ||
"github.com/cyberark/secrets-provider-for-k8s/pkg/secrets/clients/k8s" | ||
secretsConfigProvider "github.com/cyberark/secrets-provider-for-k8s/pkg/secrets/config" | ||
"github.com/cyberark/secrets-provider-for-k8s/pkg/utils" | ||
) | ||
|
||
const ( | ||
annotationsFile = "/conjur/podinfo/annotations" | ||
defaultContainerMode = "init" | ||
annotationsFilePath = "/conjur/podinfo/annotations" | ||
secretBasePath = "/conjur/secrets" | ||
) | ||
|
||
var annotationsMap map[string]string | ||
|
@@ -37,15 +36,13 @@ var envAnnotationsConversion = map[string]string{ | |
} | ||
|
||
func main() { | ||
var err error | ||
|
||
log.Info(messages.CSPFK008I, secrets.FullVersionName) | ||
|
||
// Only attempt to populate from annotations if the annotations file exists | ||
// TODO: Figure out strategy for dealing with explicit annotation file path | ||
// set by user. In that case we can't just ignore that the file is missing. | ||
if _, err := os.Stat(annotationsFile); err == nil { | ||
annotationsMap, err = annotations.NewAnnotationsFromFile(annotationsFile) | ||
// set by user. In that case we can't just ignore that the file is missing. | ||
if _, err := os.Stat(annotationsFilePath); err == nil { | ||
annotationsMap, err = annotations.NewAnnotationsFromFile(annotationsFilePath) | ||
if err != nil { | ||
printErrorAndExit(messages.CSPFK040E) | ||
} | ||
|
@@ -54,50 +51,37 @@ func main() { | |
logErrorsAndConditionalExit(errLogs, infoLogs, messages.CSPFK049E) | ||
} | ||
|
||
// Initialize Authenticator configuration | ||
// Initialize Authenticator and Secrets Provider configurations | ||
authnConfig := setupAuthnConfig() | ||
validateContainerMode(authnConfig.ContainerMode) | ||
|
||
// Initialize Secrets Provider configuration | ||
secretsConfig := setupSecretsConfig() | ||
|
||
provideConjurSecrets, err := secrets.GetProvideConjurSecretFunc(secretsConfig.StoreType) | ||
// Initialize a Conjur Secret Fetcher | ||
secretRetriever, err := conjur.NewConjurSecretRetriever(*authnConfig) | ||
if err != nil { | ||
printErrorAndExit(fmt.Sprintf(messages.CSPFK014E, err.Error())) | ||
printErrorAndExit(err.Error()) | ||
} | ||
|
||
accessToken, err := memory.NewAccessToken() | ||
if err != nil { | ||
printErrorAndExit(messages.CSPFK001E) | ||
} | ||
|
||
authn, err := authenticator.NewWithAccessToken(*authnConfig, accessToken) | ||
if err != nil { | ||
printErrorAndExit(messages.CSPFK009E) | ||
} | ||
|
||
limitedBackOff := utils.NewLimitedBackOff( | ||
provideSecrets, errs := secrets.NewProviderForType( | ||
k8s.RetrieveK8sSecret, | ||
k8s.UpdateK8sSecret, | ||
secretRetriever.Retrieve, | ||
secretsConfig.StoreType, | ||
secretsConfig.PodNamespace, | ||
secretsConfig.RequiredK8sSecrets, | ||
annotationsMap, | ||
) | ||
logErrorsAndConditionalExit(errs, nil, messages.CSPFK053E) | ||
|
||
provideSecrets = secrets.RetryableSecretProvider( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, I like the |
||
time.Duration(secretsConfig.RetryIntervalSec)*time.Second, | ||
secretsConfig.RetryCountLimit) | ||
|
||
err = backoff.Retry(func() error { | ||
if limitedBackOff.RetryCount() > 0 { | ||
log.Info(fmt.Sprintf(messages.CSPFK010I, limitedBackOff.RetryCount(), limitedBackOff.RetryLimit)) | ||
} | ||
|
||
return provideSecretsToTarget(authn, provideConjurSecrets, accessToken, secretsConfig) | ||
}, limitedBackOff) | ||
secretsConfig.RetryCountLimit, | ||
provideSecrets, | ||
) | ||
|
||
err = provideSecrets() | ||
if err != nil { | ||
log.Error(messages.CSPFK038E) | ||
|
||
// Deleting the retrieved Conjur access token in case we got an error after retrieval. | ||
// if the access token is already deleted the action should not fail | ||
err = accessToken.Delete() | ||
if err != nil { | ||
log.Error(messages.CSPFK003E, err) | ||
} | ||
printErrorAndExit(messages.CSPFK039E) | ||
printErrorAndExit(fmt.Sprintf(messages.CSPFK039E, secretsConfig.StoreType)) | ||
} | ||
} | ||
|
||
|
@@ -140,28 +124,6 @@ func setupSecretsConfig() *secretsConfigProvider.Config { | |
return secretsConfigProvider.NewConfig(secretsProviderSettings) | ||
} | ||
|
||
func provideSecretsToTarget(authn *authenticator.Authenticator, provideConjurSecrets secrets.ProvideConjurSecrets, | ||
accessToken *memory.AccessToken, secretsConfig *secretsConfigProvider.Config) error { | ||
log.Info(fmt.Sprintf(messages.CSPFK001I, authn.Config.Username)) | ||
err := authn.Authenticate() | ||
if err != nil { | ||
return log.RecordedError(messages.CSPFK010E) | ||
} | ||
|
||
err = provideConjurSecrets(accessToken, secretsConfig) | ||
if err != nil { | ||
return log.RecordedError(messages.CSPFK016E) | ||
} | ||
|
||
err = accessToken.Delete() | ||
if err != nil { | ||
return log.RecordedError(messages.CSPFK003E, err.Error()) | ||
} | ||
|
||
log.Info(messages.CSPFK009I) | ||
return nil | ||
} | ||
|
||
func printErrorAndExit(errorMessage string) { | ||
log.Error(errorMessage) | ||
os.Exit(1) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,7 +55,7 @@ const CSPFK037E string = "CSPFK037E Failed to parse DAP/Conjur variable IDs" | |
|
||
// General | ||
const CSPFK038E string = "CSPFK038E Retransmission backoff exhausted" | ||
const CSPFK039E string = "CSPFK039E Secrets Provider for Kubernetes failed to update Kubernetes Secrets" | ||
const CSPFK039E string = "CSPFK039E Secrets Provider for Kubernetes failed to update secrets in %s mode" | ||
|
||
// Annotations | ||
const CSPFK040E string = "CSPFK040E Failed to parse annotations file" | ||
|
@@ -74,3 +74,6 @@ const CSPFK049E string = "CSPFK049E Failed to validate Pod annotations" | |
const CSPFK050E string = "CSPFK050E Failed to unmarshal Push-to-File secrets. Reason: %s" | ||
const CSPFK051E string = "CSPFK051E Unknown file format '%s'" | ||
const CSPFK052E string = "CSPFK052E Failed to retrieve secrets. Reason: %s" | ||
|
||
const CSPFK053E string = "CSPFK053E Unable to initialize Secrets Provider: unable to create secret group collection" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh-oh, I used the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Branching thought off of this... This file has all error messages numbered sequentially, but it also has them loosely grouped by area of concern, separated by comments, ie. I've added I think if the messages here are to be grouped by area of concern, then maybe they should be numbered within their area of concern: const CSPFK-A001E string = "first message concerned with aspect A"
const CSPFK-A002E string = "second message concerned with aspect A"
const CSPFK-B001E string = "first message concerned with aspect B" This also might help to avoid collisions like this one! (EDIT: not this one exactly, because we're both adding to the same concern group w/ |
||
const CSPFK054E string = "CSPFK054E Unable to initialize Secrets Provider: unrecognized Store Type '%s'" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ import ( | |
"fmt" | ||
"strings" | ||
|
||
"github.com/cyberark/conjur-authn-k8s-client/pkg/access_token" | ||
"github.com/cyberark/conjur-authn-k8s-client/pkg/log" | ||
"gopkg.in/yaml.v2" | ||
v1 "k8s.io/api/core/v1" | ||
|
@@ -32,40 +31,45 @@ type K8sSecretsMap struct { | |
PathMap map[string][]string | ||
} | ||
|
||
/* | ||
This method is implemented for implementing the ProvideConjurSecrets interface. All that is done here is to | ||
initialize a K8sSecretsClient and use the internal `run` method. | ||
That method receives different structs as inputs so they can be mocked. | ||
*/ | ||
func ProvideConjurSecretsToK8sSecrets(AccessToken access_token.AccessToken, config *config.Config) error { | ||
return run( | ||
k8s.RetrieveK8sSecret, | ||
k8s.UpdateK8sSecret, | ||
config.PodNamespace, | ||
config.RequiredK8sSecrets, | ||
AccessToken, | ||
conjur.RetrieveConjurSecrets, | ||
) | ||
type k8sProvider struct { | ||
retrieveK8sSecret k8s.RetrieveK8sSecretFunc | ||
updateK8sSecret k8s.UpdateK8sSecretFunc | ||
retrieveSecretsFunc conjur.RetrieveSecretsFunc | ||
podNamespace string | ||
requiredK8sSecrets []string | ||
} | ||
|
||
func run(retrieveSecretFunc k8s.RetrieveK8sSecretFunc, updateSecretFunc k8s.UpdateK8sSecretFunc, namespace string, requiredK8sSecrets []string, accessToken access_token.AccessToken, retrieveConjurSecretsFunc conjur.RetrieveConjurSecretsFunc) error { | ||
k8sSecretsMap, err := RetrieveRequiredK8sSecrets(retrieveSecretFunc, namespace, requiredK8sSecrets) | ||
|
||
if err != nil { | ||
return log.RecordedError(messages.CSPFK021E) | ||
// NewProvider creates a new secret provider for K8s Secrets mode. | ||
func NewProvider( | ||
retrievek8sSecret k8s.RetrieveK8sSecretFunc, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function |
||
updatek8sSecret k8s.UpdateK8sSecretFunc, | ||
retrieveSecretsFunc conjur.RetrieveSecretsFunc, | ||
requiredK8sSecrets []string, | ||
podNamespace string, | ||
) k8sProvider { | ||
return k8sProvider{ | ||
retrieveK8sSecret: retrievek8sSecret, | ||
updateK8sSecret: updatek8sSecret, | ||
retrieveSecretsFunc: retrieveSecretsFunc, | ||
podNamespace: podNamespace, | ||
requiredK8sSecrets: requiredK8sSecrets, | ||
} | ||
} | ||
|
||
// Provide implements a ProviderFunc to retrieve and push secrets to K8s secrets. | ||
func (p k8sProvider) Provide() error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Method |
||
k8sSecretsMap, err := RetrieveRequiredK8sSecrets(p.retrieveK8sSecret, p.podNamespace, p.requiredK8sSecrets) | ||
|
||
accessTokenData, err := accessToken.Read() | ||
if err != nil { | ||
return log.RecordedError(messages.CSPFK002E) | ||
return log.RecordedError(messages.CSPFK021E) | ||
} | ||
|
||
variableIDs, err := getVariableIDsToRetrieve(k8sSecretsMap.PathMap) | ||
if err != nil { | ||
return log.RecordedError(messages.CSPFK037E) | ||
} | ||
|
||
retrievedConjurSecrets, err := retrieveConjurSecretsFunc(accessTokenData, variableIDs) | ||
retrievedConjurSecrets, err := p.retrieveSecretsFunc(variableIDs) | ||
if err != nil { | ||
return log.RecordedError(messages.CSPFK034E, err.Error()) | ||
} | ||
|
@@ -75,12 +79,13 @@ func run(retrieveSecretFunc k8s.RetrieveK8sSecretFunc, updateSecretFunc k8s.Upda | |
return log.RecordedError(messages.CSPFK027E) | ||
} | ||
|
||
err = UpdateRequiredK8sSecrets(updateSecretFunc, namespace, k8sSecretsMap) | ||
err = UpdateRequiredK8sSecrets(p.updateK8sSecret, p.podNamespace, k8sSecretsMap) | ||
|
||
if err != nil { | ||
return log.RecordedError(messages.CSPFK023E) | ||
} | ||
|
||
log.Info(messages.CSPFK009I) | ||
return nil | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the annotation file path be anything other than
/conjur/podinfo
? I don't think it makes sense that the file path would be user configurable...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is actually from #367 by @doodlesbykumbi. GH did a weird auto-rebase on my branch, but I've corrected it since.
I think I would agree with you here, though. The location of the annotations file seems like an internal detail rather than something the user should be concerned with. I feel differently about a configurable secrets file base path, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both of the fixed paths
/conjur/podinfo
and the/conjur/secrets
are from the perspective of the Secrets Provider container only. For the application container, the user can mount the corresponding volumes at any arbitrarymountPath
s (though they might not care about the podinfo volume).We intentionally left out "config knobs" for these SP
mountPaths
to simplify the user experience and so that we have two less things that can get misconfigured.With that in mind, we can probably change
defaultAnnotationsFile
back toannotationsFile
, anddefaultSecretBasePath
tosecretBasePath
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My motivation for making these values configurable is for
We don't have to expose these as config knobs to the user, but I think the hardcoded value should only be present in
./main.go
.I opted for
defaultAnnotationsFile
vsannotationsFile
to specifically call out the fact that this is the default, but perhaps that causes some confusion due to the word "default" implying that there are other options. Perhaps a better name would befixedAnnotationsFilePath
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to
annotationsFilePath
andsecretBasePath
. When they're made "configurable" in the context of testing/dry run, then we can change it to something more suitable.