Skip to content

Commit

Permalink
THREESCALE-8772 - AWS secret format with STS
Browse files Browse the repository at this point in the history
  • Loading branch information
valerymo committed Nov 14, 2022
1 parent 6bfa8e4 commit e467851
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 69 deletions.
1 change: 1 addition & 0 deletions apis/apps/v1alpha1/apimanager_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ type DeprecatedSystemS3Spec struct {

type SystemS3Spec struct {
ConfigurationSecretRef v1.LocalObjectReference `json:"configurationSecretRef"`
S3STSEnabled bool `json:"s3StsEnabledSts,omitempty"`
}

type SystemDatabaseSpec struct {
Expand Down
2 changes: 2 additions & 0 deletions bundle/manifests/apps.3scale.net_apimanagers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5154,6 +5154,8 @@ spec:
type: string
type: object
x-kubernetes-map-type: atomic
s3StsEnabledSts:
type: boolean
required:
- configurationSecretRef
type: object
Expand Down
2 changes: 2 additions & 0 deletions config/crd/bases/apps.3scale.net_apimanagers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9457,6 +9457,8 @@ spec:
type: string
type: object
x-kubernetes-map-type: atomic
s3StsEnabledSts:
type: boolean
required:
- configurationSecretRef
type: object
Expand Down
49 changes: 0 additions & 49 deletions pkg/3scale/amp/component/s3.go

This file was deleted.

30 changes: 22 additions & 8 deletions pkg/3scale/amp/component/s3_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,29 @@ package component

import "github.com/go-playground/validator/v10"

const (
AwsAccessKeyID = "AWS_ACCESS_KEY_ID"
AwsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
AwsBucket = "AWS_BUCKET"
AwsRegion = "AWS_REGION"
AwsProtocol = "AWS_PROTOCOL"
AwsHostname = "AWS_HOSTNAME"
AwsPathStyle = "AWS_PATH_STYLE"
AwsRoleArn = "AWS_ROLE_ARN"
AwsWebIdentityTokenFile = "AWS_WEB_IDENTITY_TOKEN_FILE"
)

type S3Options struct {
AwsAccessKeyId string `validate:"required"`
AwsSecretAccessKey string `validate:"required"`
AwsRegion string `validate:"required"`
AwsBucket string `validate:"required"`
AwsProtocol string `validate:"required"`
AwsHostname string `validate:"required"`
AwsPathStyle string `validate:"required"`
AwsCredentialsSecret string `validate:"required"`
AwsAccessKeyId string `validate:"required"`
AwsSecretAccessKey string `validate:"required"`
AwsRoleArn string `validate:"required"`
AwsWebIdentityTokenFile string `validate:"required"`
AwsRegion string `validate:"required"`
AwsBucket string `validate:"required"`
AwsProtocol string `validate:"required"`
AwsHostname string `validate:"required"`
AwsPathStyle string `validate:"required"`
AwsCredentialsSecret string `validate:"required"`
}

func NewS3Options() *S3Options {
Expand Down
81 changes: 79 additions & 2 deletions pkg/3scale/amp/component/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ const (
SystemAppDeveloperContainerMetricsPortName = "dev-metrics"
)

const (
S3StsCredentialsSecretName = "s3-credentials"
S3StsCredentialsProjectedVolumeMountPath = "/var/run/secrets/openshift/serviceaccount/token"
)

type System struct {
Options *SystemOptions
}
Expand Down Expand Up @@ -240,14 +245,24 @@ func (system *System) buildSystemBaseEnv() []v1.EnvVar {
if system.Options.S3FileStorageOptions != nil {
result = append(result,
helper.EnvVarFromConfigMap("FILE_UPLOAD_STORAGE", "system-environment", "FILE_UPLOAD_STORAGE"),
helper.EnvVarFromSecret(AwsAccessKeyID, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsAccessKeyID),
helper.EnvVarFromSecret(AwsSecretAccessKey, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsSecretAccessKey),
helper.EnvVarFromSecret(AwsBucket, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsBucket),
helper.EnvVarFromSecret(AwsRegion, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsRegion),
helper.EnvVarFromSecretOptional(AwsProtocol, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsProtocol),
helper.EnvVarFromSecretOptional(AwsHostname, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsHostname),
helper.EnvVarFromSecretOptional(AwsPathStyle, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsPathStyle),
)

if system.Options.S3FileStorageOptions != nil && system.Options.S3FileStorageOptions.S3STSEnabled {
result = append(result,
helper.EnvVarFromSecret(AwsRoleArn, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsRoleArn),
helper.EnvVarFromSecret(AwsWebIdentityTokenFile, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsWebIdentityTokenFile),
)
} else {
result = append(result,
helper.EnvVarFromSecret(AwsAccessKeyID, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsAccessKeyID),
helper.EnvVarFromSecret(AwsSecretAccessKey, system.Options.S3FileStorageOptions.ConfigurationSecretName, AwsSecretAccessKey),
)
}
}

return result
Expand Down Expand Up @@ -506,6 +521,27 @@ func (system *System) appPodVolumes() []v1.Volume {
}

res = append(res, systemConfigVolume)

if system.Options.S3FileStorageOptions != nil && system.Options.S3FileStorageOptions.S3STSEnabled {
s3CredsProjectedVolume := v1.Volume{
Name: S3StsCredentialsSecretName,
VolumeSource: v1.VolumeSource{
Projected: &v1.ProjectedVolumeSource{
Sources: []v1.VolumeProjection{
v1.VolumeProjection{
ServiceAccountToken: &v1.ServiceAccountTokenProjection{
Audience: S3StsCredentialsProjectedVolumeMountPath,
ExpirationSeconds: &[]int64{3600}[0],
Path: "token",
},
},
},
},
},
}
res = append(res, s3CredsProjectedVolume)
}

return res
}

Expand All @@ -514,6 +550,9 @@ func (system *System) volumeNamesForSystemAppPreHookPod() []string {
if system.Options.PvcFileStorageOptions != nil {
res = append(res, SystemFileStoragePVCName)
}
if system.Options.S3FileStorageOptions != nil && system.Options.S3FileStorageOptions.S3STSEnabled {
res = append(res, S3StsCredentialsSecretName)
}
return res
}

Expand Down Expand Up @@ -772,6 +811,26 @@ func (system *System) SidekiqPodVolumes() []v1.Volume {
}

res = append(res, systemConfigVolume)

if system.Options.S3FileStorageOptions != nil && system.Options.S3FileStorageOptions.S3STSEnabled {
s3CredsProjectedVolume := v1.Volume{
Name: S3StsCredentialsSecretName,
VolumeSource: v1.VolumeSource{
Projected: &v1.ProjectedVolumeSource{
Sources: []v1.VolumeProjection{
v1.VolumeProjection{
ServiceAccountToken: &v1.ServiceAccountTokenProjection{
Audience: S3StsCredentialsProjectedVolumeMountPath,
ExpirationSeconds: &[]int64{3600}[0],
Path: "token",
},
},
},
},
},
}
res = append(res, s3CredsProjectedVolume)
}
return res
}

Expand Down Expand Up @@ -869,11 +928,24 @@ func (system *System) systemConfigVolumeMount() v1.VolumeMount {
}
}

func (system *System) s3CredsProjectedVolumeMount() v1.VolumeMount {
return v1.VolumeMount{
Name: S3StsCredentialsSecretName,
ReadOnly: true,
MountPath: S3StsCredentialsProjectedVolumeMountPath,
}
}

func (system *System) appCommonContainerVolumeMounts(systemStorageReadonly bool) []v1.VolumeMount {
res := []v1.VolumeMount{}
if system.Options.PvcFileStorageOptions != nil {
res = append(res, system.systemStorageVolumeMount(systemStorageReadonly))
}

if system.Options.S3FileStorageOptions != nil && system.Options.S3FileStorageOptions.S3STSEnabled {
res = append(res, system.s3CredsProjectedVolumeMount())
}

res = append(res, system.systemConfigVolumeMount())

return res
Expand Down Expand Up @@ -905,6 +977,11 @@ func (system *System) sidekiqContainerVolumeMounts() []v1.VolumeMount {
}
res = append(res, systemTmpVolumeMount)
res = append(res, system.systemConfigVolumeMount())

if system.Options.S3FileStorageOptions != nil && system.Options.S3FileStorageOptions.S3STSEnabled {
res = append(res, system.s3CredsProjectedVolumeMount())
}

return res
}

Expand Down
1 change: 1 addition & 0 deletions pkg/3scale/amp/component/system_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

type S3FileStorageOptions struct {
ConfigurationSecretName string `validate:"required"`
S3STSEnabled bool
}

type SystemSMTPSecretOptions struct {
Expand Down
29 changes: 29 additions & 0 deletions pkg/3scale/amp/operator/system_options_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,9 @@ func (s *SystemOptionsProvider) setFileStorageOptions() {
s.apimanager.Spec.System.FileStorageSpec.S3 != nil {
s.options.S3FileStorageOptions = &component.S3FileStorageOptions{
ConfigurationSecretName: s.apimanager.Spec.System.FileStorageSpec.S3.ConfigurationSecretRef.Name,
S3STSEnabled: s.apimanager.Spec.System.FileStorageSpec.S3.S3STSEnabled,
}
s.options.S3FileStorageOptions.S3STSEnabled, _ = s.isSTS()
} else {
// default to PVC
var storageClassName *string
Expand Down Expand Up @@ -559,3 +561,30 @@ func (s *SystemOptionsProvider) sphinxPodTemplateLabels() map[string]string {

return labels
}

func (s *SystemOptionsProvider) isSTS() (bool, error) {
if s.apimanager.Spec.System == nil ||
s.apimanager.Spec.System.FileStorageSpec == nil ||
s.apimanager.Spec.System.FileStorageSpec.S3 == nil {
return false, nil
}
awsCredentialsSecretName := s.apimanager.Spec.System.FileStorageSpec.S3.ConfigurationSecretRef.Name
if awsCredentialsSecretName == "" {
return false, fmt.Errorf("no aws credentials provided")
}
awsSecret, err := helper.GetSecret(awsCredentialsSecretName, s.apimanager.Namespace, s.client)
if err != nil {
return false, err
}
secretData := awsSecret.Data
var result *string
result = helper.GetSecretDataValue(secretData, component.AwsRoleArn)
if result == nil {
return false, nil
}
result = helper.GetSecretDataValue(secretData, component.AwsWebIdentityTokenFile)
if result == nil {
return false, nil
}
return true, nil
}
30 changes: 20 additions & 10 deletions pkg/3scale/amp/operator/system_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func NewSystemReconciler(baseAPIManagerLogicReconciler *BaseAPIManagerLogicRecon
func (r *SystemReconciler) reconcileFileStorage(system *component.System) error {
if r.apiManager.Spec.System.FileStorageSpec != nil {
if r.apiManager.Spec.System.FileStorageSpec.S3 != nil {
return r.validateS3StorageProvidedConfiguration()
return r.validateS3StorageProvidedConfiguration(system)
}
if r.apiManager.Spec.System.FileStorageSpec.DeprecatedS3 != nil {
r.Logger().Info("Warning: deprecated amazonSimpleStorageService field in CR being used. Ignoring it... Please use simpleStorageService")
Expand Down Expand Up @@ -231,7 +231,7 @@ func (r *SystemReconciler) Reconcile() (reconcile.Result, error) {
return reconcile.Result{}, nil
}

func (r *SystemReconciler) validateS3StorageProvidedConfiguration() error {
func (r *SystemReconciler) validateS3StorageProvidedConfiguration(system *component.System) error {
// Nothing for reconcile.
// Check all required fields exist
awsCredentialsSecretName := r.apiManager.Spec.System.FileStorageSpec.S3.ConfigurationSecretRef.Name
Expand All @@ -246,14 +246,24 @@ func (r *SystemReconciler) validateS3StorageProvidedConfiguration() error {

secretData := awsSecret.Data
var result *string
result = helper.GetSecretDataValue(secretData, component.AwsAccessKeyID)
if result == nil {
return fmt.Errorf("Secret field '%s' is required in secret '%s'", component.AwsAccessKeyID, awsCredentialsSecretName)
}

result = helper.GetSecretDataValue(secretData, component.AwsSecretAccessKey)
if result == nil {
return fmt.Errorf("Secret field '%s' is required in secret '%s'", component.AwsSecretAccessKey, awsCredentialsSecretName)
if system.Options.S3FileStorageOptions != nil && system.Options.S3FileStorageOptions.S3STSEnabled {
result = helper.GetSecretDataValue(secretData, component.AwsRoleArn)
if result == nil {
return fmt.Errorf("Secret field '%s' is required in secret '%s'", component.AwsRoleArn, awsCredentialsSecretName)
}
result = helper.GetSecretDataValue(secretData, component.AwsWebIdentityTokenFile)
if result == nil {
return fmt.Errorf("Secret field '%s' is required in secret '%s'", component.AwsWebIdentityTokenFile, awsCredentialsSecretName)
}
} else {
result = helper.GetSecretDataValue(secretData, component.AwsAccessKeyID)
if result == nil {
return fmt.Errorf("Secret field '%s' is required in secret '%s'", component.AwsAccessKeyID, awsCredentialsSecretName)
}
result = helper.GetSecretDataValue(secretData, component.AwsSecretAccessKey)
if result == nil {
return fmt.Errorf("Secret field '%s' is required in secret '%s'", component.AwsSecretAccessKey, awsCredentialsSecretName)
}
}

result = helper.GetSecretDataValue(secretData, component.AwsBucket)
Expand Down

0 comments on commit e467851

Please sign in to comment.