A BundleTemplate
allows developers to describe their own secret requirements by
designing secret values with harp
template engine attached to a CSO
compliant secret path.
A BundleTemplate
doesn't contain secret values, and could be parametrized for
common secret generation profile (region bound bundle).
It helps developers
and secret operators
to have a common language to
manage secrets in time, and with auditable requirements that could be stored as
code, BundleTemplate is a Secret as Code definition
. It will be used as
specification to generate a secret container with a Bundle
containing rendered
secret paths and matching values.
This specification declares Bundle generation template object structure.
The specification is written in YAML, in order to be human-readable, but internally converted as protobuf object.
Cloud Secret Organization is the secret management program deployed at Elastic, to solve secret management problems.
One of them concerns the secret storage, and more precisely the secret path building process.
Too many people fall in the antipattern which consist in organizing secrets by team secrets'ownership, or mirroring the company organization tree, but this is wrong ! Secrets are just managed by teams and owned by software programs.
The policy applied for secret access authorization can be considered as
proof of ownership
, not the fact that the secret is stored in a folder named by the team name.Don't forget that a company is an organic life form that is in constant move. It's easier to modify policies than redesigning a complete secret tree.
We designed a risk-based classification ordered in "rings". Each ring evaluates a threat realization impact level when a ring secrets leak occurs.
meta
(R0) - Secret used to protect secret storage (Vault authentication, etc.)infrastructure
(R1) - Infrastructure secrets can lead to multiple platform compromise.platform
(R2) - Platform secrets can lead to multiple product compromise.product
(R3) - Product related secrets shared by all instance of the same product.application
(R4) - Application secrets are instanced product in a given platform for a given quality stage.artifact
(R5) - Artifact secrets are secrets attached to an artifact (docker image, archive, etc.)
When building a BundleTemplate
, you have to distribute the secrets across the rings
according to their sensitivity to leaks.
The CSO CheatSheet can be viewed here - https://ela.st/cso
BundleTemplate
uses Kubernetes-like YAML descriptors.
apiVersion: harp.elastic.co/v1
kind: BundleTemplate
meta:
name: "Ecebootstrap"
owner: syseng@elstc.co
description: "ECE Secret Bootstrap"
spec:
namespaces:
infrastructure:
... Omitted ...
- The
apiVersion
must beharp.elastic.co/v1
- The
kind
must beBundleTemplate
- The
meta.name
must not be empty - The
meta.owner
must be an email - The
meta.description
must not be empty - The
spec
describes the concreteBundleTemplate
secret template according namespaces (rings) used.
Protobuf definition - InfrastructureNS
An infrastructure
is a consistent set of resource provided by an infrastructure
provider (IaaS).
Infrastructure secrets
are sensitive secrets attached to these resources.
spec:
namespaces:
infrastructure:
# AWS ----------------------------------------------------------------------
- provider: "aws"
account: "{{ .Values.infra.aws.account }}"
description: "ESSP AWS Account"
regions:
- name: "us-east-1"
services:
- type: "rds"
name: "adminconsole"
description: "PostgreSQL Database used for AdminConsole storage backend"
secrets:
- suffix: "accounts/root_credentials"
description: "Root Admin Account"
template: |-
{
"user": "dbroot-{{ randAlphaNum 8 }}",
"password": "{{ paranoidPassword | b64enc }}"
}
This will generate a secret like the following one :
{
"infra/aws/essp-customer1/us-east-1/rds/adminconsole/accounts/root_credentials": {
"password": "OHxLcGI4WzZuP0V+UFJcdXEkX0M0XXhiSlF6VzFtZjZhZHc3RFFCM1Y4a2x6aHVYclhwcy41Q15QU29ZSmRzdw==",
"user": "dbroot-Lta1Swae"
}
}
Protobuf definition - PlatformNS
A platform
is a regionalized consistent set of infrastructure resources working
together in order to provide services to products.
Platform regions are not necessarily infrastructure regions. A logical overlay of physical infrastructure regions could be designed.
spec:
selector:
quality: "production"
platform: "customer1"
namespaces:
platform:
# EMEA-1
- region: "emea-1"
components:
# Zookeeper ------------------------------------------------------------
- name: "zookeeper"
secrets:
- suffix: "accounts/admin_credentials"
description: "Zookeeper administrative access account"
template: |-
{
"username": "zkadmin-{{ randAlphaNum 8 }}",
"password": "{{ paranoidPassword | b64enc}}"
}
# PostgreSQL -----------------------------------------------------------
- name: "postgresql"
secrets:
- suffix: "admiconsole/admin_credentials"
description: "PostgreSQL Administrative access on Database only"
template: |-
{
"username": "dbadmin-{{ randAlphaNum 8 }}",
"password": "{{ paranoidPassword | b64enc }}"
}
# Billing --------------------------------------------------------------
- name: "billing"
secrets:
- suffix: "recurly/vendor_api_key"
description: "Recurly API Key for invoice generation"
# Not generated by workflow, requested at generation time.
vendor: true
template: |-
{
"API_KEY": "{{ .Values.vendor.recurly.api_key }}"
}
content:
"ca.pem": |-
{{ .Files.Get "billing/ca.pem" }}
This will generate 3 secrets like the following ones:
{
"platform/production/customer1/emea-1/billing/recurly/vendor_api_key": {
"API_KEY": "recurly-foo-api-123456789",
"ca.pem": "-----BEGIN CERTIFICATE-----\nMIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\nWhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\nZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\nMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\nh77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\nA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\nT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\nB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\nB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\nKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\nOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\njh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\nqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\nrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\nhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\nubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\nNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\nORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\nTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\njNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\noyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\nmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\nemyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n-----END CERTIFICATE-----"
},
"platform/production/customer1/emea-1/postgresql/admiconsole/admin_credentials": {
"password": "aGQyd2k+M3BiUzhjdExjWGQkUzR5WEpFeVI3KXg+ankxYmxGc3ZUWlo3MH1bRnU2LGZVSkxFfjZ4JWZqeih4Vg==",
"username": "dbadmin-0GISldgQ"
},
"platform/production/customer1/emea-1/zookeeper/accounts/admin_credentials": {
"password": "bUtLJDdnaDJbdHRhRUdwaDBjKmhSNzBvdTNUMmZzZ0dwL1I9WkJreVQ2a0lQdHN2WFRtUSx7WTd6PSZQWU43ew==",
"username": "zkadmin-VO8HYMFL"
}
}
quality
path component is extracted fromselector.quality
platform
path component is extracted fromselector.platform
Protobuf definition - ProductComponentNS
A product
in CSO, is a virtual concept for non-instantiable resources, and
not related to execution.
Same secrets for all platform/stage ? ⇒ it could be a product secret.
spec:
selector:
product: "ece"
version: "v1.0.0"
namespaces:
product:
- name: artifact
description: "Artifact related secrets"
secrets:
- suffix: "signature/key"
description: "Artifact signature crypto key encoded using JWK"
template: |-
{
"privateKey": "{{ $sigKey := cryptoPair "rsa" }}{{ $sigKey.Private | toJwk | b64enc }}",
"publicKey": "{{ $sigKey.Public | toJwk | b64enc }}"
}
This will generate a secret like the following one :
{
"product/ece/v1.0.0/artifact/signature/key": {
"privateKey": "eyJrdHkiOiJSU0EiLCJraWQiOiJNczQzbDN4dVBCUzhHemxWeU0yajFGSDlHeTM0MFM0bl9sYjNuNE1nNTkwPSIsIm4iOiJtM0dLRVVhX1VSX2tJM3RXaVJUVnFOQ0pueGh4TjNYSm1rS19yV3ZUeE1rMl9taXVRU29qckpwRmlGTjI4ZWJnRWpZYnkyM2VodFlEQi1SVFVaZU1jVUFZb3VaWnBlYlpsOGhWNjBPT0dmSnBxRmVKTmlnVmxUendkN1Y5RWFlTUVhbDVvUDQwbURneW5ocElaNVNVMzhCd1dvd0Vlb2JPSW8xek11UjFwMktOSmxhT0RpR1hPTDVVVzlkT1dOTVF1VUFUVFNhZElZVnZRYmlwMWZMNl96aU9udkIwdWd2TW1qQjRIdXhEMHVsRlo2TFhpdVBqX0ZkN1g4Y2VMVld0R0doTFBKb09vUFp3LTluTzVwcDRMb1NjZFFXNGxCVjY3MVJQdThCb3JYYUREampqVVVpN2NGemR1eXFnNkhHZWk3UlFpY25POHhhTDhRekxLNUNnUFEiLCJlIjoiQVFBQiIsImQiOiJtZGhLdlFTWFQ1UlB1R3BXNEQtVm80b1oyek5Xd253NmR3bV9LY1hCaDA5YXRYc25rLWtfLTVHSVpmLXRob2RwbDd5ano3aEMtSktSMTFxOHQ5RlZON1VuYlBxdEdZeWNLU1FuSFR6MFJHdnU5S1VHY1dwRXlqclJDTG5BT2h2b2ZvYU1rYkZtbm8xb1U2QlJydXFZV2NmZHExQlBFbkdmVFFWUVVidFpVcnFsWl9HWmM5UGxxV1FnQUk5RFhXSGE5MEI0RzI1WHY2Wi1wWFhhOXpZMUZsQVF2TUo2MkIwUUxaWmppdzFfZ3pzN0w1RlpEX0JnTkhuTlZnNS0yaXo4bEZFMjZsemJVS2pCY0F3OGc2LVhKdVNRTGV2ZUQ5VElRUlZSN0hNOU43VzlNR1p6eWpfOUFyMHQ1S1BaVE9HYTctUkFNTjd0VmVDWTFkaHFBVF9KaFEiLCJwIjoieXhSVEN4cnowVktfR1lIVDUzSExxRHN0U2RCYVplcmpCSEtVbHQtQnVQQmY4ZjUyYUZZMGNZWGlXMGZBLTF3UlBCSzdJdWtycDJWUWlURHlQRjBXeVlxSnN4X3dheTRQd29uczNST1E3TkwyTzJZZEJFUzZLZEZPNEttMDc5MlhBX3NtMEVub0xCSEZUWG52Rkp0YWN3dThZMk1pRUQ0THIyMjZVczBld3Q4IiwicSI6IndfTmQ4RWNxTnc0d0dlX01hR2RVZXNINzRlb2RKY3ZhZE5XS1pZaHg2WHAxWEtoNzRackUyU0lKcGJpU3p3N3pLanVacGN0dGNkTWY3ZzBjWnV5Q1dpZGFTeEtPUy1RMGVkempkMWdnVVdPdUJTcmFIZnB6UXZyOXhPbnk1b3hwTUNkMUJ2bjlsdXk2Q0R6V1FBN3MxbVM4Sk9nY002ZXV6c3pVZDBQaFBHTSIsImRwIjoiUEdzZGtjNUFfLVBvYXdSUE1TcVA5c3MwWENPYTRYdVNjdjVMNnQ4d1R2OWs3REJTdGhQX29rNjgyMzlya04wQlc2Z08tUUg2Tk9GVnBwdGpWa1l6dzE1dVBWYWhScUg3bWx0Q2x6dDlBSmg3SFl6eDBSVkpkYXVLRmhrbmRiMnRja2ZFY20tcW5ZSGotM3J0Z0duXzdQNXUyX3JnWllpd0hVOC1BZWg0NEcwIiwiZHEiOiJFZU9DM08teVEtcHdxNzFfbkx4cU12YklwdnczZ3Y3VVI3eEM4VGYtcGtELXUtSEp4WFBhcXJQM3k0QkpMc3ZfbVFodDQzdnAxdTFlU2Q0NmpJN2s2NVFTSXk1amZUd3RLajduS1RzTFlFTElYVUpuUFR0akVHZFhpWVdPSGt3TlFrOG4yT1l6cDNhZkdTZHNxOVp3LXJXaGs0RDVLaUlSekdGWXVEYWpObDgiLCJxaSI6Inc3aV9aOS1uRUNaTWRBNVRoLVppbjEzdkhqbGo4VWxJQmNMVU12MC10WVpJSDhNMDR3bnB2cTYtbUVwbFY1V3FYNEpTcGRPV05QQzRxSmlyOWVMXzFqYXJidWpBOHlOSVNsQzJDRUZHVEgwbjdnYkJFZFI2RTRaSG1GUjlFdzFPVVBTTTRZQjJtbl9Pd2ViMkR6bjFINkl4TE9lb0FERWpuWlVLRVNOcDBhSSJ9",
"publicKey": "eyJrdHkiOiJSU0EiLCJraWQiOiJNczQzbDN4dVBCUzhHemxWeU0yajFGSDlHeTM0MFM0bl9sYjNuNE1nNTkwPSIsIm4iOiJtM0dLRVVhX1VSX2tJM3RXaVJUVnFOQ0pueGh4TjNYSm1rS19yV3ZUeE1rMl9taXVRU29qckpwRmlGTjI4ZWJnRWpZYnkyM2VodFlEQi1SVFVaZU1jVUFZb3VaWnBlYlpsOGhWNjBPT0dmSnBxRmVKTmlnVmxUendkN1Y5RWFlTUVhbDVvUDQwbURneW5ocElaNVNVMzhCd1dvd0Vlb2JPSW8xek11UjFwMktOSmxhT0RpR1hPTDVVVzlkT1dOTVF1VUFUVFNhZElZVnZRYmlwMWZMNl96aU9udkIwdWd2TW1qQjRIdXhEMHVsRlo2TFhpdVBqX0ZkN1g4Y2VMVld0R0doTFBKb09vUFp3LTluTzVwcDRMb1NjZFFXNGxCVjY3MVJQdThCb3JYYUREampqVVVpN2NGemR1eXFnNkhHZWk3UlFpY25POHhhTDhRekxLNUNnUFEiLCJlIjoiQVFBQiJ9"
}
}
product
path component is extracted fromselector.product
version
path component is extracted fromselector.version
Protobuf definition - ApplicationComponentNS
An application
is an instance of a product
running on a platform
at a
quality
stage level.
Application secrets are shared by all instances of the same software. Secret values must not be identical for different platform / quality.
spec:
selector:
quality: "production"
platform: "customer1"
product: "ece"
version: "v1.0.0"
namespaces:
application:
# AdminConsole -------------------------------------------------------------
- name: "adminconsole"
description: "Administration UI console"
secrets:
- suffix: "authentication/otp/okta_api_key"
description: "Okta API Key for OTP validation"
# Not generated by workflow, requested at generation time.
vendor: true
template: |-
{
"API_KEY": "{{ .Values.vendor.okta.api_key }}"
}
- suffix: "database/usage_credentials"
description: "PostgreSQL database account for component usage"
template: |-
{
"host": "sample-instance.abc2defghije.us-west-2.rds.amazonaws.com",
"port": "5432",
"options": "sslmode=require&application_name={{ .Data.Component }}",
"username": "dbuser-{{ .Data.Component }}-{{ randAlphaNum 8 }}",
"password": "{{ paranoidPassword | b64enc }}",
"dbname": "{{ .Data.Component }}"
}
This will generate 2 secrets like the following ones :
{
"app/production/customer1/ece/v1.0.0/adminconsole/authentication/otp/okta_api_key": {
"API_KEY": "okta-foo-api-123456789"
},
"app/production/customer1/ece/v1.0.0/adminconsole/database/usage_credentials": {
"dbname": "adminconsole",
"host": "sample-instance.abc2defghije.us-west-2.rds.amazonaws.com",
"options": "sslmode=require&application_name=adminconsole",
"password": "dHQiMXhQI3VxbDRHaUxAUWRiSzRwe1RNOEN2YnhrMnY6VlF6VXpLUlF4byoyXnIyV300bm5jOFBnXHFvdl9hNw==",
"port": "5432",
"username": "dbuser-adminconsole-qc7GUKPN"
}
}
quality
path component is extract fromselector.quality
platform
path component is extract fromselector.platform
product
path component is extracted fromselector.product
version
path component is extracted fromselector.version
A BundleTemplate
uses the harp
template engine to render a Bundle
object stored in a secret container.
Extracted from Customer BundleTemplate
$ harp from template --in essp.yaml \
--values=values.yaml \
--set quality=production | harp bundle dump --path-only
app/production/customer1/ece/v1.0.0/adminconsole/authentication/otp/okta_api_key
app/production/customer1/ece/v1.0.0/adminconsole/database/usage_credentials
app/production/customer1/ece/v1.0.0/adminconsole/http/session
app/production/customer1/ece/v1.0.0/adminconsole/mailing/sender/mailgun_api_key
app/production/customer1/ece/v1.0.0/adminconsole/privacy/anonymizer
app/production/customer1/ece/v1.0.0/userconsole/database/usage_credentials
app/production/customer1/ece/v1.0.0/userconsole/http/certificate
app/production/customer1/ece/v1.0.0/userconsole/http/session
infra/aws/essp-customer1/us-east-1/rds/adminconsole/accounts/root_credentials
platform/production/customer1/us-east-1/billing/recurly/vendor_api_key
platform/production/customer1/us-east-1/postgresql/admiconsole/admin_credentials
platform/production/customer1/us-east-1/zookeeper/accounts/admin_credentials
product/ece/v1.0.0/artifact/signature/key