1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

feat: add AWS STS Session token generator (#4041)

* feat: add AWS STS Session token generator

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* version update for the generated CRD

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

---------

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
This commit is contained in:
Gergely Brautigam 2024-11-05 13:22:00 +01:00 committed by GitHub
parent 6b70c9002f
commit d4d4f4bc4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 928 additions and 2 deletions

View file

@ -0,0 +1,80 @@
/*
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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RequestParameters contains parameters that can be passed to the STS service.
type RequestParameters struct {
// SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
// IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
// (12 hours) as the default.
// +optional
SessionDuration *int64 `json:"sessionDuration,omitempty"`
// SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
// the GetSessionToken call.
// Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
// (such as arn:aws:iam::123456789012:mfa/user)
// +optional
SerialNumber *string `json:"serialNumber,omitempty"`
// TokenCode is the value provided by the MFA device, if MFA is required.
// +optional
TokenCode *string `json:"tokenCode,omitempty"`
}
type STSSessionTokenSpec struct {
// Region specifies the region to operate in.
Region string `json:"region"`
// Auth defines how to authenticate with AWS
// +optional
Auth AWSAuth `json:"auth,omitempty"`
// You can assume a role before making calls to the
// desired AWS service.
// +optional
Role string `json:"role,omitempty"`
// RequestParameters contains parameters that can be passed to the STS service.
// +optional
RequestParameters *RequestParameters `json:"requestParameters,omitempty"`
}
// STSSessionToken uses the GetSessionToken API to retrieve an authorization token.
// The authorization token is valid for 12 hours.
// The authorizationToken returned is a base64 encoded string that can be decoded.
// For more information, see GetSessionToken (https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html).
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators},shortName=stssessiontoken
type STSSessionToken struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec STSSessionTokenSpec `json:"spec,omitempty"`
}
// +kubebuilder:object:root=true
// STSSessionTokenList contains a list of STSSessionToken resources.
type STSSessionTokenList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []STSSessionToken `json:"items"`
}

View file

@ -44,6 +44,14 @@ var (
ECRAuthorizationTokenGroupVersionKind = SchemeGroupVersion.WithKind(ECRAuthorizationTokenKind)
)
// STSSessionToken type metadata.
var (
STSSessionTokenKind = reflect.TypeOf(STSSessionToken{}).Name()
STSSessionTokenGroupKind = schema.GroupKind{Group: Group, Kind: STSSessionTokenKind}.String()
STSSessionTokenKindAPIVersion = STSSessionTokenKind + "." + SchemeGroupVersion.String()
STSSessionTokenGroupVersionKind = SchemeGroupVersion.WithKind(STSSessionTokenKind)
)
// GCRAccessToken type metadata.
var (
GCRAccessTokenKind = reflect.TypeOf(GCRAccessToken{}).Name()

View file

@ -772,6 +772,115 @@ func (in *PasswordSpec) DeepCopy() *PasswordSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RequestParameters) DeepCopyInto(out *RequestParameters) {
*out = *in
if in.SessionDuration != nil {
in, out := &in.SessionDuration, &out.SessionDuration
*out = new(int64)
**out = **in
}
if in.SerialNumber != nil {
in, out := &in.SerialNumber, &out.SerialNumber
*out = new(string)
**out = **in
}
if in.TokenCode != nil {
in, out := &in.TokenCode, &out.TokenCode
*out = new(string)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestParameters.
func (in *RequestParameters) DeepCopy() *RequestParameters {
if in == nil {
return nil
}
out := new(RequestParameters)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *STSSessionToken) DeepCopyInto(out *STSSessionToken) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new STSSessionToken.
func (in *STSSessionToken) DeepCopy() *STSSessionToken {
if in == nil {
return nil
}
out := new(STSSessionToken)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *STSSessionToken) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *STSSessionTokenList) DeepCopyInto(out *STSSessionTokenList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]STSSessionToken, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new STSSessionTokenList.
func (in *STSSessionTokenList) DeepCopy() *STSSessionTokenList {
if in == nil {
return nil
}
out := new(STSSessionTokenList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *STSSessionTokenList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *STSSessionTokenSpec) DeepCopyInto(out *STSSessionTokenSpec) {
*out = *in
in.Auth.DeepCopyInto(&out.Auth)
if in.RequestParameters != nil {
in, out := &in.RequestParameters, &out.RequestParameters
*out = new(RequestParameters)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new STSSessionTokenSpec.
func (in *STSSessionTokenSpec) DeepCopy() *STSSessionTokenSpec {
if in == nil {
return nil
}
out := new(STSSessionTokenSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in

View file

@ -0,0 +1,183 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.5
labels:
external-secrets.io/component: controller
name: stssessiontokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- external-secrets
- external-secrets-generators
kind: STSSessionToken
listKind: STSSessionTokenList
plural: stssessiontokens
shortNames:
- stssessiontoken
singular: stssessiontoken
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: |-
STSSessionToken uses the GetSessionToken API to retrieve an authorization token.
The authorization token is valid for 12 hours.
The authorizationToken returned is a base64 encoded string that can be decoded.
For more information, see GetSessionToken (https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html).
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
auth:
description: Auth defines how to authenticate with AWS
properties:
jwt:
description: Authenticate against AWS using service account tokens.
properties:
serviceAccountRef:
description: A reference to a ServiceAccount resource.
properties:
audiences:
description: |-
Audience specifies the `aud` claim for the service account token
If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
then this audiences will be appended to the list
items:
type: string
type: array
name:
description: The name of the ServiceAccount resource being
referred to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
required:
- name
type: object
type: object
secretRef:
description: |-
AWSAuthSecretRef holds secret references for AWS credentials
both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
properties:
accessKeyIDSecretRef:
description: The AccessKeyID is used for authentication
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
defaulted, in others it may be required.
type: string
name:
description: The name of the Secret resource being referred
to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
type: object
secretAccessKeySecretRef:
description: The SecretAccessKey is used for authentication
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
defaulted, in others it may be required.
type: string
name:
description: The name of the Secret resource being referred
to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
type: object
sessionTokenSecretRef:
description: |-
The SessionToken used for authentication
This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
defaulted, in others it may be required.
type: string
name:
description: The name of the Secret resource being referred
to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
type: object
type: object
type: object
region:
description: Region specifies the region to operate in.
type: string
requestParameters:
description: RequestParameters contains parameters that can be passed
to the STS service.
properties:
serialNumber:
description: |-
SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
the GetSessionToken call.
Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
(such as arn:aws:iam::123456789012:mfa/user)
type: string
sessionDuration:
description: |-
SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
(12 hours) as the default.
format: int64
type: integer
tokenCode:
description: TokenCode is the value provided by the MFA device,
if MFA is required.
type: string
type: object
role:
description: |-
You can assume a role before making calls to the
desired AWS service.
type: string
required:
- region
type: object
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -13,6 +13,7 @@ resources:
- generators.external-secrets.io_gcraccesstokens.yaml
- generators.external-secrets.io_githubaccesstokens.yaml
- generators.external-secrets.io_passwords.yaml
- generators.external-secrets.io_stssessiontokens.yaml
- generators.external-secrets.io_uuids.yaml
- generators.external-secrets.io_vaultdynamicsecrets.yaml
- generators.external-secrets.io_webhooks.yaml

View file

@ -11920,6 +11920,194 @@ spec:
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.5
labels:
external-secrets.io/component: controller
name: stssessiontokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- external-secrets
- external-secrets-generators
kind: STSSessionToken
listKind: STSSessionTokenList
plural: stssessiontokens
shortNames:
- stssessiontoken
singular: stssessiontoken
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: |-
STSSessionToken uses the GetSessionToken API to retrieve an authorization token.
The authorization token is valid for 12 hours.
The authorizationToken returned is a base64 encoded string that can be decoded.
For more information, see GetSessionToken (https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html).
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
auth:
description: Auth defines how to authenticate with AWS
properties:
jwt:
description: Authenticate against AWS using service account tokens.
properties:
serviceAccountRef:
description: A reference to a ServiceAccount resource.
properties:
audiences:
description: |-
Audience specifies the `aud` claim for the service account token
If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
then this audiences will be appended to the list
items:
type: string
type: array
name:
description: The name of the ServiceAccount resource being referred to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
required:
- name
type: object
type: object
secretRef:
description: |-
AWSAuthSecretRef holds secret references for AWS credentials
both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
properties:
accessKeyIDSecretRef:
description: The AccessKeyID is used for authentication
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
defaulted, in others it may be required.
type: string
name:
description: The name of the Secret resource being referred to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
type: object
secretAccessKeySecretRef:
description: The SecretAccessKey is used for authentication
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
defaulted, in others it may be required.
type: string
name:
description: The name of the Secret resource being referred to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
type: object
sessionTokenSecretRef:
description: |-
The SessionToken used for authentication
This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
defaulted, in others it may be required.
type: string
name:
description: The name of the Secret resource being referred to.
type: string
namespace:
description: |-
Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
to the namespace of the referent.
type: string
type: object
type: object
type: object
region:
description: Region specifies the region to operate in.
type: string
requestParameters:
description: RequestParameters contains parameters that can be passed to the STS service.
properties:
serialNumber:
description: |-
SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
the GetSessionToken call.
Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
(such as arn:aws:iam::123456789012:mfa/user)
type: string
sessionDuration:
description: |-
SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
(12 hours) as the default.
format: int64
type: integer
tokenCode:
description: TokenCode is the value provided by the MFA device, if MFA is required.
type: string
type: object
role:
description: |-
You can assume a role before making calls to the
desired AWS service.
type: string
required:
- region
type: object
type: object
served: true
storage: true
subresources:
status: {}
conversion:
strategy: Webhook
webhook:
conversionReviewVersions:
- v1
clientConfig:
service:
name: kubernetes
namespace: default
path: /convert
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.5

37
docs/api/generator/sts.md Normal file
View file

@ -0,0 +1,37 @@
STSSessionToken uses the GetSessionToken API to retrieve a temporary session token.
## Output Keys and Values
| Key | Description |
|-------------------|-------------------------------------------------------------------------------------|
| access_key_id | The access key ID that identifies the temporary security credentials. |
| secret_access_key | The secret access key that can be used to sign requests. |
| session_token | The token that users must pass to the service API to use the temporary credentials. |
| expiration | The date on which the current credentials expire. |
## Authentication
You can choose from three authentication mechanisms:
* static credentials using `spec.auth.secretRef`
* point to a IRSA Service Account with `spec.auth.jwt`
* use credentials from the [SDK default credentials chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default) from the controller environment
## Request Parameters
Following request parameters can be provided:
- duration seconds -> can specify the TTL of the generated token
- serial number -> define the serial number of the MFA device used by the user
- token code -> possible code generated by the above referenced MFA device
## Example Manifest
```yaml
{% include 'generator-sts.yaml' %}
```
Example `ExternalSecret` that references the STS Session Token generator:
```yaml
{% include 'generator-sts-example.yaml' %}
```

View file

@ -0,0 +1,14 @@
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: "sts-secret"
spec:
refreshInterval: "1h"
target:
name: sts-secret
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: STSSessionToken
name: "sts-gen"

View file

@ -0,0 +1,40 @@
apiVersion: generators.external-secrets.io/v1alpha1
kind: STSSessionToken
metadata:
name: sts-gen
spec:
# specify aws region (mandatory)
region: eu-west-1
# assume role with the given authentication credentials
role: "my-role"
# choose an authentication strategy
# if no auth strategy is defined it falls back to using
# credentials from the environment of the controller.
auth:
# 1: static credentials
# point to a secret that contains static credentials
# like AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
secretRef:
accessKeyIDSecretRef:
name: "my-aws-creds"
key: "key-id"
secretAccessKeySecretRef:
name: "my-aws-creds"
key: "access-secret"
# option 2: IAM Roles for Service Accounts
# point to a service account that should be used
# that is configured for IAM Roles for Service Accounts (IRSA)
jwt:
serviceAccountRef:
name: "oci-token-sync"
# optional request parameters for further fine-tuning the Token generation.
requestParameters:
serialNumber: arn:aws:iam::123456789012:mfa/user
sessionDuration: 900
tokenCode: "123456"

View file

@ -17,7 +17,7 @@ theme:
media: "(prefers-color-scheme: dark)"
toggle:
icon: material/brightness-4
name: Switch to light mode
name: Switch to light mode
features:
- navigation.tabs
- navigation.indexes
@ -68,6 +68,7 @@ nav:
- "api/generator/index.md"
- Azure Container Registry: api/generator/acr.md
- AWS Elastic Container Registry: api/generator/ecr.md
- AWS STS Session Token: api/generator/sts.md
- Google Container Registry: api/generator/gcr.md
- Vault Dynamic Secret: api/generator/vault.md
- Password: api/generator/password.md

114
pkg/generator/sts/sts.go Normal file
View file

@ -0,0 +1,114 @@
/*
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 sts
import (
"context"
"errors"
"fmt"
"strconv"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/aws-sdk-go/service/sts/stsiface"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
awsauth "github.com/external-secrets/external-secrets/pkg/provider/aws/auth"
)
type Generator struct{}
const (
errNoSpec = "no config spec provided"
errParseSpec = "unable to parse spec: %w"
errCreateSess = "unable to create aws session: %w"
errGetToken = "unable to get authorization token: %w"
)
func (g *Generator) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kube client.Client, namespace string) (map[string][]byte, error) {
return g.generate(ctx, jsonSpec, kube, namespace, stsFactory)
}
func (g *Generator) generate(
ctx context.Context,
jsonSpec *apiextensions.JSON,
kube client.Client,
namespace string,
stsFunc stsFactoryFunc,
) (map[string][]byte, error) {
if jsonSpec == nil {
return nil, errors.New(errNoSpec)
}
res, err := parseSpec(jsonSpec.Raw)
if err != nil {
return nil, fmt.Errorf(errParseSpec, err)
}
sess, err := awsauth.NewGeneratorSession(
ctx,
esv1beta1.AWSAuth{
SecretRef: (*esv1beta1.AWSAuthSecretRef)(res.Spec.Auth.SecretRef),
JWTAuth: (*esv1beta1.AWSJWTAuth)(res.Spec.Auth.JWTAuth),
},
res.Spec.Role,
res.Spec.Region,
kube,
namespace,
awsauth.DefaultSTSProvider,
awsauth.DefaultJWTProvider)
if err != nil {
return nil, fmt.Errorf(errCreateSess, err)
}
client := stsFunc(sess)
input := &sts.GetSessionTokenInput{}
if res.Spec.RequestParameters != nil {
input.DurationSeconds = res.Spec.RequestParameters.SessionDuration
input.TokenCode = res.Spec.RequestParameters.TokenCode
input.SerialNumber = res.Spec.RequestParameters.SerialNumber
}
out, err := client.GetSessionToken(input)
if err != nil {
return nil, fmt.Errorf(errGetToken, err)
}
if out.Credentials == nil {
return nil, errors.New("no credentials found")
}
return map[string][]byte{
"access_key_id": []byte(*out.Credentials.AccessKeyId),
"expiration": []byte(strconv.FormatInt(out.Credentials.Expiration.Unix(), 10)),
"secret_access_key": []byte(*out.Credentials.SecretAccessKey),
"session_token": []byte(*out.Credentials.SessionToken),
}, nil
}
type stsFactoryFunc func(aws *session.Session) stsiface.STSAPI
func stsFactory(aws *session.Session) stsiface.STSAPI {
return sts.New(aws)
}
func parseSpec(data []byte) (*genv1alpha1.STSSessionToken, error) {
var spec genv1alpha1.STSSessionToken
err := yaml.Unmarshal(data, &spec)
return &spec, err
}
func init() {
genv1alpha1.Register(genv1alpha1.STSSessionTokenGroupKind, &Generator{})
}

View file

@ -0,0 +1,151 @@
/*
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 sts
import (
"context"
"errors"
"reflect"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/aws-sdk-go/service/sts/stsiface"
v1 "k8s.io/api/core/v1"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/external-secrets/external-secrets/pkg/utils"
)
func TestGenerate(t *testing.T) {
type args struct {
ctx context.Context
jsonSpec *apiextensions.JSON
kube client.Client
namespace string
tokenFunc func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error)
}
tests := []struct {
name string
g *Generator
args args
want map[string][]byte
wantErr bool
}{
{
name: "nil spec",
args: args{
jsonSpec: nil,
},
wantErr: true,
},
{
name: "invalid json",
args: args{
tokenFunc: func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) {
return nil, errors.New("boom")
},
jsonSpec: &apiextensions.JSON{
Raw: []byte(``),
},
},
wantErr: true,
},
{
name: "full spec",
args: args{
namespace: "foobar",
kube: clientfake.NewClientBuilder().WithObjects(&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-aws-creds",
Namespace: "foobar",
},
Data: map[string][]byte{
"key-id": []byte("foo"),
"access-secret": []byte("bar"),
},
}).Build(),
tokenFunc: func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) {
t := time.Unix(1234, 0)
return &sts.GetSessionTokenOutput{
Credentials: &sts.Credentials{
AccessKeyId: utils.Ptr("access-key-id"),
Expiration: utils.Ptr(t),
SecretAccessKey: utils.Ptr("secret-access-key"),
SessionToken: utils.Ptr("session-token"),
},
}, nil
},
jsonSpec: &apiextensions.JSON{
Raw: []byte(`apiVersion: generators.external-secrets.io/v1alpha1
kind: STSSessionToken
spec:
region: eu-west-1
role: "my-role"
auth:
secretRef:
accessKeyIDSecretRef:
name: "my-aws-creds"
key: "key-id"
secretAccessKeySecretRef:
name: "my-aws-creds"
key: "access-secret"`),
},
},
want: map[string][]byte{
"access_key_id": []byte("access-key-id"),
"expiration": []byte("1234"),
"secret_access_key": []byte("secret-access-key"),
"session_token": []byte("session-token"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := &Generator{}
got, err := g.generate(
tt.args.ctx,
tt.args.jsonSpec,
tt.args.kube,
tt.args.namespace,
func(aws *session.Session) stsiface.STSAPI {
return &FakeSTS{
getSessionToken: tt.args.tokenFunc,
}
},
)
if (err != nil) != tt.wantErr {
t.Errorf("Generator.Generate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Generator.Generate() = %v, want %v", got, tt.want)
}
})
}
}
type FakeSTS struct {
stsiface.STSAPI
getSessionToken func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error)
}
func (e *FakeSTS) GetSessionToken(in *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) {
return e.getSessionToken(in)
}

View file

@ -155,7 +155,7 @@ func New(ctx context.Context, store esv1beta1.GenericStore, kube client.Client,
return sess, nil
}
// NewSession creates a new aws session based on the provided store
// NewGeneratorSession creates a new aws session based on the provided store
// it uses the following authentication mechanisms in order:
// * service-account token authentication via AssumeRoleWithWebIdentity
// * static credentials from a Kind=Secret, optionally with doing a AssumeRole.