mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Merge pull request #701 from external-secrets/feature/template-string-interface
feat: implement template engine v2
This commit is contained in:
commit
86aedda434
52 changed files with 2028 additions and 63 deletions
4
Makefile
4
Makefile
|
@ -194,8 +194,8 @@ docs: generate ## Generate docs
|
|||
docs.publish: generate ## Generate and deploys docs
|
||||
$(MAKE) -C ./hack/api-docs build.publish
|
||||
|
||||
.PHONY: serve-docs
|
||||
serve-docs: ## Serve docs
|
||||
.PHONY: docs.serve
|
||||
docs.serve: ## Serve docs
|
||||
$(MAKE) -C ./hack/api-docs serve
|
||||
|
||||
# ====================================================================================
|
||||
|
|
|
@ -59,6 +59,12 @@ type ExternalSecretTemplate struct {
|
|||
// +optional
|
||||
Type corev1.SecretType `json:"type,omitempty"`
|
||||
|
||||
// EngineVersion specifies the template engine version
|
||||
// that should be used to compile/execute the
|
||||
// template specified in .data and .templateFrom[].
|
||||
// +kubebuilder:default="v1"
|
||||
EngineVersion TemplateEngineVersion `json:"engineVersion,omitempty"`
|
||||
|
||||
// +optional
|
||||
Metadata ExternalSecretTemplateMetadata `json:"metadata,omitempty"`
|
||||
|
||||
|
@ -69,6 +75,13 @@ type ExternalSecretTemplate struct {
|
|||
TemplateFrom []TemplateFrom `json:"templateFrom,omitempty"`
|
||||
}
|
||||
|
||||
type TemplateEngineVersion string
|
||||
|
||||
const (
|
||||
TemplateEngineV1 TemplateEngineVersion = "v1"
|
||||
TemplateEngineV2 TemplateEngineVersion = "v2"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:MinProperties=1
|
||||
// +kubebuilder:validation:MaxProperties=1
|
||||
type TemplateFrom struct {
|
||||
|
|
|
@ -148,6 +148,12 @@ spec:
|
|||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
engineVersion:
|
||||
default: v1
|
||||
description: EngineVersion specifies the template engine version
|
||||
that should be used to compile/execute the template specified
|
||||
in .data and .templateFrom[].
|
||||
type: string
|
||||
metadata:
|
||||
description: ExternalSecretTemplateMetadata defines metadata
|
||||
fields for the Secret blueprint.
|
||||
|
|
50
docs/guides-templating-v1.md
Normal file
50
docs/guides-templating-v1.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Advanced Templating v1
|
||||
|
||||
!!! warning
|
||||
|
||||
Templating Engine v1 is **deprecated** and will be removed in the future. Please migrate to engine v2 and take a look at our [upgrade guide](guides-templating.md#migrating-from-v1) for changes.
|
||||
|
||||
|
||||
With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`. Each data value is interpreted as a [golang template](https://golang.org/pkg/text/template/).
|
||||
|
||||
## Examples
|
||||
|
||||
You can use templates to inject your secrets into a configuration file that you mount into your pod:
|
||||
``` yaml
|
||||
{% include 'multiline-template-v1-external-secret.yaml' %}
|
||||
```
|
||||
|
||||
You can also use pre-defined functions to extract data from your secrets. Here: extract key/cert from a pkcs12 archive and store it as PEM.
|
||||
``` yaml
|
||||
{% include 'pkcs12-template-v1-external-secret.yaml' %}
|
||||
```
|
||||
|
||||
### TemplateFrom
|
||||
|
||||
You do not have to define your templates inline in an ExternalSecret but you can pull `ConfigMaps` or other Secrets that contain a template. Consider the following example:
|
||||
|
||||
``` yaml
|
||||
{% include 'template-v1-from-secret.yaml' %}
|
||||
```
|
||||
|
||||
## Helper functions
|
||||
We provide a bunch of convenience functions that help you transform your secrets. A secret value is a `[]byte`.
|
||||
|
||||
| Function | Description | Input | Output |
|
||||
| -------------- | -------------------------------------------------------------------------- | -------------------------------- | ------------- |
|
||||
| pkcs12key | extracts the private key from a pkcs12 archive | `[]byte` | `[]byte` |
|
||||
| pkcs12keyPass | extracts the private key from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte` |
|
||||
| pkcs12cert | extracts the certificate from a pkcs12 archive | `[]byte` | `[]byte` |
|
||||
| pkcs12certPass | extracts the certificate from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte` |
|
||||
| pemPrivateKey | PEM encodes the provided bytes as private key | `[]byte` | `string` |
|
||||
| pemCertificate | PEM encodes the provided bytes as certificate | `[]byte` | `string` |
|
||||
| jwkPublicKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PUBLIC KEY` that contains the public key ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey)) for details | `[]byte` | `string` |
|
||||
| jwkPrivateKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey)) for details | `[]byte` | `string` |
|
||||
| base64decode | decodes the provided bytes as base64 | `[]byte` | `[]byte` |
|
||||
| base64encode | encodes the provided bytes as base64 | `[]byte` | `[]byte` |
|
||||
| fromJSON | parses the bytes as JSON so you can access individual properties | `[]byte` | `interface{}` |
|
||||
| toJSON | encodes the provided object as json string | `interface{}` | `string` |
|
||||
| toString | converts bytes to string | `[]byte` | `string` |
|
||||
| toBytes | converts string to bytes | `string` | `[]byte` |
|
||||
| upper | converts all characters to their upper case | `string` | `string` |
|
||||
| lower | converts all character to their lower case | `string` | `string` |
|
|
@ -1,43 +1,145 @@
|
|||
# Advanced Templating v2
|
||||
|
||||
With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`. Each data value is interpreted as a [golang template](https://golang.org/pkg/text/template/).
|
||||
|
||||
## Examples
|
||||
|
||||
You can use templates to inject your secrets into a configuration file that you mount into your pod:
|
||||
``` yaml
|
||||
{% include 'multiline-template-external-secret.yaml' %}
|
||||
```
|
||||
|
||||
You can also use pre-defined functions to extract data from your secrets. Here: extract key/cert from a pkcs12 archive and store it as PEM.
|
||||
``` yaml
|
||||
{% include 'pkcs12-template-external-secret.yaml' %}
|
||||
```yaml
|
||||
{% include 'multiline-template-v2-external-secret.yaml' %}
|
||||
```
|
||||
|
||||
### TemplateFrom
|
||||
|
||||
You do not have to define your templates inline in an ExternalSecret but you can pull `ConfigMaps` or other Secrets that contain a template. Consider the following example:
|
||||
|
||||
``` yaml
|
||||
{% include 'template-from-secret.yaml' %}
|
||||
```yaml
|
||||
{% include 'template-v2-from-secret.yaml' %}
|
||||
```
|
||||
|
||||
### Extract Keys and Certificates from PKCS#12 Archive
|
||||
|
||||
You can use pre-defined functions to extract data from your secrets. Here: extract keys and certificates from a PKCS#12 archive and store it as PEM.
|
||||
|
||||
```yaml
|
||||
{% include 'pkcs12-template-v2-external-secret.yaml' %}
|
||||
```
|
||||
|
||||
### Extract from JWK
|
||||
|
||||
You can extract the public or private key parts of a JWK and use them as [PKCS#8](https://pkg.go.dev/crypto/x509#ParsePKCS8PrivateKey) private key or PEM-encoded [PKIX](https://pkg.go.dev/crypto/x509#MarshalPKIXPublicKey) public key.
|
||||
|
||||
A JWK looks similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"kty": "RSA",
|
||||
"kid": "cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df",
|
||||
"use": "sig",
|
||||
"n": "pjdss...",
|
||||
"e": "AQAB"
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
And what you want may be a PEM-encoded public or private key portion of it. Take a look at this example on how to transform it into the desired format:
|
||||
|
||||
```yaml
|
||||
{% include 'jwk-template-v2-external-secret.yaml' %}
|
||||
```
|
||||
|
||||
### Filter PEM blocks
|
||||
|
||||
Consider you have a secret that contains both a certificate and a private key encoded in PEM format and it is your goal to use only the certificate from that secret.
|
||||
|
||||
```
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvxGZOW4IXvGlh
|
||||
. . .
|
||||
m8JCpbJXDfSSVxKHgK1Siw4K6pnTsIA2e/Z+Ha2fvtocERjq7VQMAJFaIZSTKo9Q
|
||||
JwwY+vj0yxWjyzHUzZB33tg=
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMDCCAhigAwIBAgIQabPaXuZCQaCg+eQAVptGGDANBgkqhkiG9w0BAQsFADAV
|
||||
. . .
|
||||
NtFUGA95RGN9s+pl6XY0YARPHf5O76ErC1OZtDTR5RdyQfcM+94gYZsexsXl0aQO
|
||||
9YD3Wg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
```
|
||||
|
||||
You can achieve that by using the `filterPEM` function to extract a specific type of PEM block from that secret. If multiple blocks of that type (here: `CERTIFICATE`) exist then all of them are returned in the order they are specified.
|
||||
```yaml
|
||||
{% include 'pem-filter-template-v2-external-secret.yaml' %}
|
||||
```
|
||||
|
||||
## Helper functions
|
||||
We provide a bunch of convenience functions that help you transform your secrets. A secret value is a `[]byte`.
|
||||
|
||||
| Function | Description | Input | Output |
|
||||
| -------------- | -------------------------------------------------------------------------- | -------------------------------- | ------------- |
|
||||
| pkcs12key | extracts the private key from a pkcs12 archive | `[]byte` | `[]byte` |
|
||||
| pkcs12keyPass | extracts the private key from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte` |
|
||||
| pkcs12cert | extracts the certificate from a pkcs12 archive | `[]byte` | `[]byte` |
|
||||
| pkcs12certPass | extracts the certificate from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte` |
|
||||
| pemPrivateKey | PEM encodes the provided bytes as private key | `[]byte` | `string` |
|
||||
| pemCertificate | PEM encodes the provided bytes as certificate | `[]byte` | `string` |
|
||||
| jwkPublicKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PUBLIC KEY` that contains the public key ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey)) for details | `[]byte` | `string` |
|
||||
| jwkPrivateKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey)) for details | `[]byte` | `string` |
|
||||
| base64decode | decodes the provided bytes as base64 | `[]byte` | `[]byte` |
|
||||
| base64encode | encodes the provided bytes as base64 | `[]byte` | `[]byte` |
|
||||
| fromJSON | parses the bytes as JSON so you can access individual properties | `[]byte` | `interface{}` |
|
||||
| toJSON | encodes the provided object as json string | `interface{}` | `string` |
|
||||
| toString | converts bytes to string | `[]byte` | `string` |
|
||||
| toBytes | converts string to bytes | `string` | `[]byte` |
|
||||
| upper | converts all characters to their upper case | `string` | `string` |
|
||||
| lower | converts all character to their lower case | `string` | `string` |
|
||||
!!! info inline end
|
||||
|
||||
Note: we removed `env` and `expandenv` from sprig functions for security reasons.
|
||||
|
||||
We provide a couple of convenience functions that help you transform your secrets. This is useful when dealing with PKCS#12 archives or JSON Web Keys (JWK).
|
||||
|
||||
In addition to that you can use over 200+ [sprig functions](http://masterminds.github.io/sprig/). If you feel a function is missing or might be valuable feel free to open an issue and submit a [pull request](contributing-process.md#submitting-a-pull-request).
|
||||
|
||||
<br/>
|
||||
|
||||
| Function | Description |
|
||||
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| pkcs12key | Extracts all private keys from a PKCS#12 archive and encodes them in **PKCS#8 PEM** format. |
|
||||
| pkcs12keyPass | Same as `pkcs12key`. Uses the provided password to decrypt the PKCS#12 archive. |
|
||||
| pkcs12cert | Extracts all certificates from a PKCS#12 archive and orders them if possible. If disjunct or multiple leaf certs are provided they are returned as-is. <br/> Sort order: `leaf / intermediate(s) / root`. |
|
||||
| pkcs12certPass | Same as `pkcs12cert`. Uses the provided password to decrypt the PKCS#12 archive. |
|
||||
| filterPEM | Filters PEM blocks with a specific type from a list of PEM blocks. |
|
||||
|
||||
| jwkPublicKeyPem | Takes an json-serialized JWK and returns an PEM block of type `PUBLIC KEY` that contains the public key. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey) for details. |
|
||||
| jwkPrivateKeyPem | Takes an json-serialized JWK as `string` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey) for details. |
|
||||
|
||||
## Migrating from v1
|
||||
|
||||
You have to opt-in to use the new engine version by specifying `template.engineVersion=v2`:
|
||||
|
||||
```yaml
|
||||
apiVersion: external-secrets.io/v1alpha1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: secret
|
||||
spec:
|
||||
# ...
|
||||
target:
|
||||
template:
|
||||
engineVersion: v2
|
||||
# ...
|
||||
```
|
||||
|
||||
The biggest change was that basically all function parameter types were changed from accepting/returning `[]byte` to `string`. This is relevant for you because now you don't need to specify `toString` all the time at the end of a template pipeline.
|
||||
|
||||
```yaml
|
||||
{% raw %}
|
||||
apiVersion: external-secrets.io/v1alpha1
|
||||
kind: ExternalSecret
|
||||
# ...
|
||||
spec:
|
||||
target:
|
||||
template:
|
||||
engineVersion: v2
|
||||
data:
|
||||
# this used to be {{ .foobar | toString }}
|
||||
egg: "new: {{ .foobar }}"
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
##### Functions removed/replaced
|
||||
|
||||
- `base64encode` was renamed to `b64enc`.
|
||||
- `base64decode` was renamed to `b64dec`. Any errors that occurr during decoding are silenced.
|
||||
- `fromJSON` was renamed to `fromJson`. Any errors that occurr during unmarshalling are silenced.
|
||||
- `toJSON` was renamed to `toJson`. Any errors that occurr during marshalling are silenced.
|
||||
- `pkcs12key` and `pkcs12keyPass` encode the PKCS#8 key directly into PEM format. There is no need to call `pemPrivateKey` anymore. Also, these functions do extract all private keys from the PKCS#12 archive not just the first one.
|
||||
- `pkcs12cert` and `pkcs12certPass` encode the certs directly into PEM format. There is no need to call `pemCertificate` anymore. These functions now **extract all certificates** from the PKCS#12 archive not just the first one.
|
||||
- `toString` implementation was replaced by the `sprig` implementation and should be api-compatible.
|
||||
- `toBytes` was removed.
|
||||
- `pemPrivateKey` was removed. It's now implemented within the `pkcs12*` functions.
|
||||
- `pemCertificate` was removed. It's now implemented within the `pkcs12*` functions.
|
||||
|
|
25
docs/snippets/jwk-template-v2-external-secret.yaml
Normal file
25
docs/snippets/jwk-template-v2-external-secret.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
{% raw %}
|
||||
apiVersion: external-secrets.io/v1alpha1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: template
|
||||
spec:
|
||||
# ...
|
||||
target:
|
||||
template:
|
||||
engineVersion: v2
|
||||
data:
|
||||
# .myjwk is a json-encoded JWK string.
|
||||
#
|
||||
# this template will produce for jwk_pub a PEM encoded public key:
|
||||
# -----BEGIN PUBLIC KEY-----
|
||||
# MIIBI...
|
||||
# ...
|
||||
# ...AQAB
|
||||
# -----END PUBLIC KEY-----
|
||||
jwk_pub: "{{ .myjwk | jwkPublicKeyPem }}"
|
||||
# private key is a pem-encoded PKCS#8 private key
|
||||
jwk_priv: "{{ .myjwk | jwkPrivateKeyPem }}"
|
||||
|
||||
|
||||
{% endraw %}
|
|
@ -10,6 +10,10 @@ spec:
|
|||
kind: SecretStore
|
||||
target:
|
||||
name: secret-to-be-created
|
||||
|
||||
# v1 is the default version
|
||||
engineVersion: v1
|
||||
|
||||
# this is how the Kind=Secret will look like
|
||||
template:
|
||||
type: kubernetes.io/tls
|
32
docs/snippets/multiline-template-v2-external-secret.yaml
Normal file
32
docs/snippets/multiline-template-v2-external-secret.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
{% raw %}
|
||||
apiVersion: external-secrets.io/v1alpha1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: template
|
||||
spec:
|
||||
# ...
|
||||
target:
|
||||
name: secret-to-be-created
|
||||
# this is how the Kind=Secret will look like
|
||||
template:
|
||||
type: kubernetes.io/tls
|
||||
engineVersion: v2
|
||||
data:
|
||||
# multiline string
|
||||
config: |
|
||||
datasources:
|
||||
- name: Graphite
|
||||
type: graphite
|
||||
access: proxy
|
||||
url: http://localhost:8080
|
||||
password: "{{ .password }}"
|
||||
user: "{{ .user }}"
|
||||
|
||||
data:
|
||||
- secretKey: user
|
||||
remoteRef:
|
||||
key: /grafana/user
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: /grafana/password
|
||||
{% endraw %}
|
19
docs/snippets/pkcs12-template-v2-external-secret.yaml
Normal file
19
docs/snippets/pkcs12-template-v2-external-secret.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% raw %}
|
||||
apiVersion: external-secrets.io/v1alpha1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: template
|
||||
spec:
|
||||
# ...
|
||||
target:
|
||||
template:
|
||||
type: kubernetes.io/tls
|
||||
engineVersion: v2
|
||||
data:
|
||||
tls.crt: "{{ .mysecret | pkcs12cert }}"
|
||||
tls.key: "{{ .mysecret | pkcs12key }}"
|
||||
|
||||
# if needed unlock the pkcs12 with the password
|
||||
tls.crt: "{{ .mysecret | pkcs12certPass "my-password" }}"
|
||||
|
||||
{% endraw %}
|
41
docs/snippets/template-v2-from-secret.yaml
Normal file
41
docs/snippets/template-v2-from-secret.yaml
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% raw %}
|
||||
# define your template in a config map
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: grafana-config-tpl
|
||||
data:
|
||||
config.yaml: |
|
||||
datasources:
|
||||
- name: Graphite
|
||||
type: graphite
|
||||
access: proxy
|
||||
url: http://localhost:8080
|
||||
password: "{{ .password }}"
|
||||
user: "{{ .user }}"
|
||||
---
|
||||
apiVersion: external-secrets.io/v1alpha1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: my-template-example
|
||||
spec:
|
||||
# ...
|
||||
target:
|
||||
name: secret-to-be-created
|
||||
template:
|
||||
engineVersion: v2
|
||||
templateFrom:
|
||||
- configMap:
|
||||
# name of the configmap to pull in
|
||||
name: grafana-config-tpl
|
||||
# here you define the keys that should be used as template
|
||||
items:
|
||||
- key: config.yaml
|
||||
data:
|
||||
- secretKey: user
|
||||
remoteRef:
|
||||
key: /grafana/user
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: /grafana/password
|
||||
{% endraw %}
|
36
docs/spec.md
36
docs/spec.md
|
@ -1408,6 +1408,21 @@ Kubernetes core/v1.SecretType
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>engineVersion</code></br>
|
||||
<em>
|
||||
<a href="#external-secrets.io/v1alpha1.TemplateEngineVersion">
|
||||
TemplateEngineVersion
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>EngineVersion specifies the template engine version
|
||||
that should be used to compile/execute the
|
||||
template specified in .data and .templateFrom[].</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>metadata</code></br>
|
||||
<em>
|
||||
<a href="#external-secrets.io/v1alpha1.ExternalSecretTemplateMetadata">
|
||||
|
@ -2666,6 +2681,27 @@ Kubernetes meta/v1.Time
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="external-secrets.io/v1alpha1.TemplateEngineVersion">TemplateEngineVersion
|
||||
(<code>string</code> alias)</p></h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#external-secrets.io/v1alpha1.ExternalSecretTemplate">ExternalSecretTemplate</a>)
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr><td><p>"v1"</p></td>
|
||||
<td></td>
|
||||
</tr><tr><td><p>"v2"</p></td>
|
||||
<td></td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
<h3 id="external-secrets.io/v1alpha1.TemplateFrom">TemplateFrom
|
||||
</h3>
|
||||
<p>
|
||||
|
|
|
@ -110,8 +110,9 @@ func (f *Framework) Install(a addon.Addon) {
|
|||
|
||||
// Compose helps define multiple testcases with same/different auth methods.
|
||||
func Compose(descAppend string, f *Framework, fn func(f *Framework) (string, func(*TestCase)), tweaks ...func(*TestCase)) TableEntry {
|
||||
desc, tfn := fn(f)
|
||||
tweaks = append(tweaks, tfn)
|
||||
// prepend common fn to tweaks
|
||||
desc, cfn := fn(f)
|
||||
tweaks = append([]func(*TestCase){cfn}, tweaks...)
|
||||
|
||||
// need to convert []func to []interface{}
|
||||
ifs := make([]interface{}, len(tweaks))
|
||||
|
|
|
@ -20,5 +20,6 @@ import (
|
|||
_ "github.com/external-secrets/external-secrets/e2e/suite/aws/secretsmanager"
|
||||
_ "github.com/external-secrets/external-secrets/e2e/suite/azure"
|
||||
_ "github.com/external-secrets/external-secrets/e2e/suite/gcp"
|
||||
_ "github.com/external-secrets/external-secrets/e2e/suite/template"
|
||||
_ "github.com/external-secrets/external-secrets/e2e/suite/vault"
|
||||
)
|
||||
|
|
89
e2e/suite/template/provider.go
Normal file
89
e2e/suite/template/provider.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
// nolint
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
|
||||
// nolint
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||
)
|
||||
|
||||
type templateProvider struct {
|
||||
framework *framework.Framework
|
||||
}
|
||||
|
||||
func newProvider(f *framework.Framework) *templateProvider {
|
||||
prov := &templateProvider{
|
||||
framework: f,
|
||||
}
|
||||
BeforeEach(prov.BeforeEach)
|
||||
return prov
|
||||
}
|
||||
|
||||
func (s *templateProvider) CreateSecret(key, val string) {
|
||||
// noop: this provider implements static key/value pairs
|
||||
}
|
||||
|
||||
func (s *templateProvider) DeleteSecret(key string) {
|
||||
// noop: this provider implements static key/value pairs
|
||||
}
|
||||
|
||||
func (s *templateProvider) BeforeEach() {
|
||||
// Create a secret store - change these values to match YAML
|
||||
By("creating a secret store for credentials")
|
||||
secretStore := &esv1alpha1.SecretStore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.framework.Namespace.Name,
|
||||
Namespace: s.framework.Namespace.Name,
|
||||
},
|
||||
Spec: esv1alpha1.SecretStoreSpec{
|
||||
Provider: &esv1alpha1.SecretStoreProvider{
|
||||
Fake: &esv1alpha1.FakeProvider{
|
||||
Data: []esv1alpha1.FakeProviderData{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Key: "baz",
|
||||
Value: "bang",
|
||||
},
|
||||
{
|
||||
Key: "map",
|
||||
ValueMap: map[string]string{
|
||||
"foo": "barmap",
|
||||
"bar": "bangmap",
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "json",
|
||||
Value: `{"foo":{"bar":"baz"}}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := s.framework.CRClient.Create(context.Background(), secretStore)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
101
e2e/suite/template/template.go
Normal file
101
e2e/suite/template/template.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
|
||||
// nolint
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||
)
|
||||
|
||||
var _ = Describe("[template]", Label("template"), func() {
|
||||
f := framework.New("eso-template")
|
||||
prov := newProvider(f)
|
||||
|
||||
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
||||
framework.Compose("template v1", f, genericTemplate, useTemplateV1),
|
||||
framework.Compose("template v2", f, genericTemplate, useTemplateV2),
|
||||
)
|
||||
})
|
||||
|
||||
// useTemplateV1 specifies a test case which uses the template engine v1.
|
||||
func useTemplateV1(tc *framework.TestCase) {
|
||||
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
||||
EngineVersion: esv1alpha1.TemplateEngineV1,
|
||||
Data: map[string]string{
|
||||
"tplv1": "executed: {{ .singlefoo | toString }}|{{ .singlebaz | toString }}",
|
||||
"other": `{{ .foo | toString }}|{{ .bar | toString }}`,
|
||||
},
|
||||
}
|
||||
tc.ExpectedSecret.Data = map[string][]byte{
|
||||
"tplv1": []byte(`executed: bar|bang`),
|
||||
"other": []byte(`barmap|bangmap`),
|
||||
}
|
||||
}
|
||||
|
||||
// useTemplateV2 specifies a test case which uses the template engine v2.
|
||||
func useTemplateV2(tc *framework.TestCase) {
|
||||
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
||||
EngineVersion: esv1alpha1.TemplateEngineV2,
|
||||
Data: map[string]string{
|
||||
"tplv2": "executed: {{ .singlefoo }}|{{ .singlebaz }}",
|
||||
"other": `{{ .foo }}|{{ .bar }}`,
|
||||
"sprig-str": `{{ .foo | upper }}`,
|
||||
"json-ex": `{{ $var := .singlejson | fromJson }}{{ $var.foo | toJson }}`,
|
||||
},
|
||||
}
|
||||
tc.ExpectedSecret.Data = map[string][]byte{
|
||||
"tplv2": []byte(`executed: bar|bang`),
|
||||
"other": []byte(`barmap|bangmap`),
|
||||
"sprig-str": []byte(`BARMAP`),
|
||||
"json-ex": []byte(`{"bar":"baz"}`),
|
||||
}
|
||||
}
|
||||
|
||||
// This case uses template engine v1.
|
||||
func genericTemplate(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||
return "[template] should execute template v1", func(tc *framework.TestCase) {
|
||||
tc.ExpectedSecret = &v1.Secret{
|
||||
Type: v1.SecretTypeOpaque,
|
||||
}
|
||||
tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
|
||||
{
|
||||
SecretKey: "singlefoo",
|
||||
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
|
||||
Key: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
SecretKey: "singlebaz",
|
||||
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
|
||||
Key: "baz",
|
||||
},
|
||||
},
|
||||
{
|
||||
SecretKey: "singlejson",
|
||||
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
|
||||
Key: "json",
|
||||
},
|
||||
},
|
||||
}
|
||||
tc.ExternalSecret.Spec.DataFrom = []esv1alpha1.ExternalSecretDataRemoteRef{
|
||||
{
|
||||
Key: "map",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
6
go.mod
6
go.mod
|
@ -40,8 +40,7 @@ require (
|
|||
github.com/IBM/go-sdk-core/v5 v5.9.1
|
||||
github.com/IBM/secrets-manager-go-sdk v1.0.31
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/Masterminds/sprig/v3 v3.2.2
|
||||
github.com/PaesslerAG/jsonpath v0.1.1
|
||||
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0
|
||||
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2
|
||||
|
@ -98,6 +97,7 @@ require (
|
|||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/PaesslerAG/gval v1.0.0 // indirect
|
||||
github.com/armon/go-metrics v0.3.10 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
|
@ -179,8 +179,10 @@ require (
|
|||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.2.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
|
|
14
go.sum
14
go.sum
|
@ -93,10 +93,10 @@ github.com/IBM/secrets-manager-go-sdk v1.0.31 h1:KRRyeEvlKkkZb90njgReOrK92+IyS6L
|
|||
github.com/IBM/secrets-manager-go-sdk v1.0.31/go.mod h1:0Juj6ER/LpDqJ49nw705MNyXSHsHodgztFdkXz5ttxs=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
|
||||
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
|
@ -515,11 +515,13 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe
|
|||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
|
||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
|
@ -757,6 +759,8 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
|
|||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
|
@ -777,6 +781,7 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
|
|||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
|
@ -896,6 +901,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
|
|
|
@ -23,4 +23,4 @@ RUN apk add -U --no-cache \
|
|||
bash \
|
||||
gcc \
|
||||
diffutils \
|
||||
&& pip3 install -r /requirements.txt
|
||||
&& pip3 install -r /requirements.txt
|
||||
|
|
|
@ -53,8 +53,7 @@ build: image generate $(SOURCES)
|
|||
--rm \
|
||||
--user $(UID):$(GID) \
|
||||
$(MKDOCS_IMAGE) \
|
||||
/bin/bash -c "cd /repo && git config user.email "docs@external-secrets.io" && git config user.name "Docs" && $(MIKE) deploy --update-aliases -F hack/api-docs/mkdocs.yml $(DOCS_VERSION) $(DOCS_ALIAS);"
|
||||
|
||||
/bin/bash -c "cd /repo && git config user.email "docs@external-secrets.io" && git config user.name "Docs" && $(MIKE) deploy --ignore --update-aliases -F hack/api-docs/mkdocs.yml $(DOCS_VERSION) $(DOCS_ALIAS);"
|
||||
.PHONY: build.publish
|
||||
build.publish: image generate $(SOURCES)
|
||||
mkdir -p $(GENROOT)
|
||||
|
@ -65,7 +64,6 @@ build.publish: image generate $(SOURCES)
|
|||
--user $(UID):$(GID) \
|
||||
$(MKDOCS_IMAGE) \
|
||||
/bin/bash -c "cd /repo && git config user.email "docs@external-secrets.io" && git config user.name "Docs" && $(MIKE) deploy --update-aliases -p -F hack/api-docs/mkdocs.yml $(DOCS_VERSION) $(DOCS_ALIAS);"
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
./generate.sh $(SRCDIR)/spec.md
|
||||
|
@ -86,4 +84,4 @@ serve:
|
|||
-p $(SERVE_BIND_ADDRESS):8000:8000 \
|
||||
--rm \
|
||||
$(MKDOCS_IMAGE) \
|
||||
/bin/bash -c "cd /repo && $(MIKE) serve -F hack/api-docs/mkdocs.yml -a 0.0.0.0:8000"
|
||||
/bin/bash -c "cd /repo && mkdocs serve -f hack/api-docs/mkdocs.yml -a 0.0.0.0:8000"
|
||||
|
|
|
@ -34,7 +34,9 @@ nav:
|
|||
- Guides:
|
||||
- Introduction: guides-introduction.md
|
||||
- Getting started: guides-getting-started.md
|
||||
- Advanced Templating: guides-templating.md
|
||||
- Advanced Templating:
|
||||
v2: guides-templating.md
|
||||
v1: guides-templating-v1.md
|
||||
- Controller Classes: guides-controller-class.md
|
||||
- All keys, One secret: guides-all-keys-one-secret.md
|
||||
- Common K8S Secret Types: guides-common-k8s-secret-types.md
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
Click==7.0
|
||||
Click==8.0.3
|
||||
htmlmin==0.1.12
|
||||
Jinja2==2.11.1
|
||||
jsmin==2.2.2
|
||||
livereload==2.6.1
|
||||
Markdown==3.2.1
|
||||
MarkupSafe==1.1.1
|
||||
Jinja2==3.0.3
|
||||
jsmin==3.0.1
|
||||
livereload==2.6.3
|
||||
Markdown==3.3.6
|
||||
MarkupSafe==2.0.1
|
||||
mkdocs==1.2.3
|
||||
mike==1.1.2
|
||||
mkdocs-material==8.1.9
|
||||
mkdocs-minify-plugin==0.2.1
|
||||
pep562==1.0
|
||||
Pygments==2.10.0
|
||||
pymdown-extensions==9.0
|
||||
PyYAML==5.3
|
||||
six==1.14.0
|
||||
tornado==6.0.3
|
||||
mkdocs-macros-plugin==0.4.18
|
||||
mkdocs-material==8.1.10
|
||||
mkdocs-minify-plugin==0.5.0
|
||||
pep562==1.1
|
||||
Pygments==2.11.2
|
||||
pymdown-extensions==9.1
|
||||
PyYAML==6.0
|
||||
six==1.16.0
|
||||
tornado==6.1
|
||||
mkdocs-macros-plugin==0.6.4
|
||||
|
|
|
@ -56,7 +56,11 @@ func (r *Reconciler) applyTemplate(ctx context.Context, es *esv1alpha1.ExternalS
|
|||
}
|
||||
r.Log.V(1).Info("found template data", "tpl_data", tplMap)
|
||||
|
||||
err = template.Execute(tplMap, dataMap, secret)
|
||||
execute, err := template.EngineForVersion(es.Spec.Target.Template.EngineVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = execute(tplMap, dataMap, secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf(errExecTpl, err)
|
||||
}
|
||||
|
|
|
@ -433,6 +433,9 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
"hihi": "ga",
|
||||
},
|
||||
},
|
||||
// We do not specify the engine version
|
||||
// it should default to v1 for alpha1
|
||||
// EngineVersion: esv1alpha1.TemplateEngineV1,
|
||||
Type: v1.SecretTypeOpaque,
|
||||
Data: map[string]string{
|
||||
targetProp: targetPropObj,
|
||||
|
@ -453,6 +456,23 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}
|
||||
}
|
||||
|
||||
// when using a v2 template it should use the v2 engine version
|
||||
syncWithTemplateV2 := func(tc *testCase) {
|
||||
const secretVal = "someValue"
|
||||
tc.externalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
||||
Type: v1.SecretTypeOpaque,
|
||||
EngineVersion: esv1alpha1.TemplateEngineV2,
|
||||
Data: map[string]string{
|
||||
targetProp: "{{ .targetProperty | upper }} was templated",
|
||||
},
|
||||
}
|
||||
fakeProvider.WithGetSecret([]byte(secretVal), nil)
|
||||
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
||||
// check values
|
||||
Expect(string(secret.Data[targetProp])).To(Equal(expectedSecretVal))
|
||||
}
|
||||
}
|
||||
|
||||
// secret should be synced with correct value precedence:
|
||||
// * template
|
||||
// * templateFrom
|
||||
|
@ -1046,6 +1066,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Entry("should not resolve conflicts with creationPolicy=Merge", mergeWithConflict),
|
||||
Entry("should not update unchanged secret using creationPolicy=Merge", mergeWithSecretNoChange),
|
||||
Entry("should sync with template", syncWithTemplate),
|
||||
Entry("should sync with template engine v2", syncWithTemplateV2),
|
||||
Entry("should sync template with correct value precedence", syncWithTemplatePrecedence),
|
||||
Entry("should refresh secret from template", refreshWithTemplate),
|
||||
Entry("should be able to use only metadata from template", onlyMetadataFromTemplate),
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
"strings"
|
||||
tpl "text/template"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/PaesslerAG/jsonpath"
|
||||
"gopkg.in/yaml.v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -36,7 +36,7 @@ import (
|
|||
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
|
||||
"github.com/external-secrets/external-secrets/pkg/provider"
|
||||
"github.com/external-secrets/external-secrets/pkg/provider/schema"
|
||||
"github.com/external-secrets/external-secrets/pkg/template"
|
||||
"github.com/external-secrets/external-secrets/pkg/template/v2"
|
||||
)
|
||||
|
||||
// Provider satisfies the provider interface.
|
||||
|
|
36
pkg/template/engine.go
Normal file
36
pkg/template/engine.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||
v1 "github.com/external-secrets/external-secrets/pkg/template/v1"
|
||||
v2 "github.com/external-secrets/external-secrets/pkg/template/v2"
|
||||
)
|
||||
|
||||
type ExecFunc func(tpl, data map[string][]byte, secret *corev1.Secret) error
|
||||
|
||||
func EngineForVersion(version esapi.TemplateEngineVersion) (ExecFunc, error) {
|
||||
switch version {
|
||||
case esapi.TemplateEngineV1:
|
||||
return v1.Execute, nil
|
||||
case esapi.TemplateEngineV2:
|
||||
return v2.Execute, nil
|
||||
}
|
||||
|
||||
// in case we run with a old v1alpha1 CRD
|
||||
// we must return v1 as default
|
||||
return v1.Execute, nil
|
||||
}
|
|
@ -2,9 +2,7 @@
|
|||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
80
pkg/template/v2/_testdata/Makefile
Normal file
80
pkg/template/v2/_testdata/Makefile
Normal file
|
@ -0,0 +1,80 @@
|
|||
# prerequisite:
|
||||
# install step cli
|
||||
# from: https://github.com/smallstep/cli
|
||||
|
||||
all: ca disjunct-ca intermediate leaf \
|
||||
pkcs12-nopass pkcs12-disjunct pkcs12-multibag pkcs12-withpass-1234
|
||||
|
||||
clean:
|
||||
rm *.{pfx,crt,key,pem}
|
||||
|
||||
ca:
|
||||
step certificate create root-ca \
|
||||
root-ca.crt root-ca.key \
|
||||
--profile root-ca --kty OKP --curve Ed25519 \
|
||||
--no-password --insecure -f
|
||||
|
||||
disjunct-ca:
|
||||
step certificate create disjunct-root-ca \
|
||||
disjunct-root-ca.crt disjunct-root-ca.key \
|
||||
--profile root-ca --kty OKP --curve Ed25519 \
|
||||
--no-password --insecure -f
|
||||
|
||||
intermediate:
|
||||
step certificate create intermediate-ca \
|
||||
intermediate-ca.crt intermediate-ca.key \
|
||||
--profile intermediate-ca \
|
||||
--ca ./root-ca.crt \
|
||||
--ca-key ./root-ca.key \
|
||||
--kty EC --curve P-256 \
|
||||
--no-password --insecure -f
|
||||
|
||||
leaf:
|
||||
step certificate create foo \
|
||||
foo.crt foo.key --profile leaf \
|
||||
--ca ./intermediate-ca.crt \
|
||||
--ca-key ./intermediate-ca.key \
|
||||
--no-password --insecure -f
|
||||
|
||||
pkcs12-nopass: ca intermediate leaf
|
||||
# deliberately in wrong order
|
||||
cat foo.crt root-ca.crt intermediate-ca.crt > chain.pem
|
||||
|
||||
# create pkcs12
|
||||
openssl pkcs12 -export \
|
||||
-in chain.pem \
|
||||
-inkey foo.key \
|
||||
-out foo-nopass.pfx \
|
||||
-password pass:
|
||||
|
||||
pkcs12-disjunct: ca intermediate disjunct-ca leaf
|
||||
cat root-ca.crt intermediate-ca.crt disjunct-root-ca.crt > disjunct-chain.pem
|
||||
|
||||
openssl pkcs12 -export \
|
||||
-in foo.crt \
|
||||
-certfile disjunct-chain.pem \
|
||||
-inkey foo.key \
|
||||
-out foo-disjunct-nopass.pfx \
|
||||
-password pass:
|
||||
|
||||
pkcs12-multibag: ca intermediate leaf
|
||||
# deliberately in wrong order, we're missing the leaf cert here
|
||||
cat root-ca.crt intermediate-ca.crt > intermediate-chain.pem
|
||||
|
||||
openssl pkcs12 -export \
|
||||
-in foo.crt \
|
||||
-certfile intermediate-chain.pem \
|
||||
-inkey foo.key \
|
||||
-out foo-multibag-nopass.pfx \
|
||||
-password pass:
|
||||
|
||||
pkcs12-withpass-1234: ca intermediate leaf
|
||||
# deliberately in the wrong order
|
||||
cat foo.crt root-ca.crt intermediate-ca.crt > chain.pem
|
||||
|
||||
# create pkcs12
|
||||
openssl pkcs12 -export \
|
||||
-in chain.pem \
|
||||
-inkey foo.key \
|
||||
-out foo-withpass-1234.pfx \
|
||||
-password pass:1234
|
31
pkg/template/v2/_testdata/chain.pem
Normal file
31
pkg/template/v2/_testdata/chain.pem
Normal file
|
@ -0,0 +1,31 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBqjCCAU+gAwIBAgIRAPnGGsBUMbZhmh5QdnYdBmUwCgYIKoZIzj0EAwIwGjEY
|
||||
MBYGA1UEAxMPaW50ZXJtZWRpYXRlLWNhMB4XDTIyMDIwOTEwMjUzMVoXDTIyMDIx
|
||||
MDEwMjUzMVowDjEMMAoGA1UEAxMDZm9vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
|
||||
QgAEqnxdeInykx8JZsLi13rZLekoG2cosQ3F+2InVNy7hCQ7soMqdaJsGQ6LFtov
|
||||
ogUFtOOTRWrunblqNWGZsowHbKOBgTB/MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE
|
||||
FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFLtundVbuKd73OWzo6SY
|
||||
by0Ajeb2MB8GA1UdIwQYMBaAFCLg80J/bZBbOd+Y8+V94l5xM2zEMA4GA1UdEQQH
|
||||
MAWCA2ZvbzAKBggqhkjOPQQDAgNJADBGAiEA4K4SbVNqrEtl7RfwBfJFMnWI+X8D
|
||||
zMPMc4Xqzp2qTxcCIQDsySgtiakypZfWakpB49zJph0kLwGK8xhWvGMUw1N1/w==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBJzCB2qADAgECAhEArvunrLoYXTmwMROkmbAlBTAFBgMrZXAwEjEQMA4GA1UE
|
||||
AxMHcm9vdC1jYTAeFw0yMjAyMDkxMDI1MzBaFw0zMjAyMDcxMDI1MzBaMBIxEDAO
|
||||
BgNVBAMTB3Jvb3QtY2EwKjAFBgMrZXADIQDSw5uQ1io+jcKevCH0sl+tGTB6/BQs
|
||||
Bu84ibw13QoP36NFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
|
||||
AQEwHQYDVR0OBBYEFGtn1GnNjmd6cxOhMnVau1L8IkGtMAUGAytlcANBAOHSAS4z
|
||||
/6ctcvRwlGr9Hyt7vVLROImD2t3rFdDDHLLL1znikK3JZvVbETyMFOMbOMQS33C/
|
||||
4FtLGenZFXySjQw=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBgDCCATKgAwIBAgIRAOzjpCdp42oW5MoccLpRXpAwBQYDK2VwMBIxEDAOBgNV
|
||||
BAMTB3Jvb3QtY2EwHhcNMjIwMjA5MTAyNTMxWhcNMzIwMjA3MTAyNTMxWjAaMRgw
|
||||
FgYDVQQDEw9pbnRlcm1lZGlhdGUtY2EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
||||
AATekdyX6cZe0Ajmme363TQoWnrQwXnARzeWEf4FRQE8BGWgf8z7wljjpb4M4S4f
|
||||
+CJAYYY/6x38UnlsxXEeBTofo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/
|
||||
BAgwBgEB/wIBADAdBgNVHQ4EFgQUIuDzQn9tkFs535jz5X3iXnEzbMQwHwYDVR0j
|
||||
BBgwFoAUa2fUac2OZ3pzE6EydVq7UvwiQa0wBQYDK2VwA0EA4gntaGs/3ME6q1y9
|
||||
gO4ntri2qwoC25l3q7q9BiFBmeBmvS6I1w9HCZHtB3JnVC/IYDTCYDNTbpGWEOjl
|
||||
aCKLCA==
|
||||
-----END CERTIFICATE-----
|
29
pkg/template/v2/_testdata/disjunct-chain.pem
Normal file
29
pkg/template/v2/_testdata/disjunct-chain.pem
Normal file
|
@ -0,0 +1,29 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBJzCB2qADAgECAhEArvunrLoYXTmwMROkmbAlBTAFBgMrZXAwEjEQMA4GA1UE
|
||||
AxMHcm9vdC1jYTAeFw0yMjAyMDkxMDI1MzBaFw0zMjAyMDcxMDI1MzBaMBIxEDAO
|
||||
BgNVBAMTB3Jvb3QtY2EwKjAFBgMrZXADIQDSw5uQ1io+jcKevCH0sl+tGTB6/BQs
|
||||
Bu84ibw13QoP36NFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
|
||||
AQEwHQYDVR0OBBYEFGtn1GnNjmd6cxOhMnVau1L8IkGtMAUGAytlcANBAOHSAS4z
|
||||
/6ctcvRwlGr9Hyt7vVLROImD2t3rFdDDHLLL1znikK3JZvVbETyMFOMbOMQS33C/
|
||||
4FtLGenZFXySjQw=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBgDCCATKgAwIBAgIRAOzjpCdp42oW5MoccLpRXpAwBQYDK2VwMBIxEDAOBgNV
|
||||
BAMTB3Jvb3QtY2EwHhcNMjIwMjA5MTAyNTMxWhcNMzIwMjA3MTAyNTMxWjAaMRgw
|
||||
FgYDVQQDEw9pbnRlcm1lZGlhdGUtY2EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
||||
AATekdyX6cZe0Ajmme363TQoWnrQwXnARzeWEf4FRQE8BGWgf8z7wljjpb4M4S4f
|
||||
+CJAYYY/6x38UnlsxXEeBTofo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/
|
||||
BAgwBgEB/wIBADAdBgNVHQ4EFgQUIuDzQn9tkFs535jz5X3iXnEzbMQwHwYDVR0j
|
||||
BBgwFoAUa2fUac2OZ3pzE6EydVq7UvwiQa0wBQYDK2VwA0EA4gntaGs/3ME6q1y9
|
||||
gO4ntri2qwoC25l3q7q9BiFBmeBmvS6I1w9HCZHtB3JnVC/IYDTCYDNTbpGWEOjl
|
||||
aCKLCA==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBOTCB7KADAgECAhEA0cArEY0s5JoP8JfqIDv2BTAFBgMrZXAwGzEZMBcGA1UE
|
||||
AxMQZGlzanVuY3Qtcm9vdC1jYTAeFw0yMjAyMDkxMDI1MzBaFw0zMjAyMDcxMDI1
|
||||
MzBaMBsxGTAXBgNVBAMTEGRpc2p1bmN0LXJvb3QtY2EwKjAFBgMrZXADIQAJER3w
|
||||
QFZH1DBmxZm9IkaZ5noangcg/CYNP+GtcyQPL6NFMEMwDgYDVR0PAQH/BAQDAgEG
|
||||
MBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMddJG4lWXTN/x9InsiJUTxu
|
||||
d4+HMAUGAytlcANBABcyLV4om9LPV0TGDf0jiM+JxH1R+ATvAE8FHDtd8L66BrzA
|
||||
Id656nPz3fz9ZMB9VZr7iGcghXYlTHxu6NkQEgA=
|
||||
-----END CERTIFICATE-----
|
9
pkg/template/v2/_testdata/disjunct-root-ca.crt
Normal file
9
pkg/template/v2/_testdata/disjunct-root-ca.crt
Normal file
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBOTCB7KADAgECAhEA0cArEY0s5JoP8JfqIDv2BTAFBgMrZXAwGzEZMBcGA1UE
|
||||
AxMQZGlzanVuY3Qtcm9vdC1jYTAeFw0yMjAyMDkxMDI1MzBaFw0zMjAyMDcxMDI1
|
||||
MzBaMBsxGTAXBgNVBAMTEGRpc2p1bmN0LXJvb3QtY2EwKjAFBgMrZXADIQAJER3w
|
||||
QFZH1DBmxZm9IkaZ5noangcg/CYNP+GtcyQPL6NFMEMwDgYDVR0PAQH/BAQDAgEG
|
||||
MBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMddJG4lWXTN/x9InsiJUTxu
|
||||
d4+HMAUGAytlcANBABcyLV4om9LPV0TGDf0jiM+JxH1R+ATvAE8FHDtd8L66BrzA
|
||||
Id656nPz3fz9ZMB9VZr7iGcghXYlTHxu6NkQEgA=
|
||||
-----END CERTIFICATE-----
|
3
pkg/template/v2/_testdata/disjunct-root-ca.key
Normal file
3
pkg/template/v2/_testdata/disjunct-root-ca.key
Normal file
|
@ -0,0 +1,3 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEIDWjPU0WTbgryudXkq5qduRP5utcq8yEsmYPizQ0J1vW
|
||||
-----END PRIVATE KEY-----
|
BIN
pkg/template/v2/_testdata/foo-disjunct-nopass.pfx
Normal file
BIN
pkg/template/v2/_testdata/foo-disjunct-nopass.pfx
Normal file
Binary file not shown.
BIN
pkg/template/v2/_testdata/foo-multibag-nopass.pfx
Normal file
BIN
pkg/template/v2/_testdata/foo-multibag-nopass.pfx
Normal file
Binary file not shown.
BIN
pkg/template/v2/_testdata/foo-nopass.pfx
Normal file
BIN
pkg/template/v2/_testdata/foo-nopass.pfx
Normal file
Binary file not shown.
BIN
pkg/template/v2/_testdata/foo-withpass-1234.pfx
Normal file
BIN
pkg/template/v2/_testdata/foo-withpass-1234.pfx
Normal file
Binary file not shown.
11
pkg/template/v2/_testdata/foo.crt
Normal file
11
pkg/template/v2/_testdata/foo.crt
Normal file
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBqjCCAU+gAwIBAgIRAPnGGsBUMbZhmh5QdnYdBmUwCgYIKoZIzj0EAwIwGjEY
|
||||
MBYGA1UEAxMPaW50ZXJtZWRpYXRlLWNhMB4XDTIyMDIwOTEwMjUzMVoXDTIyMDIx
|
||||
MDEwMjUzMVowDjEMMAoGA1UEAxMDZm9vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
|
||||
QgAEqnxdeInykx8JZsLi13rZLekoG2cosQ3F+2InVNy7hCQ7soMqdaJsGQ6LFtov
|
||||
ogUFtOOTRWrunblqNWGZsowHbKOBgTB/MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE
|
||||
FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFLtundVbuKd73OWzo6SY
|
||||
by0Ajeb2MB8GA1UdIwQYMBaAFCLg80J/bZBbOd+Y8+V94l5xM2zEMA4GA1UdEQQH
|
||||
MAWCA2ZvbzAKBggqhkjOPQQDAgNJADBGAiEA4K4SbVNqrEtl7RfwBfJFMnWI+X8D
|
||||
zMPMc4Xqzp2qTxcCIQDsySgtiakypZfWakpB49zJph0kLwGK8xhWvGMUw1N1/w==
|
||||
-----END CERTIFICATE-----
|
5
pkg/template/v2/_testdata/foo.key
Normal file
5
pkg/template/v2/_testdata/foo.key
Normal file
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIMuAjEwBeXznOjx3V7viagAznflfL+p64CXkm++xlXhkoAoGCCqGSM49
|
||||
AwEHoUQDQgAEqnxdeInykx8JZsLi13rZLekoG2cosQ3F+2InVNy7hCQ7soMqdaJs
|
||||
GQ6LFtovogUFtOOTRWrunblqNWGZsowHbA==
|
||||
-----END EC PRIVATE KEY-----
|
11
pkg/template/v2/_testdata/intermediate-ca.crt
Normal file
11
pkg/template/v2/_testdata/intermediate-ca.crt
Normal file
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBgDCCATKgAwIBAgIRAOzjpCdp42oW5MoccLpRXpAwBQYDK2VwMBIxEDAOBgNV
|
||||
BAMTB3Jvb3QtY2EwHhcNMjIwMjA5MTAyNTMxWhcNMzIwMjA3MTAyNTMxWjAaMRgw
|
||||
FgYDVQQDEw9pbnRlcm1lZGlhdGUtY2EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
||||
AATekdyX6cZe0Ajmme363TQoWnrQwXnARzeWEf4FRQE8BGWgf8z7wljjpb4M4S4f
|
||||
+CJAYYY/6x38UnlsxXEeBTofo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/
|
||||
BAgwBgEB/wIBADAdBgNVHQ4EFgQUIuDzQn9tkFs535jz5X3iXnEzbMQwHwYDVR0j
|
||||
BBgwFoAUa2fUac2OZ3pzE6EydVq7UvwiQa0wBQYDK2VwA0EA4gntaGs/3ME6q1y9
|
||||
gO4ntri2qwoC25l3q7q9BiFBmeBmvS6I1w9HCZHtB3JnVC/IYDTCYDNTbpGWEOjl
|
||||
aCKLCA==
|
||||
-----END CERTIFICATE-----
|
5
pkg/template/v2/_testdata/intermediate-ca.key
Normal file
5
pkg/template/v2/_testdata/intermediate-ca.key
Normal file
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIIYmlQRczt7PR7VN9jjKf5MGcgKHgohcBbrPbikYVWNqoAoGCCqGSM49
|
||||
AwEHoUQDQgAE3pHcl+nGXtAI5pnt+t00KFp60MF5wEc3lhH+BUUBPARloH/M+8JY
|
||||
46W+DOEuH/giQGGGP+sd/FJ5bMVxHgU6Hw==
|
||||
-----END EC PRIVATE KEY-----
|
20
pkg/template/v2/_testdata/intermediate-chain.pem
Normal file
20
pkg/template/v2/_testdata/intermediate-chain.pem
Normal file
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBJzCB2qADAgECAhEArvunrLoYXTmwMROkmbAlBTAFBgMrZXAwEjEQMA4GA1UE
|
||||
AxMHcm9vdC1jYTAeFw0yMjAyMDkxMDI1MzBaFw0zMjAyMDcxMDI1MzBaMBIxEDAO
|
||||
BgNVBAMTB3Jvb3QtY2EwKjAFBgMrZXADIQDSw5uQ1io+jcKevCH0sl+tGTB6/BQs
|
||||
Bu84ibw13QoP36NFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
|
||||
AQEwHQYDVR0OBBYEFGtn1GnNjmd6cxOhMnVau1L8IkGtMAUGAytlcANBAOHSAS4z
|
||||
/6ctcvRwlGr9Hyt7vVLROImD2t3rFdDDHLLL1znikK3JZvVbETyMFOMbOMQS33C/
|
||||
4FtLGenZFXySjQw=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBgDCCATKgAwIBAgIRAOzjpCdp42oW5MoccLpRXpAwBQYDK2VwMBIxEDAOBgNV
|
||||
BAMTB3Jvb3QtY2EwHhcNMjIwMjA5MTAyNTMxWhcNMzIwMjA3MTAyNTMxWjAaMRgw
|
||||
FgYDVQQDEw9pbnRlcm1lZGlhdGUtY2EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
||||
AATekdyX6cZe0Ajmme363TQoWnrQwXnARzeWEf4FRQE8BGWgf8z7wljjpb4M4S4f
|
||||
+CJAYYY/6x38UnlsxXEeBTofo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/
|
||||
BAgwBgEB/wIBADAdBgNVHQ4EFgQUIuDzQn9tkFs535jz5X3iXnEzbMQwHwYDVR0j
|
||||
BBgwFoAUa2fUac2OZ3pzE6EydVq7UvwiQa0wBQYDK2VwA0EA4gntaGs/3ME6q1y9
|
||||
gO4ntri2qwoC25l3q7q9BiFBmeBmvS6I1w9HCZHtB3JnVC/IYDTCYDNTbpGWEOjl
|
||||
aCKLCA==
|
||||
-----END CERTIFICATE-----
|
9
pkg/template/v2/_testdata/root-ca.crt
Normal file
9
pkg/template/v2/_testdata/root-ca.crt
Normal file
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBJzCB2qADAgECAhEArvunrLoYXTmwMROkmbAlBTAFBgMrZXAwEjEQMA4GA1UE
|
||||
AxMHcm9vdC1jYTAeFw0yMjAyMDkxMDI1MzBaFw0zMjAyMDcxMDI1MzBaMBIxEDAO
|
||||
BgNVBAMTB3Jvb3QtY2EwKjAFBgMrZXADIQDSw5uQ1io+jcKevCH0sl+tGTB6/BQs
|
||||
Bu84ibw13QoP36NFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
|
||||
AQEwHQYDVR0OBBYEFGtn1GnNjmd6cxOhMnVau1L8IkGtMAUGAytlcANBAOHSAS4z
|
||||
/6ctcvRwlGr9Hyt7vVLROImD2t3rFdDDHLLL1znikK3JZvVbETyMFOMbOMQS33C/
|
||||
4FtLGenZFXySjQw=
|
||||
-----END CERTIFICATE-----
|
3
pkg/template/v2/_testdata/root-ca.key
Normal file
3
pkg/template/v2/_testdata/root-ca.key
Normal file
|
@ -0,0 +1,3 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEICJ7FAxZXElbLQe/yrvr+ZqYQHKf9oGtzsasBZ8a32nk
|
||||
-----END PRIVATE KEY-----
|
55
pkg/template/v2/jwk.go
Normal file
55
pkg/template/v2/jwk.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
)
|
||||
|
||||
func jwkPublicKeyPem(jwkjson string) (string, error) {
|
||||
k, err := jwk.ParseKey([]byte(jwkjson))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var rawkey interface{}
|
||||
err = k.Raw(&rawkey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mpk, err := x509.MarshalPKIXPublicKey(rawkey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return pemEncode(string(mpk), "PUBLIC KEY")
|
||||
}
|
||||
|
||||
func jwkPrivateKeyPem(jwkjson string) (string, error) {
|
||||
k, err := jwk.ParseKey([]byte(jwkjson))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var mpk []byte
|
||||
var pk interface{}
|
||||
err = k.Raw(&pk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mpk, err = x509.MarshalPKCS8PrivateKey(pk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return pemEncode(string(mpk), "PRIVATE KEY")
|
||||
}
|
62
pkg/template/v2/pem.go
Normal file
62
pkg/template/v2/pem.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
errJunk = "error filtering pem: found junk"
|
||||
)
|
||||
|
||||
func filterPEM(pemType, input string) (string, error) {
|
||||
data := []byte(input)
|
||||
var blocks []byte
|
||||
var block *pem.Block
|
||||
var rest []byte
|
||||
for {
|
||||
block, rest = pem.Decode(data)
|
||||
data = rest
|
||||
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if !strings.EqualFold(block.Type, pemType) {
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := pem.Encode(&buf, block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
blocks = append(blocks, buf.Bytes()...)
|
||||
}
|
||||
|
||||
if len(blocks) == 0 && len(rest) != 0 {
|
||||
return "", errors.New(errJunk)
|
||||
}
|
||||
|
||||
return string(blocks), nil
|
||||
}
|
||||
|
||||
func pemEncode(thing, kind string) (string, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err := pem.Encode(buf, &pem.Block{Type: kind, Bytes: []byte(thing)})
|
||||
return buf.String(), err
|
||||
}
|
141
pkg/template/v2/pem_chain.go
Normal file
141
pkg/template/v2/pem_chain.go
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
|
||||
Original Author: Anish Ramasekar https://github.com/aramase
|
||||
In: https://github.com/Azure/secrets-store-csi-driver-provider-azure/pull/332
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
errNilCert = "certificate is nil"
|
||||
errFoundDisjunctCert = "found multiple leaf or disjunct certificates"
|
||||
errNoLeafFound = "no leaf certificate found"
|
||||
errChainCycle = "constructing chain resulted in cycle"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
cert *x509.Certificate
|
||||
parent *node
|
||||
isParent bool
|
||||
}
|
||||
|
||||
func fetchCertChains(data []byte) ([]byte, error) {
|
||||
var newCertChain []*x509.Certificate
|
||||
var pemData []byte
|
||||
nodes, err := pemToNodes(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// at the end of this computation, the output will be a single linked list
|
||||
// the tail of the list will be the root node (which has no parents)
|
||||
// the head of the list will be the leaf node (whose parent will be intermediate certs)
|
||||
// (head) leaf -> intermediates -> root (tail)
|
||||
for i := range nodes {
|
||||
for j := range nodes {
|
||||
// ignore same node to prevent generating a cycle
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
// if ith node AuthorityKeyId is same as jth node SubjectKeyId, jth node was used
|
||||
// to sign the ith certificate
|
||||
if string(nodes[i].cert.AuthorityKeyId) == string(nodes[j].cert.SubjectKeyId) {
|
||||
nodes[j].isParent = true
|
||||
nodes[i].parent = nodes[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var foundLeaf bool
|
||||
var leaf *node
|
||||
for i := range nodes {
|
||||
if !nodes[i].isParent {
|
||||
if foundLeaf {
|
||||
return nil, fmt.Errorf(errFoundDisjunctCert)
|
||||
}
|
||||
// this is the leaf node as it's not a parent for any other node
|
||||
leaf = nodes[i]
|
||||
foundLeaf = true
|
||||
}
|
||||
}
|
||||
|
||||
if leaf == nil {
|
||||
return nil, fmt.Errorf(errNoLeafFound)
|
||||
}
|
||||
|
||||
processedNodes := 0
|
||||
// iterate through the directed list and append the nodes to new cert chain
|
||||
for leaf != nil {
|
||||
processedNodes++
|
||||
// ensure we aren't stuck in a cyclic loop
|
||||
if processedNodes > len(nodes) {
|
||||
return pemData, fmt.Errorf(errChainCycle)
|
||||
}
|
||||
newCertChain = append(newCertChain, leaf.cert)
|
||||
leaf = leaf.parent
|
||||
}
|
||||
|
||||
for _, cert := range newCertChain {
|
||||
b := &pem.Block{
|
||||
Type: pemTypeCertificate,
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
pemData = append(pemData, pem.EncodeToMemory(b)...)
|
||||
}
|
||||
return pemData, nil
|
||||
}
|
||||
|
||||
func pemToNodes(data []byte) ([]*node, error) {
|
||||
nodes := make([]*node, 0)
|
||||
for {
|
||||
// decode pem to der first
|
||||
block, rest := pem.Decode(data)
|
||||
data = rest
|
||||
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// this should not be the case because ParseCertificate should return a non nil
|
||||
// certificate when there is no error.
|
||||
if cert == nil {
|
||||
return nil, fmt.Errorf(errNilCert)
|
||||
}
|
||||
nodes = append(nodes, &node{
|
||||
cert: cert,
|
||||
parent: nil,
|
||||
isParent: false,
|
||||
})
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
180
pkg/template/v2/pem_test.go
Normal file
180
pkg/template/v2/pem_test.go
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import "testing"
|
||||
|
||||
const (
|
||||
certData = `-----BEGIN CERTIFICATE-----
|
||||
MIIDHTCCAgWgAwIBAgIRAKC4yxy9QGocND+6avTf7BgwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMTAzMjAyMDA4MDhaFw0yMTAzMjAyMDM4
|
||||
MDhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQC3o6/JdZEqNbqNRkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4Nzj
|
||||
aG15owr92/11W0pxPUliRLti3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvW
|
||||
Y8jh8A0LQALZiV/9QsrJdXZdS47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE
|
||||
1gEDqnKfRxXI8DEQKXr+CKPUwCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4e
|
||||
ugHe52vXHdh/HJ9VjNp0xOH1waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJa
|
||||
YOOonQSEswveSv6PcG9AHvpNPot2Xs6hAgMBAAGjbjBsMA4GA1UdDwEB/wQEAwIC
|
||||
pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
|
||||
BBR00805mrpoonp95RmC3B6oLl+cGTAVBgNVHREEDjAMggpnb29ibGUuY29tMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQAipc1b6JrEDayPjpz5GM5krcI8dCWVd8re0a9bGjjN
|
||||
ioWGlu/eTr5El0ffwCNZ2WLmL9rewfHf/bMvYz3ioFZJ2OTxfazqYXNggQz6cMfa
|
||||
lbedDCdt5XLVX2TyerGvFram+9Uyvk3l0uM7rZnwAmdirG4Tv94QRaD3q4xTj/c0
|
||||
mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
|
||||
9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
|
||||
QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
otherCert = `-----BEGIN CERTIFICATE-----
|
||||
MIIBqjCCAU+gAwIBAgIRAPnGGsBUMbZhmh5QdnYdBmUwCgYIKoZIzj0EAwIwGjEY
|
||||
MBYGA1UEAxMPaW50ZXJtZWRpYXRlLWNhMB4XDTIyMDIwOTEwMjUzMVoXDTIyMDIx
|
||||
MDEwMjUzMVowDjEMMAoGA1UEAxMDZm9vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
|
||||
QgAEqnxdeInykx8JZsLi13rZLekoG2cosQ3F+2InVNy7hCQ7soMqdaJsGQ6LFtov
|
||||
ogUFtOOTRWrunblqNWGZsowHbKOBgTB/MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE
|
||||
FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFLtundVbuKd73OWzo6SY
|
||||
by0Ajeb2MB8GA1UdIwQYMBaAFCLg80J/bZBbOd+Y8+V94l5xM2zEMA4GA1UdEQQH
|
||||
MAWCA2ZvbzAKBggqhkjOPQQDAgNJADBGAiEA4K4SbVNqrEtl7RfwBfJFMnWI+X8D
|
||||
zMPMc4Xqzp2qTxcCIQDsySgtiakypZfWakpB49zJph0kLwGK8xhWvGMUw1N1/w==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
keyData = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3o6/JdZEqNbqN
|
||||
RkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4NzjaG15owr92/11W0pxPUliRLti
|
||||
3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvWY8jh8A0LQALZiV/9QsrJdXZd
|
||||
S47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE1gEDqnKfRxXI8DEQKXr+CKPU
|
||||
wCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4eugHe52vXHdh/HJ9VjNp0xOH1
|
||||
waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJaYOOonQSEswveSv6PcG9AHvpN
|
||||
Pot2Xs6hAgMBAAECggEACTGPrmVNZDCWa1Y2hkJ0J7SoNcw+9O4M/jwMp4l/PD6P
|
||||
I98S78LYLCZhPLK17SmjUcnFO1AXKW1JeFS2D/fjfP256guvcqQNjLFoioxcOhVb
|
||||
ZGyd1Mi8JPqP5wfOj16gBeYDwTkjz9wqldcfiZaL9XoXetkZecbzR2JwC2FtIVuC
|
||||
0njTjMNYpaBKnoLb8OTR0EQz7lYEo2MkQiWryz8wseONnFmdfh18p+p10YgCbuCH
|
||||
qesrWfDLLxaxZelNtDhDngg9LoCLmarYy7BgShacmUEgJTZ/x3xFC75thK3ln0OY
|
||||
+ktTgvVotYYaZi7qAjQiEsTvkTAPg5RMpQLd2UIWsQKBgQDCBp+1vURbwGzmTNUg
|
||||
HMipD6WDFdLc9DCacx6+ZqsEPTMWQbCpVZrDKiY0Rjt5F+xOCyMr00J5RDJXRC0G
|
||||
+L7NcJdywOFutT7vB+cmETg7l/6PHweNYBnE66706eTL/KVYZMi4tEinarPWhHmL
|
||||
jasfdLANtpDjdWkRt299TkPRbQKBgQDyS8Rr7KZdv04Csqkf+ASmiJpT5R6Y72kc
|
||||
3XYpKETyB2FyPZkuh/zInMut9SkkSI9O/jA3zf956jj6sF1DHvp7T8KkIp5OAQeD
|
||||
J9AF65m2MnZfHFUeJ6ZQsggwMWqrD0ycIWP7YWtiBHH+D1wGkjYrssq+bvG/yNpA
|
||||
LtqdKq9lhQKBgQCZA2hIhy61vRckuEsLvCdzTGeW7UsR/XGnHEqOlaEhArKbRsrv
|
||||
gBdA+qiOaSTV5svw8E+YbE7sG6AnuhhYeyreEYEeeoZOLJmpIG5mUwYp2UBj1nC6
|
||||
SaOI7OVZOGu7g09SWokBQQxbG4cgEfFY4Sym7fs5lVTGTP3Dfwppo6NQMQKBgQCo
|
||||
J5NDP3Lafwk58BpV+H/pv8YzUUDh7M2rXbtCpxLqUdr8OOnVlEUISWFF8m5CIyVq
|
||||
MhjuscWLK9Wtjba7/YTjDaDM3sW05xv6lyfU5ATCoNTr/zLHgcb4HAZ4w+L+otiN
|
||||
RtMnxB2NYf5mzuwUF2cG/secUEzwyAlIH/xStSwTLQKBgQCRvqF+rqxnegoOgwVW
|
||||
qrWPv06wXD8dW2FlPpY5GXqA0l6erSK3YsQQToRmbem9ibPD7bd5P4gNbWfxwK4C
|
||||
Wt+1Rcb8OrDhDJbYz85bXBnPecKp4EN0b9SHO0/dsCqn2w30emc+9T/4m1ZDkpBd
|
||||
BixHvI/EJ8YK3ta5WdJWKC6hnA==
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
)
|
||||
|
||||
const (
|
||||
filterPrivateKey = "private key"
|
||||
filterCert = "certificate"
|
||||
)
|
||||
|
||||
func TestFilterPEM(t *testing.T) {
|
||||
type args struct {
|
||||
input string
|
||||
pemType string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "extract cert / cert first",
|
||||
args: args{
|
||||
input: certData + keyData,
|
||||
pemType: filterCert,
|
||||
},
|
||||
want: certData,
|
||||
},
|
||||
{
|
||||
name: "extract cert / key first",
|
||||
args: args{
|
||||
input: keyData + certData,
|
||||
pemType: filterCert,
|
||||
},
|
||||
want: certData,
|
||||
},
|
||||
{
|
||||
name: "extract multiple certs",
|
||||
args: args{
|
||||
input: keyData + certData + keyData + otherCert,
|
||||
pemType: filterCert,
|
||||
},
|
||||
want: certData + otherCert,
|
||||
},
|
||||
{
|
||||
name: "extract key",
|
||||
args: args{
|
||||
input: keyData + certData,
|
||||
pemType: filterPrivateKey,
|
||||
},
|
||||
want: keyData,
|
||||
},
|
||||
{
|
||||
name: "key with junk",
|
||||
args: args{
|
||||
input: certData + keyData + "some ---junk---",
|
||||
pemType: filterPrivateKey,
|
||||
},
|
||||
want: keyData,
|
||||
},
|
||||
{
|
||||
name: "begin/end with junk",
|
||||
args: args{
|
||||
// pem.Decode trims junk from the beginning of the input
|
||||
// so we are able to decode both cert & key
|
||||
input: "some junk" + certData + keyData + "some ---junk---",
|
||||
pemType: filterPrivateKey,
|
||||
},
|
||||
want: keyData,
|
||||
},
|
||||
{
|
||||
name: "interleaved junk",
|
||||
args: args{
|
||||
// can parse cert but not key due to junk
|
||||
input: certData + "some junk" + keyData,
|
||||
pemType: filterPrivateKey,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "err when junk",
|
||||
args: args{
|
||||
input: "---junk---",
|
||||
pemType: filterPrivateKey,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := filterPEM(tt.args.pemType, tt.args.input)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("filterPEM() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("filterPEM() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
109
pkg/template/v2/pkcs12.go
Normal file
109
pkg/template/v2/pkcs12.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
||||
func pkcs12keyPass(pass, input string) (string, error) {
|
||||
blocks, err := pkcs12.ToPEM([]byte(input), pass)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(errDecodePKCS12WithPass, err)
|
||||
}
|
||||
|
||||
var pemData []byte
|
||||
for _, block := range blocks {
|
||||
// remove bag attributes like localKeyID, friendlyName
|
||||
block.Headers = nil
|
||||
if block.Type == pemTypeCertificate {
|
||||
continue
|
||||
}
|
||||
key, err := parsePrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// we use pkcs8 because it supports more key types (ecdsa, ed25519), not just RSA
|
||||
block.Bytes, err = x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// report error if encode fails
|
||||
var buf bytes.Buffer
|
||||
if err := pem.Encode(&buf, block); err != nil {
|
||||
return "", err
|
||||
}
|
||||
pemData = append(pemData, buf.Bytes()...)
|
||||
}
|
||||
|
||||
return string(pemData), nil
|
||||
}
|
||||
|
||||
func parsePrivateKey(block []byte) (interface{}, error) {
|
||||
if k, err := x509.ParsePKCS1PrivateKey(block); err == nil {
|
||||
return k, nil
|
||||
}
|
||||
if k, err := x509.ParsePKCS8PrivateKey(block); err == nil {
|
||||
return k, nil
|
||||
}
|
||||
if k, err := x509.ParseECPrivateKey(block); err == nil {
|
||||
return k, nil
|
||||
}
|
||||
return nil, fmt.Errorf(errParsePrivKey)
|
||||
}
|
||||
|
||||
func pkcs12key(input string) (string, error) {
|
||||
return pkcs12keyPass("", input)
|
||||
}
|
||||
|
||||
func pkcs12certPass(pass, input string) (string, error) {
|
||||
blocks, err := pkcs12.ToPEM([]byte(input), pass)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(errDecodeCertWithPass, err)
|
||||
}
|
||||
|
||||
var pemData []byte
|
||||
for _, block := range blocks {
|
||||
if block.Type != pemTypeCertificate {
|
||||
continue
|
||||
}
|
||||
// remove bag attributes like localKeyID, friendlyName
|
||||
block.Headers = nil
|
||||
// report error if encode fails
|
||||
var buf bytes.Buffer
|
||||
if err := pem.Encode(&buf, block); err != nil {
|
||||
return "", err
|
||||
}
|
||||
pemData = append(pemData, buf.Bytes()...)
|
||||
}
|
||||
|
||||
// try to order certificate chain. If it fails we return
|
||||
// the unordered raw pem data.
|
||||
// This fails if multiple leaf or disjunct certs are provided.
|
||||
ordered, err := fetchCertChains(pemData)
|
||||
if err != nil {
|
||||
return string(pemData), nil
|
||||
}
|
||||
|
||||
return string(ordered), nil
|
||||
}
|
||||
|
||||
func pkcs12cert(input string) (string, error) {
|
||||
return pkcs12certPass("", input)
|
||||
}
|
95
pkg/template/v2/template.go
Normal file
95
pkg/template/v2/template.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
tpl "text/template"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
var tplFuncs = tpl.FuncMap{
|
||||
"pkcs12key": pkcs12key,
|
||||
"pkcs12keyPass": pkcs12keyPass,
|
||||
"pkcs12cert": pkcs12cert,
|
||||
"pkcs12certPass": pkcs12certPass,
|
||||
|
||||
"filterPEM": filterPEM,
|
||||
|
||||
"jwkPublicKeyPem": jwkPublicKeyPem,
|
||||
"jwkPrivateKeyPem": jwkPrivateKeyPem,
|
||||
}
|
||||
|
||||
// So other templating calls can use the same extra functions.
|
||||
func FuncMap() tpl.FuncMap {
|
||||
return tplFuncs
|
||||
}
|
||||
|
||||
const (
|
||||
errParse = "unable to parse template at key %s: %s"
|
||||
errExecute = "unable to execute template at key %s: %s"
|
||||
errDecodePKCS12WithPass = "unable to decode pkcs12 with password: %s"
|
||||
errDecodeCertWithPass = "unable to decode pkcs12 certificate with password: %s"
|
||||
errParsePrivKey = "unable to parse private key type"
|
||||
|
||||
pemTypeCertificate = "CERTIFICATE"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sprigFuncs := sprig.TxtFuncMap()
|
||||
delete(sprigFuncs, "env")
|
||||
delete(sprigFuncs, "expandenv")
|
||||
|
||||
for k, v := range sprigFuncs {
|
||||
tplFuncs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Execute renders the secret data as template. If an error occurs processing is stopped immediately.
|
||||
func Execute(tpl, data map[string][]byte, secret *corev1.Secret) error {
|
||||
if tpl == nil {
|
||||
return nil
|
||||
}
|
||||
for k, v := range tpl {
|
||||
val, err := execute(k, string(v), data)
|
||||
if err != nil {
|
||||
return fmt.Errorf(errExecute, k, err)
|
||||
}
|
||||
secret.Data[k] = val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func execute(k, val string, data map[string][]byte) ([]byte, error) {
|
||||
strValData := make(map[string]string, len(data))
|
||||
for k := range data {
|
||||
strValData[k] = string(data[k])
|
||||
}
|
||||
|
||||
t, err := tpl.New(k).
|
||||
Funcs(tplFuncs).
|
||||
Parse(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(errParse, k, err)
|
||||
}
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err = t.Execute(buf, strValData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(errExecute, k, err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
520
pkg/template/v2/template_test.go
Normal file
520
pkg/template/v2/template_test.go
Normal file
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package template
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
pkcs12ContentNoPass = `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`
|
||||
pkcs12ContentWithPass = `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`
|
||||
pkcs12Cert = `-----BEGIN CERTIFICATE-----
|
||||
MIIDHTCCAgWgAwIBAgIRAKC4yxy9QGocND+6avTf7BgwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMTAzMjAyMDA4MDhaFw0yMTAzMjAyMDM4
|
||||
MDhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQC3o6/JdZEqNbqNRkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4Nzj
|
||||
aG15owr92/11W0pxPUliRLti3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvW
|
||||
Y8jh8A0LQALZiV/9QsrJdXZdS47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE
|
||||
1gEDqnKfRxXI8DEQKXr+CKPUwCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4e
|
||||
ugHe52vXHdh/HJ9VjNp0xOH1waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJa
|
||||
YOOonQSEswveSv6PcG9AHvpNPot2Xs6hAgMBAAGjbjBsMA4GA1UdDwEB/wQEAwIC
|
||||
pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
|
||||
BBR00805mrpoonp95RmC3B6oLl+cGTAVBgNVHREEDjAMggpnb29ibGUuY29tMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQAipc1b6JrEDayPjpz5GM5krcI8dCWVd8re0a9bGjjN
|
||||
ioWGlu/eTr5El0ffwCNZ2WLmL9rewfHf/bMvYz3ioFZJ2OTxfazqYXNggQz6cMfa
|
||||
lbedDCdt5XLVX2TyerGvFram+9Uyvk3l0uM7rZnwAmdirG4Tv94QRaD3q4xTj/c0
|
||||
mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
|
||||
9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
|
||||
QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
pkcs12Key = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3o6/JdZEqNbqN
|
||||
RkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4NzjaG15owr92/11W0pxPUliRLti
|
||||
3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvWY8jh8A0LQALZiV/9QsrJdXZd
|
||||
S47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE1gEDqnKfRxXI8DEQKXr+CKPU
|
||||
wCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4eugHe52vXHdh/HJ9VjNp0xOH1
|
||||
waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJaYOOonQSEswveSv6PcG9AHvpN
|
||||
Pot2Xs6hAgMBAAECggEACTGPrmVNZDCWa1Y2hkJ0J7SoNcw+9O4M/jwMp4l/PD6P
|
||||
I98S78LYLCZhPLK17SmjUcnFO1AXKW1JeFS2D/fjfP256guvcqQNjLFoioxcOhVb
|
||||
ZGyd1Mi8JPqP5wfOj16gBeYDwTkjz9wqldcfiZaL9XoXetkZecbzR2JwC2FtIVuC
|
||||
0njTjMNYpaBKnoLb8OTR0EQz7lYEo2MkQiWryz8wseONnFmdfh18p+p10YgCbuCH
|
||||
qesrWfDLLxaxZelNtDhDngg9LoCLmarYy7BgShacmUEgJTZ/x3xFC75thK3ln0OY
|
||||
+ktTgvVotYYaZi7qAjQiEsTvkTAPg5RMpQLd2UIWsQKBgQDCBp+1vURbwGzmTNUg
|
||||
HMipD6WDFdLc9DCacx6+ZqsEPTMWQbCpVZrDKiY0Rjt5F+xOCyMr00J5RDJXRC0G
|
||||
+L7NcJdywOFutT7vB+cmETg7l/6PHweNYBnE66706eTL/KVYZMi4tEinarPWhHmL
|
||||
jasfdLANtpDjdWkRt299TkPRbQKBgQDyS8Rr7KZdv04Csqkf+ASmiJpT5R6Y72kc
|
||||
3XYpKETyB2FyPZkuh/zInMut9SkkSI9O/jA3zf956jj6sF1DHvp7T8KkIp5OAQeD
|
||||
J9AF65m2MnZfHFUeJ6ZQsggwMWqrD0ycIWP7YWtiBHH+D1wGkjYrssq+bvG/yNpA
|
||||
LtqdKq9lhQKBgQCZA2hIhy61vRckuEsLvCdzTGeW7UsR/XGnHEqOlaEhArKbRsrv
|
||||
gBdA+qiOaSTV5svw8E+YbE7sG6AnuhhYeyreEYEeeoZOLJmpIG5mUwYp2UBj1nC6
|
||||
SaOI7OVZOGu7g09SWokBQQxbG4cgEfFY4Sym7fs5lVTGTP3Dfwppo6NQMQKBgQCo
|
||||
J5NDP3Lafwk58BpV+H/pv8YzUUDh7M2rXbtCpxLqUdr8OOnVlEUISWFF8m5CIyVq
|
||||
MhjuscWLK9Wtjba7/YTjDaDM3sW05xv6lyfU5ATCoNTr/zLHgcb4HAZ4w+L+otiN
|
||||
RtMnxB2NYf5mzuwUF2cG/secUEzwyAlIH/xStSwTLQKBgQCRvqF+rqxnegoOgwVW
|
||||
qrWPv06wXD8dW2FlPpY5GXqA0l6erSK3YsQQToRmbem9ibPD7bd5P4gNbWfxwK4C
|
||||
Wt+1Rcb8OrDhDJbYz85bXBnPecKp4EN0b9SHO0/dsCqn2w30emc+9T/4m1ZDkpBd
|
||||
BixHvI/EJ8YK3ta5WdJWKC6hnA==
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
jwkPubRSA = `{"kid":"ex","kty":"RSA","key_ops":["sign","verify","wrapKey","unwrapKey","encrypt","decrypt"],"n":"p2VQo8qCfWAZmdWBVaYuYb-a-tWWm78K6Sr9poCvNcmv8rUPSLACxitQWR8gZaSH1DklVkqz-Ed8Cdlf8lkDg4Ex5tkB64jRdC1Uvn4CDpOH6cp-N2s8hTFLqy9_YaDmyQS7HiqthOi9oVjil1VMeWfaAbClGtFt6UnKD0Vb_DvLoWYQSqlhgBArFJi966b4E1pOq5Ad02K8pHBDThlIIx7unibLehhDU6q3DCwNH_OOLx6bgNtmvGYJDd1cywpkLQ3YzNCUPWnfMBJRP3iQP_WI21uP6cvo0DqBPBM4wvVzHbCT0vnIflwkbgEWkq1FprqAitZlop9KjLqzjp9vyQ","e":"AQAB"}`
|
||||
jwkPubRSAPKIX = `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2VQo8qCfWAZmdWBVaYu
|
||||
Yb+a+tWWm78K6Sr9poCvNcmv8rUPSLACxitQWR8gZaSH1DklVkqz+Ed8Cdlf8lkD
|
||||
g4Ex5tkB64jRdC1Uvn4CDpOH6cp+N2s8hTFLqy9/YaDmyQS7HiqthOi9oVjil1VM
|
||||
eWfaAbClGtFt6UnKD0Vb/DvLoWYQSqlhgBArFJi966b4E1pOq5Ad02K8pHBDThlI
|
||||
Ix7unibLehhDU6q3DCwNH/OOLx6bgNtmvGYJDd1cywpkLQ3YzNCUPWnfMBJRP3iQ
|
||||
P/WI21uP6cvo0DqBPBM4wvVzHbCT0vnIflwkbgEWkq1FprqAitZlop9KjLqzjp9v
|
||||
yQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
jwkPrivRSA = `{"kty" : "RSA","kid" : "cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df","use" : "sig","n" : "pjdss8ZaDfEH6K6U7GeW2nxDqR4IP049fk1fK0lndimbMMVBdPv_hSpm8T8EtBDxrUdi1OHZfMhUixGaut-3nQ4GG9nM249oxhCtxqqNvEXrmQRGqczyLxuh-fKn9Fg--hS9UpazHpfVAFnB5aCfXoNhPuI8oByyFKMKaOVgHNqP5NBEqabiLftZD3W_lsFCPGuzr4Vp0YS7zS2hDYScC2oOMu4rGU1LcMZf39p3153Cq7bS2Xh6Y-vw5pwzFYZdjQxDn8x8BG3fJ6j8TGLXQsbKH1218_HcUJRvMwdpbUQG5nvA2GXVqLqdwp054Lzk9_B_f1lVrmOKuHjTNHq48w","e" : "AQAB","d" : "ksDmucdMJXkFGZxiomNHnroOZxe8AmDLDGO1vhs-POa5PZM7mtUPonxwjVmthmpbZzla-kg55OFfO7YcXhg-Hm2OWTKwm73_rLh3JavaHjvBqsVKuorX3V3RYkSro6HyYIzFJ1Ek7sLxbjDRcDOj4ievSX0oN9l-JZhaDYlPlci5uJsoqro_YrE0PRRWVhtGynd-_aWgQv1YzkfZuMD-hJtDi1Im2humOWxA4eZrFs9eG-whXcOvaSwO4sSGbS99ecQZHM2TcdXeAs1PvjVgQ_dKnZlGN3lTWoWfQP55Z7Tgt8Nf1q4ZAKd-NlMe-7iqCFfsnFwXjSiaOa2CRGZn-Q","p" : "4A5nU4ahEww7B65yuzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ--wwfpRwHvSxtNU9qXb8ewo-BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3InKF4JvIlchyqs0RQ8wx7lULqwnn0","q" : "ven83GM6SfrmO-TBHbjTk6JhP_3CMsIvmSdo4KrbQNvp4vHO3w1_0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEBpxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA-k4UoH_eQmGKGK44TRzYj5hZYGWIC8","dp" : "lmmU_AG5SGxBhJqb8wxfNXDPJjf__i92BgJT2Vp4pskBbr5PGoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ-m0_XSWx13v9t9DIbheAtgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpE","dq" : "mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe__EjuCBbwHfcT8OG3hWOv8vpzokQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p-AF2p6Yfahscjtq-GY9cB85NxLy2IXCC0PF--Sq9LOrTE9QV988SJy_yUrAjcZ5MmECk","qi" : "ldHXIrEmMZVaNwGzDF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uYiqewXfCKw_UngrJt8Xwfq1Zruz0YY869zPN4GiE9-9rzdZB33RBw8kIOquY3MK74FMwCihYx_LiU2YTHkaoJ3ncvtvg"}`
|
||||
jwkPrivRSAPKCS8 = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCmN2yzxloN8Qfo
|
||||
rpTsZ5bafEOpHgg/Tj1+TV8rSWd2KZswxUF0+/+FKmbxPwS0EPGtR2LU4dl8yFSL
|
||||
EZq637edDgYb2czbj2jGEK3Gqo28ReuZBEapzPIvG6H58qf0WD76FL1SlrMel9UA
|
||||
WcHloJ9eg2E+4jygHLIUowpo5WAc2o/k0ESppuIt+1kPdb+WwUI8a7OvhWnRhLvN
|
||||
LaENhJwLag4y7isZTUtwxl/f2nfXncKrttLZeHpj6/DmnDMVhl2NDEOfzHwEbd8n
|
||||
qPxMYtdCxsofXbXz8dxQlG8zB2ltRAbme8DYZdWoup3CnTngvOT38H9/WVWuY4q4
|
||||
eNM0erjzAgMBAAECggEBAJLA5rnHTCV5BRmcYqJjR566DmcXvAJgywxjtb4bPjzm
|
||||
uT2TO5rVD6J8cI1ZrYZqW2c5WvpIOeThXzu2HF4YPh5tjlkysJu9/6y4dyWr2h47
|
||||
warFSrqK191d0WJEq6Oh8mCMxSdRJO7C8W4w0XAzo+Inr0l9KDfZfiWYWg2JT5XI
|
||||
ubibKKq6P2KxND0UVlYbRsp3fv2loEL9WM5H2bjA/oSbQ4tSJtobpjlsQOHmaxbP
|
||||
XhvsIV3Dr2ksDuLEhm0vfXnEGRzNk3HV3gLNT741YEP3Sp2ZRjd5U1qFn0D+eWe0
|
||||
4LfDX9auGQCnfjZTHvu4qghX7JxcF40omjmtgkRmZ/kCgYEA4A5nU4ahEww7B65y
|
||||
uzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ++wwf
|
||||
pRwHvSxtNU9qXb8ewo+BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3In
|
||||
KF4JvIlchyqs0RQ8wx7lULqwnn0CgYEAven83GM6SfrmO+TBHbjTk6JhP/3CMsIv
|
||||
mSdo4KrbQNvp4vHO3w1/0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEB
|
||||
pxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA+k4UoH/eQmGKGK44TRz
|
||||
Yj5hZYGWIC8CgYEAlmmU/AG5SGxBhJqb8wxfNXDPJjf//i92BgJT2Vp4pskBbr5P
|
||||
GoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ+m0/XSWx13v9t9DIbheA
|
||||
tgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpECgYEA
|
||||
mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe//EjuCBbwHfcT8OG3hWOv8vpzo
|
||||
kQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p+AF2p6Yfahscjtq+GY9cB85Nx
|
||||
Ly2IXCC0PF++Sq9LOrTE9QV988SJy/yUrAjcZ5MmECkCgYEAldHXIrEmMZVaNwGz
|
||||
DF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uY
|
||||
iqewXfCKw/UngrJt8Xwfq1Zruz0YY869zPN4GiE9+9rzdZB33RBw8kIOquY3MK74
|
||||
FMwCihYx/LiU2YTHkaoJ3ncvtvg=
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
jwkPubEC = `{"kid":"https://kv-test-mj.vault.azure.net/keys/ec-p-521/e3d0e9c179b54988860c69c6ae172c65","kty":"EC","key_ops":["sign","verify"],"crv":"P-521","x":"AedOAtb7H7Oz1C_cPKI_R4CN_eai5nteY6KFW07FOoaqgQfVCSkQDK22fCOiMT_28c8LZYJRsiIFz_IIbQUW7bXj","y":"AOnchHnmBphIWXvanmMAmcCDkaED6ycW8GsAl9fQ43BMVZTqcTkJYn6vGnhn7MObizmkNSmgZYTwG-vZkIg03HHs"}`
|
||||
jwkPubECPKIX = `-----BEGIN PUBLIC KEY-----
|
||||
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB504C1vsfs7PUL9w8oj9HgI395qLm
|
||||
e15jooVbTsU6hqqBB9UJKRAMrbZ8I6IxP/bxzwtlglGyIgXP8ghtBRbtteMA6dyE
|
||||
eeYGmEhZe9qeYwCZwIORoQPrJxbwawCX19DjcExVlOpxOQlifq8aeGfsw5uLOaQ1
|
||||
KaBlhPAb69mQiDTccew=
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
jwkPrivEC = `{"kty": "EC","kid": "rie3pHe8u8gjSa0IaJfqk7_iEfHeYfDYx-Bqi7vQc0s","crv": "P-256","x": "fDjg3Nq4jPf8IOZ0277aPVal_8iXySnzLUJAZghUzZM","y": "d863PeyBOK_Q4duiSmWwgIRzi1RPlFZTR-vACMlPg-Q","d": "jJs5xsoHUetdMabtt8H2KyX5T92nGul1chFeMT5hlr0"}`
|
||||
jwkPrivECPKCS8 = `-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjJs5xsoHUetdMabt
|
||||
t8H2KyX5T92nGul1chFeMT5hlr2hRANCAAR8OODc2riM9/wg5nTbvto9VqX/yJfJ
|
||||
KfMtQkBmCFTNk3fOtz3sgTiv0OHbokplsICEc4tUT5RWU0frwAjJT4Pk
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
)
|
||||
|
||||
func TestExecute(t *testing.T) {
|
||||
tbl := []struct {
|
||||
name string
|
||||
tpl map[string][]byte
|
||||
data map[string][]byte
|
||||
expetedData map[string][]byte
|
||||
expErr string
|
||||
}{
|
||||
{
|
||||
name: "test empty",
|
||||
tpl: nil,
|
||||
data: nil,
|
||||
},
|
||||
{
|
||||
name: "b64dec func",
|
||||
tpl: map[string][]byte{
|
||||
"foo": []byte("{{ .secret | b64dec }}"),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte("MTIzNA=="),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"foo": []byte("1234"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fromJSON func",
|
||||
tpl: map[string][]byte{
|
||||
"foo": []byte("{{ $var := .secret | fromJson }}{{ $var.foo }}"),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(`{"foo": "bar"}`),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"foo": []byte("bar"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from & toJSON func",
|
||||
tpl: map[string][]byte{
|
||||
"foo": []byte("{{ $var := .secret | fromJson }}{{ $var.foo | toJson }}"),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(`{"foo": {"baz":"bang"}}`),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"foo": []byte(`{"baz":"bang"}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "use sprig functions",
|
||||
tpl: map[string][]byte{
|
||||
"foo": []byte(`{{ .path | ext }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"path": []byte(`foo/bar/baz.exe`),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"foo": []byte(`.exe`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiline template",
|
||||
tpl: map[string][]byte{
|
||||
"cfg": []byte(`
|
||||
datasources:
|
||||
- name: Graphite
|
||||
type: graphite
|
||||
access: proxy
|
||||
url: http://localhost:8080
|
||||
password: "{{ .password }}"
|
||||
user: "{{ .user }}"`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"user": []byte(`foobert`),
|
||||
"password": []byte("harharhar"),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"cfg": []byte(`
|
||||
datasources:
|
||||
- name: Graphite
|
||||
type: graphite
|
||||
access: proxy
|
||||
url: http://localhost:8080
|
||||
password: "harharhar"
|
||||
user: "foobert"`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "base64 pipeline",
|
||||
tpl: map[string][]byte{
|
||||
"foo": []byte(`{{ "123412341234" | b64enc | b64dec }}`),
|
||||
},
|
||||
data: map[string][]byte{},
|
||||
expetedData: map[string][]byte{
|
||||
"foo": []byte("123412341234"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "base64 pkcs12 extract",
|
||||
tpl: map[string][]byte{
|
||||
"key": []byte(`{{ .secret | b64dec | pkcs12key }}`),
|
||||
"cert": []byte(`{{ .secret | b64dec | pkcs12cert }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(pkcs12ContentNoPass),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"key": []byte(pkcs12Key),
|
||||
"cert": []byte(pkcs12Cert),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "base64 pkcs12 extract with password",
|
||||
tpl: map[string][]byte{
|
||||
"key": []byte(`{{ .secret | b64dec | pkcs12keyPass "123456" }}`),
|
||||
"cert": []byte(`{{ .secret | b64dec | pkcs12certPass "123456" }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(pkcs12ContentWithPass),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"key": []byte(pkcs12Key),
|
||||
"cert": []byte(pkcs12Cert),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "base64 decode error",
|
||||
tpl: map[string][]byte{
|
||||
"key": []byte(`{{ .example | b64dec }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"example": []byte("iam_no_base64"),
|
||||
},
|
||||
expErr: "", // silent error
|
||||
},
|
||||
{
|
||||
name: "pkcs12 key wrong password",
|
||||
tpl: map[string][]byte{
|
||||
"key": []byte(`{{ .secret | b64dec | pkcs12keyPass "wrong" }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(pkcs12ContentWithPass),
|
||||
},
|
||||
expErr: "unable to decode pkcs12",
|
||||
},
|
||||
{
|
||||
name: "pkcs12 cert wrong password",
|
||||
tpl: map[string][]byte{
|
||||
"cert": []byte(`{{ .secret | b64dec | pkcs12certPass "wrong" }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(pkcs12ContentWithPass),
|
||||
},
|
||||
expErr: "unable to decode pkcs12",
|
||||
},
|
||||
{
|
||||
name: "fromJSON error",
|
||||
tpl: map[string][]byte{
|
||||
"key": []byte(`{{ "{ # no json # }" | fromJson }}`),
|
||||
},
|
||||
data: map[string][]byte{},
|
||||
expErr: "", // silent error
|
||||
},
|
||||
{
|
||||
name: "template syntax error",
|
||||
tpl: map[string][]byte{
|
||||
"key": []byte(`{{ #xx }}`),
|
||||
},
|
||||
data: map[string][]byte{},
|
||||
expErr: "unable to parse template",
|
||||
},
|
||||
{
|
||||
name: "jwk rsa pub pem",
|
||||
tpl: map[string][]byte{
|
||||
"fn": []byte(`{{ .secret | jwkPublicKeyPem }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(jwkPubRSA),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"fn": []byte(jwkPubRSAPKIX),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jwk rsa priv pem",
|
||||
tpl: map[string][]byte{
|
||||
"fn": []byte(`{{ .secret | jwkPrivateKeyPem }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(jwkPrivRSA),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"fn": []byte(jwkPrivRSAPKCS8),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jwk ecdsa pub pem",
|
||||
tpl: map[string][]byte{
|
||||
"fn": []byte(`{{ .secret | jwkPublicKeyPem }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(jwkPubEC),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"fn": []byte(jwkPubECPKIX),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jwk ecdsa priv pem",
|
||||
tpl: map[string][]byte{
|
||||
"fn": []byte(`{{ .secret | jwkPrivateKeyPem }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(jwkPrivEC),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"fn": []byte(jwkPrivECPKCS8),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filter pem certificate",
|
||||
tpl: map[string][]byte{
|
||||
"fn": []byte(`{{ .secret | filterPEM "CERTIFICATE" }}`),
|
||||
},
|
||||
data: map[string][]byte{
|
||||
"secret": []byte(jwkPrivRSAPKCS8 + pkcs12Cert),
|
||||
},
|
||||
expetedData: map[string][]byte{
|
||||
"fn": []byte(pkcs12Cert),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tbl {
|
||||
row := tbl[i]
|
||||
t.Run(row.name, func(t *testing.T) {
|
||||
sec := &corev1.Secret{
|
||||
Data: make(map[string][]byte),
|
||||
}
|
||||
err := Execute(row.tpl, row.data, sec)
|
||||
if !ErrorContains(err, row.expErr) {
|
||||
t.Errorf("unexpected error: %s, expected: %s", err, row.expErr)
|
||||
}
|
||||
if row.expetedData == nil {
|
||||
return
|
||||
}
|
||||
assert.EqualValues(t, row.expetedData, sec.Data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorContains(out error, want string) bool {
|
||||
if out == nil {
|
||||
return want == ""
|
||||
}
|
||||
if want == "" {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(out.Error(), want)
|
||||
}
|
||||
|
||||
func TestPkcs12certPass(t *testing.T) {
|
||||
const (
|
||||
leafCertPath = "_testdata/foo.crt"
|
||||
intermediateCertPath = "_testdata/intermediate-ca.crt"
|
||||
rootCertPath = "_testdata/root-ca.crt"
|
||||
disjunctCertPath = "_testdata/disjunct-root-ca.crt"
|
||||
)
|
||||
type args struct {
|
||||
pass string
|
||||
filename string
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
wantErr bool
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
// this case expects the whole chain to be stored
|
||||
// in a single bag.
|
||||
// bag(1): leaf/root/intermediate cert
|
||||
// bag(2): private key
|
||||
name: "read file without password",
|
||||
args: args{
|
||||
pass: "",
|
||||
filename: "_testdata/foo-nopass.pfx",
|
||||
},
|
||||
want: []string{
|
||||
// this order is important
|
||||
leafCertPath,
|
||||
intermediateCertPath,
|
||||
rootCertPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
// same as above but with password
|
||||
name: "read file with password",
|
||||
args: args{
|
||||
pass: "1234",
|
||||
filename: "_testdata/foo-withpass-1234.pfx",
|
||||
},
|
||||
want: []string{
|
||||
// this order is important
|
||||
leafCertPath,
|
||||
intermediateCertPath,
|
||||
rootCertPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
// cert chain may be stored in different bags
|
||||
// this test case uses a pfx that has the following structure:
|
||||
// bag(1): leaf certificate
|
||||
// bag(2): root + intermediate cert
|
||||
// bag(3): private key
|
||||
name: "read multibag cert chain",
|
||||
args: args{
|
||||
pass: "",
|
||||
filename: "_testdata/foo-multibag-nopass.pfx",
|
||||
},
|
||||
want: []string{
|
||||
// this order is important
|
||||
leafCertPath,
|
||||
intermediateCertPath,
|
||||
rootCertPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
// cert chain may contain a disjunct cert
|
||||
// bag(1): leaf/root/intermediate/disjunct
|
||||
// bag(2): private key
|
||||
name: "read disjunct cert chain",
|
||||
args: args{
|
||||
pass: "",
|
||||
filename: "_testdata/foo-disjunct-nopass.pfx",
|
||||
},
|
||||
want: []string{
|
||||
// this order is important
|
||||
leafCertPath,
|
||||
rootCertPath,
|
||||
intermediateCertPath,
|
||||
disjunctCertPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read file wrong password",
|
||||
args: args{
|
||||
pass: "wrongpass",
|
||||
filename: "_testdata/foo-withpass-1234.pfx",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
testFunc := func(t *testing.T, tc testCase) {
|
||||
archive, err := os.ReadFile(tc.args.filename)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var expOut []byte
|
||||
for _, w := range tc.want {
|
||||
c, err := os.ReadFile(w)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expOut = append(expOut, c...)
|
||||
}
|
||||
got, err := pkcs12certPass(tc.args.pass, string(archive))
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Errorf("pkcs12certPass() error = %v, wantErr %v", err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(string(expOut), got); diff != "" {
|
||||
t.Errorf("pkcs12certPass() = diff:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testFunc(t, tt)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue