Operator that reads secret configuration in a SecretTemplate custom resource, looks up secret values from a key manager, and generates a Secret.
A common issue when doing GitOps is dealing with sensitive information that should not be stored in the git repository (e.g. passwords, keys, etc). There are two different approaches to how to handle this issue:
- Inject the values from another source into kubernetes Secret(s) at deployment time
- Inject the values from another source in the pod at startup time via an InitContainer
The "other source" in this case would be a key management system that centralizes the storage and management of sensitive information.
This operator addresses the first approach listed above by pulling sensitive values from Key Protect at deployment time to generate the appropriate kubernetes Secret(s).
The operator takes a SecretTemplate custom resource(s) as input, looks up the values of any sensitive information for the provided keyIds from Key Protect, and generates a kubernetes Secret for each input template.
The input to the operator is one or more "secret templates". In this case the "secret template" provides the structure of the desired template with placeholders for the values that will be pulled from the key management system. The following provides the structure of the template:
apiVersion: keymanagement.ibm/v1
kind: SecretTemplate
metadata:
name: mysecret
annotations:
key-manager: key-protect
key-protect/instanceId: instance-id
key-protect/region: us-east
spec:
labels: {}
annotations: {}
values:
- key: url
stringData: https://ibm.com
- key: username
data: dGVhbS1jYXA=
- key: password
keyId: 36397b07-d98d-4c0b-bd7a-d6c290163684
-
The
metadata.annotations
value is optional.key-manager
- the only value supported currently iskey-protect
key-protect/instanceId
- the instance id of the key protect instance. If not provided then theinstance-id
value from thekey-protect-access
secret will be used.key-protect/region
- the region where the key protect instance has been provisioned. If not provided then theregion
value from thekey-protect-access
secret will be used.
-
The
metadata.name
value given will be used as the name for the Secret that will be generated. -
The information in
spec.labels
andspec.annotations
will be copied over as thelabels
andannotations
in the Secret that is generated -
The
spec.values
section contains the information that should be provided in thedata
section of the generated Secret. There are three possible ways the values can be provided:stringData
- the actual value can be provided directly as clear text. This would be appropriate for information that is not sensitive but is required in the secret.data
- a base64 encoded value can be provided to the secret. This can be used for large values that might present formatting issues or for information that is not sensitive but that might be obfuscated a bit (like a username).keyId
- the id (not the name) of the Standard Key that has been stored in Key Protect. The value stored in Key Protect can be anything.
Key Protect manages two different types of keys: root keys
and standard keys
. Standard keys
are used to store any
kind of protected information. The Key Protect plugin reads the contents of a standard key, identified by a given key id, and
stores the key value into a secret in the cluster.
The following steps describe how to create a standard key:
-
Open the IBM Cloud console and navigate to the Key Protect service
-
Within Key Protect, select the Manage Keys tab
-
Press the
Add key
button to open the "Add a new key" dialog -
Select the
Import your own key
radio button andStandard key
from the drop down -
Provide a descriptive name for the key and paste the base-64 encoded value of the key into the
Key material
fieldNote: A value can be encoded as base-64 from the terminal with the following command:
echo -n "{VALUE}" | base64
If you need to encode a larger value, create the value in a file and encode the entire contents of the file with:
cat {file} | base64
-
Click Import key to create the key
-
Copy the value of the ID. This will be used later by the plugin
In order to connect with Key Protect you will need three pieces of information:
IBM Cloud API Key
- an API Key that hasReader
andReaderPlus
access to the Key Protect instanceKey Protect region
- the region where the Key Protect instance has been deployedKey Protect instance id
- the GUID of the Key Protect instance where the secrets are stored
The three values can be provided in a secret named key-protect-access
in the same namespace where ArgoCD has been
deployed. As shown above, the region and instance id values can be provided in the SecretTemplate configuration. Additionally,
the default API Key will be used from the cloud-access
secret if the value is not available from the key-protect-access
secret.
-
Set the target resource group and region for the Key Protect instance.
ibmcloud target -g {RESOURCE_GROUP} -r {REGION}
-
List the available resources and find the name of the Key Protect instance.
ibmcloud resource service-instances
-
List the details for the Key Protect instance. The
Key Protect instance id
is listed asGUID
.ibmcloud resource service-instance {INSTANCE_NAME}
Armed with the information from the previous step, run the following to create a secret:
NAMESPACE="tools"
kubectl create secret generic -n ${NAMESPACE} key-protect-access \
--from-literal=api-key=${API_KEY} \
--from-literal=region=${REGION} \
--from-literal=instance-id=${KP_INSTANCE_ID}
where:
NAMESPACE
should be the namespace where ArgoCD has been deployed
- Operator SDK v1.0.1
make test
make container-build container-push
make install
make deploy
These are the steps that were performed to initialize the operator.
operator-sdk init --domain=ibm --repo=github.com/ibmgaragecloud/key-management-operator
operator-sdk create api --group keymanagement --version v1 --kind SecretTemplate --resource=true --controller=true