Skip to content

Commit

Permalink
Add LDAP provider for Bucket STS API
Browse files Browse the repository at this point in the history
Signed-off-by: Matheus Pimenta <[email protected]>
  • Loading branch information
matheuscscp committed Aug 15, 2024
1 parent 7c4fdd5 commit 5fb914a
Show file tree
Hide file tree
Showing 10 changed files with 519 additions and 73 deletions.
16 changes: 13 additions & 3 deletions api/v1beta2/bucket_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ const (

// BucketSpec specifies the required configuration to produce an Artifact for
// an object storage bucket.
// +kubebuilder:validation:XValidation:rule="self.provider == 'aws' || !has(self.sts)", message="STS configuration is only supported for the 'aws' Bucket provider"
// +kubebuilder:validation:XValidation:rule="self.provider == 'aws' || self.provider == 'generic' || !has(self.sts)", message="STS configuration is only supported for the 'aws' and 'generic' Bucket providers"
// +kubebuilder:validation:XValidation:rule="self.provider != 'aws' || !has(self.sts) || self.sts.provider == 'aws'", message="'aws' is the only supported STS provider for the 'aws' Bucket provider"
// +kubebuilder:validation:XValidation:rule="self.provider != 'generic' || !has(self.sts) || self.sts.provider == 'ldap'", message="'ldap' is the only supported STS provider for the 'generic' Bucket provider"
// +kubebuilder:validation:XValidation:rule="!has(self.sts) || self.sts.provider != 'aws' || !has(self.sts.secretRef)", message="spec.sts.secretRef is not required for the 'aws' STS provider"
type BucketSpec struct {
// Provider of the object storage bucket.
// Defaults to 'generic', which expects an S3 (API) compatible object
Expand All @@ -72,7 +74,7 @@ type BucketSpec struct {
// Service for fetching temporary credentials to authenticate in a
// Bucket provider.
//
// This field is only supported for the `aws` provider.
// This field is only supported for the `aws` and `generic` providers.
// +optional
STS *BucketSTSSpec `json:"sts,omitempty"`

Expand Down Expand Up @@ -153,7 +155,7 @@ type BucketSpec struct {
// provider.
type BucketSTSSpec struct {
// Provider of the Security Token Service.
// +kubebuilder:validation:Enum=aws
// +kubebuilder:validation:Enum=aws;ldap
// +required
Provider string `json:"provider"`

Expand All @@ -162,6 +164,14 @@ type BucketSTSSpec struct {
// +required
// +kubebuilder:validation:Pattern="^(http|https)://.*$"
Endpoint string `json:"endpoint"`

// SecretRef specifies the Secret containing authentication credentials
// for the STS endpoint.
//
// Required for the `ldap` provider, in which case it should contain
// the fields `username` and `password`.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
}

// BucketStatus records the observed state of a Bucket.
Expand Down
3 changes: 3 additions & 0 deletions api/v1beta2/sts_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ const (
// STSProviderAmazon represents the AWS provider for Security Token Service.
// Provides support for fetching temporary credentials from an AWS STS endpoint.
STSProviderAmazon string = "aws"
// STSProviderLDAP represents the LDAP provider for Security Token Service.
// Provides support for fetching temporary credentials from an LDAP endpoint.
STSProviderLDAP string = "ldap"
)
7 changes: 6 additions & 1 deletion api/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 26 additions & 3 deletions config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ spec:
Bucket provider.
This field is only supported for the `aws` provider.
This field is only supported for the `aws` and `generic` providers.
properties:
endpoint:
description: |-
Expand All @@ -436,7 +436,23 @@ spec:
description: Provider of the Security Token Service.
enum:
- aws
- ldap
type: string
secretRef:
description: |-
SecretRef specifies the Secret containing authentication credentials
for the STS endpoint.
Required for the `ldap` provider, in which case it should contain
the fields `username` and `password`.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
required:
- endpoint
- provider
Expand All @@ -457,12 +473,19 @@ spec:
- interval
type: object
x-kubernetes-validations:
- message: STS configuration is only supported for the 'aws' Bucket provider
rule: self.provider == 'aws' || !has(self.sts)
- message: STS configuration is only supported for the 'aws' and 'generic'
Bucket providers
rule: self.provider == 'aws' || self.provider == 'generic' || !has(self.sts)
- message: '''aws'' is the only supported STS provider for the ''aws''
Bucket provider'
rule: self.provider != 'aws' || !has(self.sts) || self.sts.provider
== 'aws'
- message: '''ldap'' is the only supported STS provider for the ''generic''
Bucket provider'
rule: self.provider != 'generic' || !has(self.sts) || self.sts.provider
== 'ldap'
- message: spec.sts.secretRef is not required for the 'aws' STS provider
rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)'
status:
default:
observedGeneration: -1
Expand Down
21 changes: 19 additions & 2 deletions docs/api/v1beta2/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ BucketSTSSpec
<p>STS specifies the required configuration to use a Security Token
Service for fetching temporary credentials to authenticate in a
Bucket provider.</p>
<p>This field is only supported for the <code>aws</code> provider.</p>
<p>This field is only supported for the <code>aws</code> and <code>generic</code> providers.</p>
</td>
</tr>
<tr>
Expand Down Expand Up @@ -1497,6 +1497,23 @@ string
where temporary credentials will be fetched.</p>
</td>
</tr>
<tr>
<td>
<code>secretRef</code><br>
<em>
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>SecretRef specifies the Secret containing authentication credentials
for the STS endpoint.</p>
<p>Required for the <code>ldap</code> provider, in which case it should contain
the fields <code>username</code> and <code>password</code>.</p>
</td>
</tr>
</tbody>
</table>
</div>
Expand Down Expand Up @@ -1569,7 +1586,7 @@ BucketSTSSpec
<p>STS specifies the required configuration to use a Security Token
Service for fetching temporary credentials to authenticate in a
Bucket provider.</p>
<p>This field is only supported for the <code>aws</code> provider.</p>
<p>This field is only supported for the <code>aws</code> and <code>generic</code> providers.</p>
</td>
</tr>
<tr>
Expand Down
17 changes: 13 additions & 4 deletions docs/spec/v1beta2/buckets.md
Original file line number Diff line number Diff line change
Expand Up @@ -756,15 +756,24 @@ configuration. A Security Token Service (STS) is a web service that issues
temporary security credentials. By adding this field, one may specify the
STS endpoint from where temporary credentials will be fetched.

This field is only supported for the `aws` and `generic` bucket [providers](#provider).

If using `.spec.sts`, the following fields are required:

- `.spec.sts.provider`, the Security Token Service provider. The only supported
option is `aws`.
option for the `aws` bucket provider is `aws`. The only supported option for
the `generic` bucket provider is `ldap`.
- `.spec.sts.endpoint`, the HTTP/S endpoint of the Security Token Service. In
the case of AWS, this can be `https://sts.amazonaws.com`, or a Regional STS
Endpoint, or an Interface Endpoint created inside a VPC.
the case of `aws` this can be `https://sts.amazonaws.com`, or a Regional STS
Endpoint, or an Interface Endpoint created inside a VPC. In the case of
`ldap` this must be the LDAP server endpoint.

When using the `ldap` provider, the following fields are also required:

This field is only supported for the `aws` bucket provider.
- `.spec.sts.secretRef.name`, the name of the Secret containing the LDAP
credentials. The Secret must contain the following keys:
- `username`, the username to authenticate with.
- `password`, the password to authenticate with.

### Bucket name

Expand Down
40 changes: 33 additions & 7 deletions internal/controller/bucket_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,6 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
if err != nil {
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
// Return error as the world as observed may change
return sreconcile.ResultEmpty, e
}
proxyURL, err := r.getProxyURL(ctx, obj)
Expand Down Expand Up @@ -483,6 +482,18 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}
tlsConfig, err := r.getTLSConfig(ctx, obj)
if err != nil {
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}
stsSecret, err := r.getSTSSecret(ctx, obj)
if err != nil {
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}
if sts := obj.Spec.STS; sts != nil {
if err := minio.ValidateSTSProvider(obj.Spec.Provider, sts.Provider); err != nil {
e := serror.NewStalling(err, sourcev1.InvalidSTSConfigurationReason)
Expand All @@ -495,12 +506,11 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}
}
tlsConfig, err := r.getTLSConfig(ctx, obj)
if err != nil {
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
if err := minio.ValidateSTSSecret(sts.Provider, stsSecret); err != nil {
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}
}
var opts []minio.Option
if secret != nil {
Expand All @@ -512,6 +522,9 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
if proxyURL != nil {
opts = append(opts, minio.WithProxyURL(proxyURL))
}
if stsSecret != nil {
opts = append(opts, minio.WithSTSSecret(stsSecret))
}
if provider, err = minio.NewClient(obj, opts...); err != nil {
e := serror.NewGeneric(err, "ClientError")
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
Expand Down Expand Up @@ -732,6 +745,8 @@ func (r *BucketReconciler) getSecret(ctx context.Context, secretRef *meta.LocalO
return secret, nil
}

// getTLSConfig attempts to fetch a TLS configuration from the object's
// certificate secret reference.
func (r *BucketReconciler) getTLSConfig(ctx context.Context, obj *bucketv1.Bucket) (*stdtls.Config, error) {
certSecret, err := r.getSecret(ctx, obj.Spec.CertSecretRef, obj.GetNamespace())
if err != nil || certSecret == nil {
Expand All @@ -747,6 +762,8 @@ func (r *BucketReconciler) getTLSConfig(ctx context.Context, obj *bucketv1.Bucke
return tlsConfig, nil
}

// getProxyURL attempts to fetch a proxy URL from the object's proxy secret
// reference.
func (r *BucketReconciler) getProxyURL(ctx context.Context, obj *bucketv1.Bucket) (*url.URL, error) {
namespace := obj.GetNamespace()
proxySecret, err := r.getSecret(ctx, obj.Spec.ProxySecretRef, namespace)
Expand All @@ -771,6 +788,15 @@ func (r *BucketReconciler) getProxyURL(ctx context.Context, obj *bucketv1.Bucket
return proxyURL, nil
}

// getSTSSecret attempts to fetch the secret from the object's STS secret
// reference.
func (r *BucketReconciler) getSTSSecret(ctx context.Context, obj *bucketv1.Bucket) (*corev1.Secret, error) {
if obj.Spec.STS == nil {
return nil, nil
}
return r.getSecret(ctx, obj.Spec.STS.SecretRef, obj.GetNamespace())
}

// eventLogf records events, and logs at the same time.
//
// This log is different from the debug log in the EventRecorder, in the sense
Expand Down
Loading

0 comments on commit 5fb914a

Please sign in to comment.