Merge pull request #1585 from fluxcd/bucket-sts-endpoint-ldap
Add LDAP provider for Bucket STS API
This commit is contained in:
commit
74e82d2467
|
@ -49,8 +49,11 @@ const (
|
||||||
|
|
||||||
// BucketSpec specifies the required configuration to produce an Artifact for
|
// BucketSpec specifies the required configuration to produce an Artifact for
|
||||||
// an object storage bucket.
|
// 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 != '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"
|
||||||
|
// +kubebuilder:validation:XValidation:rule="!has(self.sts) || self.sts.provider != 'aws' || !has(self.sts.certSecretRef)", message="spec.sts.certSecretRef is not required for the 'aws' STS provider"
|
||||||
type BucketSpec struct {
|
type BucketSpec struct {
|
||||||
// Provider of the object storage bucket.
|
// Provider of the object storage bucket.
|
||||||
// Defaults to 'generic', which expects an S3 (API) compatible object
|
// Defaults to 'generic', which expects an S3 (API) compatible object
|
||||||
|
@ -72,7 +75,7 @@ type BucketSpec struct {
|
||||||
// Service for fetching temporary credentials to authenticate in a
|
// Service for fetching temporary credentials to authenticate in a
|
||||||
// Bucket provider.
|
// Bucket provider.
|
||||||
//
|
//
|
||||||
// This field is only supported for the `aws` provider.
|
// This field is only supported for the `aws` and `generic` providers.
|
||||||
// +optional
|
// +optional
|
||||||
STS *BucketSTSSpec `json:"sts,omitempty"`
|
STS *BucketSTSSpec `json:"sts,omitempty"`
|
||||||
|
|
||||||
|
@ -153,7 +156,7 @@ type BucketSpec struct {
|
||||||
// provider.
|
// provider.
|
||||||
type BucketSTSSpec struct {
|
type BucketSTSSpec struct {
|
||||||
// Provider of the Security Token Service.
|
// Provider of the Security Token Service.
|
||||||
// +kubebuilder:validation:Enum=aws
|
// +kubebuilder:validation:Enum=aws;ldap
|
||||||
// +required
|
// +required
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
|
|
||||||
|
@ -162,6 +165,29 @@ type BucketSTSSpec struct {
|
||||||
// +required
|
// +required
|
||||||
// +kubebuilder:validation:Pattern="^(http|https)://.*$"
|
// +kubebuilder:validation:Pattern="^(http|https)://.*$"
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
|
|
||||||
|
// SecretRef specifies the Secret containing authentication credentials
|
||||||
|
// for the STS endpoint. This Secret must contain the fields `username`
|
||||||
|
// and `password` and is supported only for the `ldap` provider.
|
||||||
|
// +optional
|
||||||
|
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
|
||||||
|
|
||||||
|
// CertSecretRef can be given the name of a Secret containing
|
||||||
|
// either or both of
|
||||||
|
//
|
||||||
|
// - a PEM-encoded client certificate (`tls.crt`) and private
|
||||||
|
// key (`tls.key`);
|
||||||
|
// - a PEM-encoded CA certificate (`ca.crt`)
|
||||||
|
//
|
||||||
|
// and whichever are supplied, will be used for connecting to the
|
||||||
|
// STS endpoint. The client cert and key are useful if you are
|
||||||
|
// authenticating with a certificate; the CA cert is useful if
|
||||||
|
// you are using a self-signed server certificate. The Secret must
|
||||||
|
// be of type `Opaque` or `kubernetes.io/tls`.
|
||||||
|
//
|
||||||
|
// This field is only supported for the `ldap` provider.
|
||||||
|
// +optional
|
||||||
|
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BucketStatus records the observed state of a Bucket.
|
// BucketStatus records the observed state of a Bucket.
|
||||||
|
|
|
@ -20,4 +20,7 @@ const (
|
||||||
// STSProviderAmazon represents the AWS provider for Security Token Service.
|
// STSProviderAmazon represents the AWS provider for Security Token Service.
|
||||||
// Provides support for fetching temporary credentials from an AWS STS endpoint.
|
// Provides support for fetching temporary credentials from an AWS STS endpoint.
|
||||||
STSProviderAmazon string = "aws"
|
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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -118,6 +118,16 @@ func (in *BucketList) DeepCopyObject() runtime.Object {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *BucketSTSSpec) DeepCopyInto(out *BucketSTSSpec) {
|
func (in *BucketSTSSpec) DeepCopyInto(out *BucketSTSSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.SecretRef != nil {
|
||||||
|
in, out := &in.SecretRef, &out.SecretRef
|
||||||
|
*out = new(meta.LocalObjectReference)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.CertSecretRef != nil {
|
||||||
|
in, out := &in.CertSecretRef, &out.CertSecretRef
|
||||||
|
*out = new(meta.LocalObjectReference)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketSTSSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketSTSSpec.
|
||||||
|
@ -136,7 +146,7 @@ func (in *BucketSpec) DeepCopyInto(out *BucketSpec) {
|
||||||
if in.STS != nil {
|
if in.STS != nil {
|
||||||
in, out := &in.STS, &out.STS
|
in, out := &in.STS, &out.STS
|
||||||
*out = new(BucketSTSSpec)
|
*out = new(BucketSTSSpec)
|
||||||
**out = **in
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
if in.SecretRef != nil {
|
if in.SecretRef != nil {
|
||||||
in, out := &in.SecretRef, &out.SecretRef
|
in, out := &in.SecretRef, &out.SecretRef
|
||||||
|
|
|
@ -424,8 +424,34 @@ spec:
|
||||||
Bucket provider.
|
Bucket provider.
|
||||||
|
|
||||||
|
|
||||||
This field is only supported for the `aws` provider.
|
This field is only supported for the `aws` and `generic` providers.
|
||||||
properties:
|
properties:
|
||||||
|
certSecretRef:
|
||||||
|
description: |-
|
||||||
|
CertSecretRef can be given the name of a Secret containing
|
||||||
|
either or both of
|
||||||
|
|
||||||
|
|
||||||
|
- a PEM-encoded client certificate (`tls.crt`) and private
|
||||||
|
key (`tls.key`);
|
||||||
|
- a PEM-encoded CA certificate (`ca.crt`)
|
||||||
|
|
||||||
|
|
||||||
|
and whichever are supplied, will be used for connecting to the
|
||||||
|
STS endpoint. The client cert and key are useful if you are
|
||||||
|
authenticating with a certificate; the CA cert is useful if
|
||||||
|
you are using a self-signed server certificate. The Secret must
|
||||||
|
be of type `Opaque` or `kubernetes.io/tls`.
|
||||||
|
|
||||||
|
|
||||||
|
This field is only supported for the `ldap` provider.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Name of the referent.
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
type: object
|
||||||
endpoint:
|
endpoint:
|
||||||
description: |-
|
description: |-
|
||||||
Endpoint is the HTTP/S endpoint of the Security Token Service from
|
Endpoint is the HTTP/S endpoint of the Security Token Service from
|
||||||
|
@ -436,7 +462,20 @@ spec:
|
||||||
description: Provider of the Security Token Service.
|
description: Provider of the Security Token Service.
|
||||||
enum:
|
enum:
|
||||||
- aws
|
- aws
|
||||||
|
- ldap
|
||||||
type: string
|
type: string
|
||||||
|
secretRef:
|
||||||
|
description: |-
|
||||||
|
SecretRef specifies the Secret containing authentication credentials
|
||||||
|
for the STS endpoint. This Secret must contain the fields `username`
|
||||||
|
and `password` and is supported only for the `ldap` provider.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Name of the referent.
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
type: object
|
||||||
required:
|
required:
|
||||||
- endpoint
|
- endpoint
|
||||||
- provider
|
- provider
|
||||||
|
@ -457,12 +496,21 @@ spec:
|
||||||
- interval
|
- interval
|
||||||
type: object
|
type: object
|
||||||
x-kubernetes-validations:
|
x-kubernetes-validations:
|
||||||
- message: STS configuration is only supported for the 'aws' Bucket provider
|
- message: STS configuration is only supported for the 'aws' and 'generic'
|
||||||
rule: self.provider == 'aws' || !has(self.sts)
|
Bucket providers
|
||||||
|
rule: self.provider == 'aws' || self.provider == 'generic' || !has(self.sts)
|
||||||
- message: '''aws'' is the only supported STS provider for the ''aws''
|
- message: '''aws'' is the only supported STS provider for the ''aws''
|
||||||
Bucket provider'
|
Bucket provider'
|
||||||
rule: self.provider != 'aws' || !has(self.sts) || self.sts.provider
|
rule: self.provider != 'aws' || !has(self.sts) || self.sts.provider
|
||||||
== 'aws'
|
== '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)'
|
||||||
|
- message: spec.sts.certSecretRef is not required for the 'aws' STS provider
|
||||||
|
rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)'
|
||||||
status:
|
status:
|
||||||
default:
|
default:
|
||||||
observedGeneration: -1
|
observedGeneration: -1
|
||||||
|
|
|
@ -126,7 +126,7 @@ BucketSTSSpec
|
||||||
<p>STS specifies the required configuration to use a Security Token
|
<p>STS specifies the required configuration to use a Security Token
|
||||||
Service for fetching temporary credentials to authenticate in a
|
Service for fetching temporary credentials to authenticate in a
|
||||||
Bucket provider.</p>
|
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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1497,6 +1497,48 @@ string
|
||||||
where temporary credentials will be fetched.</p>
|
where temporary credentials will be fetched.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</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. This Secret must contain the fields <code>username</code>
|
||||||
|
and <code>password</code> and is supported only for the <code>ldap</code> provider.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>certSecretRef</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>CertSecretRef can be given the name of a Secret containing
|
||||||
|
either or both of</p>
|
||||||
|
<ul>
|
||||||
|
<li>a PEM-encoded client certificate (<code>tls.crt</code>) and private
|
||||||
|
key (<code>tls.key</code>);</li>
|
||||||
|
<li>a PEM-encoded CA certificate (<code>ca.crt</code>)</li>
|
||||||
|
</ul>
|
||||||
|
<p>and whichever are supplied, will be used for connecting to the
|
||||||
|
STS endpoint. The client cert and key are useful if you are
|
||||||
|
authenticating with a certificate; the CA cert is useful if
|
||||||
|
you are using a self-signed server certificate. The Secret must
|
||||||
|
be of type <code>Opaque</code> or <code>kubernetes.io/tls</code>.</p>
|
||||||
|
<p>This field is only supported for the <code>ldap</code> provider.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1569,7 +1611,7 @@ BucketSTSSpec
|
||||||
<p>STS specifies the required configuration to use a Security Token
|
<p>STS specifies the required configuration to use a Security Token
|
||||||
Service for fetching temporary credentials to authenticate in a
|
Service for fetching temporary credentials to authenticate in a
|
||||||
Bucket provider.</p>
|
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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -756,15 +756,75 @@ configuration. A Security Token Service (STS) is a web service that issues
|
||||||
temporary security credentials. By adding this field, one may specify the
|
temporary security credentials. By adding this field, one may specify the
|
||||||
STS endpoint from where temporary credentials will be fetched.
|
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:
|
If using `.spec.sts`, the following fields are required:
|
||||||
|
|
||||||
- `.spec.sts.provider`, the Security Token Service provider. The only supported
|
- `.spec.sts.provider`, the Security Token Service provider. The only supported
|
||||||
option is `aws`.
|
option for the `generic` bucket provider is `ldap`. The only supported option
|
||||||
|
for the `aws` bucket provider is `aws`.
|
||||||
- `.spec.sts.endpoint`, the HTTP/S endpoint of the Security Token Service. In
|
- `.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
|
the case of `aws` this can be `https://sts.amazonaws.com`, or a Regional STS
|
||||||
Endpoint, or an Interface Endpoint created inside a VPC.
|
Endpoint, or an Interface Endpoint created inside a VPC. In the case of
|
||||||
|
`ldap` this must be the LDAP server endpoint.
|
||||||
|
|
||||||
This field is only supported for the `aws` bucket provider.
|
When using the `ldap` provider, the following fields may also be specified:
|
||||||
|
|
||||||
|
- `.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.
|
||||||
|
- `.spec.sts.certSecretRef.name`, the name of the Secret containing the
|
||||||
|
TLS configuration for communicating with the STS endpoint. The contents
|
||||||
|
of this Secret must follow the same structure of
|
||||||
|
[`.spec.certSecretRef.name`](#cert-secret-reference).
|
||||||
|
|
||||||
|
If [`.spec.proxySecretRef.name`](#proxy-secret-reference) is specified,
|
||||||
|
the proxy configuration will be used for commucating with the STS endpoint.
|
||||||
|
|
||||||
|
Example for the `ldap` provider:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||||
|
kind: Bucket
|
||||||
|
metadata:
|
||||||
|
name: example
|
||||||
|
namespace: example
|
||||||
|
spec:
|
||||||
|
interval: 5m
|
||||||
|
bucketName: example
|
||||||
|
provider: generic
|
||||||
|
endpoint: minio.example.com
|
||||||
|
sts:
|
||||||
|
provider: ldap
|
||||||
|
endpoint: https://ldap.example.com
|
||||||
|
secretRef:
|
||||||
|
name: ldap-credentials
|
||||||
|
certSecretRef:
|
||||||
|
name: ldap-tls
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: ldap-credentials
|
||||||
|
namespace: example
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
username: <username>
|
||||||
|
password: <password>
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: ldap-tls
|
||||||
|
namespace: example
|
||||||
|
type: kubernetes.io/tls # or Opaque
|
||||||
|
stringData:
|
||||||
|
tls.crt: <PEM-encoded cert>
|
||||||
|
tls.key: <PEM-encoded key>
|
||||||
|
ca.crt: <PEM-encoded cert>
|
||||||
|
```
|
||||||
|
|
||||||
### Bucket name
|
### Bucket name
|
||||||
|
|
||||||
|
|
|
@ -483,8 +483,27 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
|
||||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||||
return sreconcile.ResultEmpty, e
|
return sreconcile.ResultEmpty, e
|
||||||
}
|
}
|
||||||
|
tlsConfig, err := r.getTLSConfig(ctx, obj.Spec.CertSecretRef, obj.GetNamespace(), obj.Spec.Endpoint)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
stsTLSConfig, err := r.getSTSTLSConfig(ctx, obj)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("failed to get STS TLS config: %w", err)
|
||||||
|
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 sts := obj.Spec.STS; sts != nil {
|
||||||
if err := minio.ValidateSTSProvider(obj.Spec.Provider, sts.Provider); err != nil {
|
if err := minio.ValidateSTSProvider(obj.Spec.Provider, sts); err != nil {
|
||||||
e := serror.NewStalling(err, sourcev1.InvalidSTSConfigurationReason)
|
e := serror.NewStalling(err, sourcev1.InvalidSTSConfigurationReason)
|
||||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||||
return sreconcile.ResultEmpty, e
|
return sreconcile.ResultEmpty, e
|
||||||
|
@ -495,12 +514,11 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
|
||||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||||
return sreconcile.ResultEmpty, e
|
return sreconcile.ResultEmpty, e
|
||||||
}
|
}
|
||||||
}
|
if err := minio.ValidateSTSSecret(sts.Provider, stsSecret); err != nil {
|
||||||
tlsConfig, err := r.getTLSConfig(ctx, obj)
|
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
|
||||||
if err != nil {
|
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||||
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
|
return sreconcile.ResultEmpty, e
|
||||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
}
|
||||||
return sreconcile.ResultEmpty, e
|
|
||||||
}
|
}
|
||||||
var opts []minio.Option
|
var opts []minio.Option
|
||||||
if secret != nil {
|
if secret != nil {
|
||||||
|
@ -512,6 +530,12 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
|
||||||
if proxyURL != nil {
|
if proxyURL != nil {
|
||||||
opts = append(opts, minio.WithProxyURL(proxyURL))
|
opts = append(opts, minio.WithProxyURL(proxyURL))
|
||||||
}
|
}
|
||||||
|
if stsSecret != nil {
|
||||||
|
opts = append(opts, minio.WithSTSSecret(stsSecret))
|
||||||
|
}
|
||||||
|
if stsTLSConfig != nil {
|
||||||
|
opts = append(opts, minio.WithSTSTLSConfig(stsTLSConfig))
|
||||||
|
}
|
||||||
if provider, err = minio.NewClient(obj, opts...); err != nil {
|
if provider, err = minio.NewClient(obj, opts...); err != nil {
|
||||||
e := serror.NewGeneric(err, "ClientError")
|
e := serror.NewGeneric(err, "ClientError")
|
||||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||||
|
@ -732,12 +756,15 @@ func (r *BucketReconciler) getSecret(ctx context.Context, secretRef *meta.LocalO
|
||||||
return secret, nil
|
return secret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BucketReconciler) getTLSConfig(ctx context.Context, obj *bucketv1.Bucket) (*stdtls.Config, error) {
|
// getTLSConfig attempts to fetch a TLS configuration from the given
|
||||||
certSecret, err := r.getSecret(ctx, obj.Spec.CertSecretRef, obj.GetNamespace())
|
// Secret reference, namespace and endpoint.
|
||||||
|
func (r *BucketReconciler) getTLSConfig(ctx context.Context,
|
||||||
|
secretRef *meta.LocalObjectReference, namespace, endpoint string) (*stdtls.Config, error) {
|
||||||
|
certSecret, err := r.getSecret(ctx, secretRef, namespace)
|
||||||
if err != nil || certSecret == nil {
|
if err != nil || certSecret == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tlsConfig, _, err := tls.KubeTLSClientConfigFromSecret(*certSecret, obj.Spec.Endpoint)
|
tlsConfig, _, err := tls.KubeTLSClientConfigFromSecret(*certSecret, endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create TLS config: %w", err)
|
return nil, fmt.Errorf("failed to create TLS config: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -747,6 +774,8 @@ func (r *BucketReconciler) getTLSConfig(ctx context.Context, obj *bucketv1.Bucke
|
||||||
return tlsConfig, nil
|
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) {
|
func (r *BucketReconciler) getProxyURL(ctx context.Context, obj *bucketv1.Bucket) (*url.URL, error) {
|
||||||
namespace := obj.GetNamespace()
|
namespace := obj.GetNamespace()
|
||||||
proxySecret, err := r.getSecret(ctx, obj.Spec.ProxySecretRef, namespace)
|
proxySecret, err := r.getSecret(ctx, obj.Spec.ProxySecretRef, namespace)
|
||||||
|
@ -771,6 +800,24 @@ func (r *BucketReconciler) getProxyURL(ctx context.Context, obj *bucketv1.Bucket
|
||||||
return proxyURL, nil
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSTSTLSConfig attempts to fetch the certificate secret from the object's
|
||||||
|
// STS configuration.
|
||||||
|
func (r *BucketReconciler) getSTSTLSConfig(ctx context.Context, obj *bucketv1.Bucket) (*stdtls.Config, error) {
|
||||||
|
if obj.Spec.STS == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return r.getTLSConfig(ctx, obj.Spec.STS.CertSecretRef, obj.GetNamespace(), obj.Spec.STS.Endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
// eventLogf records events, and logs at the same time.
|
// eventLogf records events, and logs at the same time.
|
||||||
//
|
//
|
||||||
// This log is different from the debug log in the EventRecorder, in the sense
|
// This log is different from the debug log in the EventRecorder, in the sense
|
||||||
|
|
|
@ -592,6 +592,94 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
||||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "invalid proxy secret '/dummy': key 'address' is missing"),
|
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "invalid proxy secret '/dummy': key 'address' is missing"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Observes non-existing sts.secretRef",
|
||||||
|
bucketName: "dummy",
|
||||||
|
beforeFunc: func(obj *bucketv1.Bucket) {
|
||||||
|
obj.Spec.STS = &bucketv1.BucketSTSSpec{
|
||||||
|
SecretRef: &meta.LocalObjectReference{Name: "dummy"},
|
||||||
|
}
|
||||||
|
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
|
||||||
|
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
assertIndex: index.NewDigester(),
|
||||||
|
assertConditions: []metav1.Condition{
|
||||||
|
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||||
|
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||||
|
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Observes invalid sts.secretRef",
|
||||||
|
bucketName: "dummy",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "dummy",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeFunc: func(obj *bucketv1.Bucket) {
|
||||||
|
obj.Spec.Provider = "generic"
|
||||||
|
obj.Spec.STS = &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: "https://something",
|
||||||
|
SecretRef: &meta.LocalObjectReference{Name: "dummy"},
|
||||||
|
}
|
||||||
|
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
|
||||||
|
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
assertIndex: index.NewDigester(),
|
||||||
|
assertConditions: []metav1.Condition{
|
||||||
|
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||||
|
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||||
|
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "invalid 'dummy' secret data for 'ldap' STS provider: required fields username, password"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Observes non-existing sts.certSecretRef",
|
||||||
|
bucketName: "dummy",
|
||||||
|
beforeFunc: func(obj *bucketv1.Bucket) {
|
||||||
|
obj.Spec.STS = &bucketv1.BucketSTSSpec{
|
||||||
|
CertSecretRef: &meta.LocalObjectReference{Name: "dummy"},
|
||||||
|
}
|
||||||
|
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
|
||||||
|
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
assertIndex: index.NewDigester(),
|
||||||
|
assertConditions: []metav1.Condition{
|
||||||
|
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||||
|
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||||
|
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Observes invalid sts.certSecretRef",
|
||||||
|
bucketName: "dummy",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "dummy",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeFunc: func(obj *bucketv1.Bucket) {
|
||||||
|
obj.Spec.Provider = "generic"
|
||||||
|
obj.Spec.STS = &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: "https://something",
|
||||||
|
CertSecretRef: &meta.LocalObjectReference{Name: "dummy"},
|
||||||
|
}
|
||||||
|
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
|
||||||
|
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
assertIndex: index.NewDigester(),
|
||||||
|
assertConditions: []metav1.Condition{
|
||||||
|
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||||
|
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||||
|
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get STS TLS config: certificate secret does not contain any TLS configuration"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Observes non-existing bucket name",
|
name: "Observes non-existing bucket name",
|
||||||
bucketName: "dummy",
|
bucketName: "dummy",
|
||||||
|
@ -609,7 +697,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Observes incompatible STS provider",
|
name: "Observes incompatible sts.provider",
|
||||||
bucketName: "dummy",
|
bucketName: "dummy",
|
||||||
beforeFunc: func(obj *bucketv1.Bucket) {
|
beforeFunc: func(obj *bucketv1.Bucket) {
|
||||||
obj.Spec.Provider = "generic"
|
obj.Spec.Provider = "generic"
|
||||||
|
@ -622,18 +710,18 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
assertIndex: index.NewDigester(),
|
assertIndex: index.NewDigester(),
|
||||||
assertConditions: []metav1.Condition{
|
assertConditions: []metav1.Condition{
|
||||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.InvalidSTSConfigurationReason, "STS configuration is not supported for 'generic' bucket provider"),
|
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.InvalidSTSConfigurationReason, "STS provider 'aws' is not supported for 'generic' bucket provider"),
|
||||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Observes invalid STS endpoint",
|
name: "Observes invalid sts.endpoint",
|
||||||
bucketName: "dummy",
|
bucketName: "dummy",
|
||||||
beforeFunc: func(obj *bucketv1.Bucket) {
|
beforeFunc: func(obj *bucketv1.Bucket) {
|
||||||
obj.Spec.Provider = "aws" // TODO: change to generic when ldap STS provider is implemented
|
obj.Spec.Provider = "generic"
|
||||||
obj.Spec.STS = &bucketv1.BucketSTSSpec{
|
obj.Spec.STS = &bucketv1.BucketSTSSpec{
|
||||||
Provider: "aws", // TODO: change to ldap when ldap STS provider is implemented
|
Provider: "ldap",
|
||||||
Endpoint: "something\t",
|
Endpoint: "something\t",
|
||||||
}
|
}
|
||||||
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
|
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
|
||||||
|
@ -1863,7 +1951,7 @@ func TestBucketReconciler_APIServerValidation_STS(t *testing.T) {
|
||||||
Provider: "aws",
|
Provider: "aws",
|
||||||
Endpoint: "http://test",
|
Endpoint: "http://test",
|
||||||
},
|
},
|
||||||
err: "STS configuration is only supported for the 'aws' Bucket provider",
|
err: "STS configuration is only supported for the 'aws' and 'generic' Bucket providers",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "azure unsupported",
|
name: "azure unsupported",
|
||||||
|
@ -1872,16 +1960,7 @@ func TestBucketReconciler_APIServerValidation_STS(t *testing.T) {
|
||||||
Provider: "aws",
|
Provider: "aws",
|
||||||
Endpoint: "http://test",
|
Endpoint: "http://test",
|
||||||
},
|
},
|
||||||
err: "STS configuration is only supported for the 'aws' Bucket provider",
|
err: "STS configuration is only supported for the 'aws' and 'generic' Bucket providers",
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "generic unsupported",
|
|
||||||
bucketProvider: "generic",
|
|
||||||
stsConfig: &bucketv1.BucketSTSSpec{
|
|
||||||
Provider: "aws",
|
|
||||||
Endpoint: "http://test",
|
|
||||||
},
|
|
||||||
err: "STS configuration is only supported for the 'aws' Bucket provider",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "aws supported",
|
name: "aws supported",
|
||||||
|
@ -1916,16 +1995,70 @@ func TestBucketReconciler_APIServerValidation_STS(t *testing.T) {
|
||||||
name: "aws can be created without STS config",
|
name: "aws can be created without STS config",
|
||||||
bucketProvider: "aws",
|
bucketProvider: "aws",
|
||||||
},
|
},
|
||||||
// Can't be tested at present with only one allowed sts provider.
|
{
|
||||||
// {
|
name: "ldap unsupported for aws",
|
||||||
// name: "ldap unsupported for aws",
|
bucketProvider: "aws",
|
||||||
// bucketProvider: "aws",
|
stsConfig: &bucketv1.BucketSTSSpec{
|
||||||
// stsConfig: &bucketv1.BucketSTSSpec{
|
Provider: "ldap",
|
||||||
// Provider: "ldap",
|
Endpoint: "http://test",
|
||||||
// Endpoint: "http://test",
|
},
|
||||||
// },
|
err: "'aws' is the only supported STS provider for the 'aws' Bucket provider",
|
||||||
// err: "'aws' is the only supported STS provider for the 'aws' Bucket provider",
|
},
|
||||||
// },
|
{
|
||||||
|
name: "aws unsupported for generic",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsConfig: &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "aws",
|
||||||
|
Endpoint: "http://test",
|
||||||
|
},
|
||||||
|
err: "'ldap' is the only supported STS provider for the 'generic' Bucket provider",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aws does not require a secret",
|
||||||
|
bucketProvider: "aws",
|
||||||
|
stsConfig: &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "aws",
|
||||||
|
Endpoint: "http://test",
|
||||||
|
SecretRef: &meta.LocalObjectReference{},
|
||||||
|
},
|
||||||
|
err: "spec.sts.secretRef is not required for the 'aws' STS provider",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aws does not require a cert secret",
|
||||||
|
bucketProvider: "aws",
|
||||||
|
stsConfig: &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "aws",
|
||||||
|
Endpoint: "http://test",
|
||||||
|
CertSecretRef: &meta.LocalObjectReference{},
|
||||||
|
},
|
||||||
|
err: "spec.sts.certSecretRef is not required for the 'aws' STS provider",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap may use a secret",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsConfig: &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: "http://test",
|
||||||
|
SecretRef: &meta.LocalObjectReference{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap may use a cert secret",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsConfig: &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: "http://test",
|
||||||
|
CertSecretRef: &meta.LocalObjectReference{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap may not use a secret or cert secret",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsConfig: &bucketv1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: "http://test",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7"
|
"github.com/minio/minio-go/v7"
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
|
@ -40,9 +41,11 @@ type MinioClient struct {
|
||||||
|
|
||||||
// options holds the configuration for the Minio client.
|
// options holds the configuration for the Minio client.
|
||||||
type options struct {
|
type options struct {
|
||||||
secret *corev1.Secret
|
secret *corev1.Secret
|
||||||
tlsConfig *tls.Config
|
stsSecret *corev1.Secret
|
||||||
proxyURL *url.URL
|
tlsConfig *tls.Config
|
||||||
|
stsTLSConfig *tls.Config
|
||||||
|
proxyURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option is a function that configures the Minio client.
|
// Option is a function that configures the Minio client.
|
||||||
|
@ -69,6 +72,20 @@ func WithProxyURL(proxyURL *url.URL) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSTSSecret sets the STS secret for the Minio client.
|
||||||
|
func WithSTSSecret(secret *corev1.Secret) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.stsSecret = secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSTSTLSConfig sets the STS TLS configuration for the Minio client.
|
||||||
|
func WithSTSTLSConfig(tlsConfig *tls.Config) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.stsTLSConfig = tlsConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewClient creates a new Minio storage client.
|
// NewClient creates a new Minio storage client.
|
||||||
func NewClient(bucket *sourcev1.Bucket, opts ...Option) (*MinioClient, error) {
|
func NewClient(bucket *sourcev1.Bucket, opts ...Option) (*MinioClient, error) {
|
||||||
var o options
|
var o options
|
||||||
|
@ -89,6 +106,8 @@ func NewClient(bucket *sourcev1.Bucket, opts ...Option) (*MinioClient, error) {
|
||||||
minioOpts.Creds = newCredsFromSecret(o.secret)
|
minioOpts.Creds = newCredsFromSecret(o.secret)
|
||||||
case bucketProvider == sourcev1.AmazonBucketProvider:
|
case bucketProvider == sourcev1.AmazonBucketProvider:
|
||||||
minioOpts.Creds = newAWSCreds(bucket, o.proxyURL)
|
minioOpts.Creds = newAWSCreds(bucket, o.proxyURL)
|
||||||
|
case bucketProvider == sourcev1.GenericBucketProvider:
|
||||||
|
minioOpts.Creds = newGenericCreds(bucket, &o)
|
||||||
}
|
}
|
||||||
|
|
||||||
var transportOpts []func(*http.Transport)
|
var transportOpts []func(*http.Transport)
|
||||||
|
@ -159,6 +178,43 @@ func newAWSCreds(bucket *sourcev1.Bucket, proxyURL *url.URL) *credentials.Creden
|
||||||
return creds
|
return creds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newGenericCreds creates a new Minio credentials object for the `generic` bucket provider.
|
||||||
|
func newGenericCreds(bucket *sourcev1.Bucket, o *options) *credentials.Credentials {
|
||||||
|
|
||||||
|
sts := bucket.Spec.STS
|
||||||
|
if sts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch sts.Provider {
|
||||||
|
case sourcev1.STSProviderLDAP:
|
||||||
|
client := &http.Client{Transport: http.DefaultTransport}
|
||||||
|
if o.proxyURL != nil || o.stsTLSConfig != nil {
|
||||||
|
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
if o.proxyURL != nil {
|
||||||
|
transport.Proxy = http.ProxyURL(o.proxyURL)
|
||||||
|
}
|
||||||
|
if o.stsTLSConfig != nil {
|
||||||
|
transport.TLSClientConfig = o.stsTLSConfig.Clone()
|
||||||
|
}
|
||||||
|
client = &http.Client{Transport: transport}
|
||||||
|
}
|
||||||
|
var username, password string
|
||||||
|
if o.stsSecret != nil {
|
||||||
|
username = string(o.stsSecret.Data["username"])
|
||||||
|
password = string(o.stsSecret.Data["password"])
|
||||||
|
}
|
||||||
|
return credentials.New(&credentials.LDAPIdentity{
|
||||||
|
Client: client,
|
||||||
|
STSEndpoint: sts.Endpoint,
|
||||||
|
LDAPUsername: username,
|
||||||
|
LDAPPassword: password,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateSecret validates the credential secret. The provided Secret may
|
// ValidateSecret validates the credential secret. The provided Secret may
|
||||||
// be nil.
|
// be nil.
|
||||||
func ValidateSecret(secret *corev1.Secret) error {
|
func ValidateSecret(secret *corev1.Secret) error {
|
||||||
|
@ -176,14 +232,31 @@ func ValidateSecret(secret *corev1.Secret) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateSTSProvider validates the STS provider.
|
// ValidateSTSProvider validates the STS provider.
|
||||||
func ValidateSTSProvider(bucketProvider, stsProvider string) error {
|
func ValidateSTSProvider(bucketProvider string, sts *sourcev1.BucketSTSSpec) error {
|
||||||
errProviderIncompatbility := fmt.Errorf("STS provider '%s' is not supported for '%s' bucket provider",
|
errProviderIncompatbility := fmt.Errorf("STS provider '%s' is not supported for '%s' bucket provider",
|
||||||
stsProvider, bucketProvider)
|
sts.Provider, bucketProvider)
|
||||||
|
errSecretNotRequired := fmt.Errorf("spec.sts.secretRef is not required for the '%s' STS provider",
|
||||||
|
sts.Provider)
|
||||||
|
errCertSecretNotRequired := fmt.Errorf("spec.sts.certSecretRef is not required for the '%s' STS provider",
|
||||||
|
sts.Provider)
|
||||||
|
|
||||||
switch bucketProvider {
|
switch bucketProvider {
|
||||||
case sourcev1.AmazonBucketProvider:
|
case sourcev1.AmazonBucketProvider:
|
||||||
switch stsProvider {
|
switch sts.Provider {
|
||||||
case sourcev1.STSProviderAmazon:
|
case sourcev1.STSProviderAmazon:
|
||||||
|
if sts.SecretRef != nil {
|
||||||
|
return errSecretNotRequired
|
||||||
|
}
|
||||||
|
if sts.CertSecretRef != nil {
|
||||||
|
return errCertSecretNotRequired
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errProviderIncompatbility
|
||||||
|
}
|
||||||
|
case sourcev1.GenericBucketProvider:
|
||||||
|
switch sts.Provider {
|
||||||
|
case sourcev1.STSProviderLDAP:
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return errProviderIncompatbility
|
return errProviderIncompatbility
|
||||||
|
@ -193,6 +266,36 @@ func ValidateSTSProvider(bucketProvider, stsProvider string) error {
|
||||||
return fmt.Errorf("STS configuration is not supported for '%s' bucket provider", bucketProvider)
|
return fmt.Errorf("STS configuration is not supported for '%s' bucket provider", bucketProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateSTSSecret validates the STS secret. The provided Secret may be nil.
|
||||||
|
func ValidateSTSSecret(stsProvider string, secret *corev1.Secret) error {
|
||||||
|
switch stsProvider {
|
||||||
|
case sourcev1.STSProviderLDAP:
|
||||||
|
return validateSTSSecretForProvider(stsProvider, secret, "username", "password")
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateSTSSecretForProvider validates the STS secret for each provider.
|
||||||
|
// The provided Secret may be nil.
|
||||||
|
func validateSTSSecretForProvider(stsProvider string, secret *corev1.Secret, keys ...string) error {
|
||||||
|
if secret == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("invalid '%s' secret data for '%s' STS provider: required fields %s",
|
||||||
|
secret.Name, stsProvider, strings.Join(keys, ", "))
|
||||||
|
if len(secret.Data) == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
value, ok := secret.Data[key]
|
||||||
|
if !ok || len(value) == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// FGetObject gets the object from the provided object storage bucket, and
|
// FGetObject gets the object from the provided object storage bucket, and
|
||||||
// writes it to targetPath.
|
// writes it to targetPath.
|
||||||
// It returns the etag of the successfully fetched file, or any error.
|
// It returns the etag of the successfully fetched file, or any error.
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
@ -70,6 +71,11 @@ var (
|
||||||
testMinioClient *MinioClient
|
testMinioClient *MinioClient
|
||||||
// testTLSConfig is the TLS configuration used to connect to the Minio server.
|
// testTLSConfig is the TLS configuration used to connect to the Minio server.
|
||||||
testTLSConfig *tls.Config
|
testTLSConfig *tls.Config
|
||||||
|
// testServerCert is the path to the server certificate used to start the Minio
|
||||||
|
// and STS servers.
|
||||||
|
testServerCert string
|
||||||
|
// testServerKey is the path to the server key used to start the Minio and STS servers.
|
||||||
|
testServerKey string
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -128,8 +134,7 @@ func TestMain(m *testing.M) {
|
||||||
|
|
||||||
// Load a private key and certificate from a self-signed CA for the Minio server and
|
// Load a private key and certificate from a self-signed CA for the Minio server and
|
||||||
// a client TLS configuration to connect to the Minio server.
|
// a client TLS configuration to connect to the Minio server.
|
||||||
var serverCert, serverKey string
|
testServerCert, testServerKey, testTLSConfig, err = loadServerCertAndClientTLSConfig()
|
||||||
serverCert, serverKey, testTLSConfig, err = loadServerCertAndClientTLSConfig()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not load server cert and client TLS config: %s", err)
|
log.Fatalf("could not load server cert and client TLS config: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -148,8 +153,8 @@ func TestMain(m *testing.M) {
|
||||||
},
|
},
|
||||||
Cmd: []string{"server", "/data", "--console-address", ":9001"},
|
Cmd: []string{"server", "/data", "--console-address", ":9001"},
|
||||||
Mounts: []string{
|
Mounts: []string{
|
||||||
fmt.Sprintf("%s:/root/.minio/certs/public.crt", serverCert),
|
fmt.Sprintf("%s:/root/.minio/certs/public.crt", testServerCert),
|
||||||
fmt.Sprintf("%s:/root/.minio/certs/private.key", serverKey),
|
fmt.Sprintf("%s:/root/.minio/certs/private.key", testServerKey),
|
||||||
},
|
},
|
||||||
}, func(config *docker.HostConfig) {
|
}, func(config *docker.HostConfig) {
|
||||||
config.AutoRemove = true
|
config.AutoRemove = true
|
||||||
|
@ -247,24 +252,24 @@ func TestFGetObject(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) {
|
func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) {
|
||||||
// start a mock STS server
|
// start a mock AWS STS server
|
||||||
stsListener, stsAddr, stsPort := testlistener.New(t)
|
awsSTSListener, awsSTSAddr, awsSTSPort := testlistener.New(t)
|
||||||
stsEndpoint := fmt.Sprintf("http://%s", stsAddr)
|
awsSTSEndpoint := fmt.Sprintf("http://%s", awsSTSAddr)
|
||||||
stsHandler := http.NewServeMux()
|
awsSTSHandler := http.NewServeMux()
|
||||||
stsHandler.HandleFunc("PUT "+credentials.TokenPath,
|
awsSTSHandler.HandleFunc("PUT "+credentials.TokenPath,
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
_, err := w.Write([]byte("mock-token"))
|
_, err := w.Write([]byte("mock-token"))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
})
|
})
|
||||||
stsHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath,
|
awsSTSHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath,
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
token := r.Header.Get(credentials.TokenRequestHeader)
|
token := r.Header.Get(credentials.TokenRequestHeader)
|
||||||
assert.Equal(t, token, "mock-token")
|
assert.Equal(t, token, "mock-token")
|
||||||
_, err := w.Write([]byte("mock-role"))
|
_, err := w.Write([]byte("mock-role"))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
})
|
})
|
||||||
var roleCredsRetrieved bool
|
var credsRetrieved bool
|
||||||
stsHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath+"mock-role",
|
awsSTSHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath+"mock-role",
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
token := r.Header.Get(credentials.TokenRequestHeader)
|
token := r.Header.Get(credentials.TokenRequestHeader)
|
||||||
assert.Equal(t, token, "mock-token")
|
assert.Equal(t, token, "mock-token")
|
||||||
|
@ -274,81 +279,187 @@ func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) {
|
||||||
"SecretAccessKey": testMinioRootPassword,
|
"SecretAccessKey": testMinioRootPassword,
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
roleCredsRetrieved = true
|
credsRetrieved = true
|
||||||
})
|
})
|
||||||
stsServer := &http.Server{
|
awsSTSServer := &http.Server{
|
||||||
Addr: stsAddr,
|
Addr: awsSTSAddr,
|
||||||
Handler: stsHandler,
|
Handler: awsSTSHandler,
|
||||||
}
|
}
|
||||||
go stsServer.Serve(stsListener)
|
go awsSTSServer.Serve(awsSTSListener)
|
||||||
defer stsServer.Shutdown(context.Background())
|
defer awsSTSServer.Shutdown(context.Background())
|
||||||
|
|
||||||
|
// start a mock LDAP STS server
|
||||||
|
ldapSTSListener, ldapSTSAddr, ldapSTSPort := testlistener.New(t)
|
||||||
|
ldapSTSEndpoint := fmt.Sprintf("https://%s", ldapSTSAddr)
|
||||||
|
ldapSTSHandler := http.NewServeMux()
|
||||||
|
var ldapUsername, ldapPassword string
|
||||||
|
ldapSTSHandler.HandleFunc("POST /",
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := r.ParseForm()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
username := r.Form.Get("LDAPUsername")
|
||||||
|
password := r.Form.Get("LDAPPassword")
|
||||||
|
assert.Equal(t, username, ldapUsername)
|
||||||
|
assert.Equal(t, password, ldapPassword)
|
||||||
|
var result credentials.LDAPIdentityResult
|
||||||
|
result.Credentials.AccessKey = testMinioRootUser
|
||||||
|
result.Credentials.SecretKey = testMinioRootPassword
|
||||||
|
err = xml.NewEncoder(w).Encode(credentials.AssumeRoleWithLDAPResponse{Result: result})
|
||||||
|
assert.NilError(t, err)
|
||||||
|
credsRetrieved = true
|
||||||
|
})
|
||||||
|
ldapSTSServer := &http.Server{
|
||||||
|
Addr: ldapSTSAddr,
|
||||||
|
Handler: ldapSTSHandler,
|
||||||
|
}
|
||||||
|
go ldapSTSServer.ServeTLS(ldapSTSListener, testServerCert, testServerKey)
|
||||||
|
defer ldapSTSServer.Shutdown(context.Background())
|
||||||
|
|
||||||
// start proxy
|
// start proxy
|
||||||
proxyAddr, proxyPort := testproxy.New(t)
|
proxyAddr, proxyPort := testproxy.New(t)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
provider string
|
provider string
|
||||||
stsSpec *sourcev1.BucketSTSSpec
|
stsSpec *sourcev1.BucketSTSSpec
|
||||||
opts []Option
|
opts []Option
|
||||||
err string
|
ldapUsername string
|
||||||
|
ldapPassword string
|
||||||
|
err string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "with correct endpoint",
|
name: "with correct aws endpoint",
|
||||||
provider: "aws",
|
provider: "aws",
|
||||||
stsSpec: &sourcev1.BucketSTSSpec{
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
Provider: "aws",
|
Provider: "aws",
|
||||||
Endpoint: stsEndpoint,
|
Endpoint: awsSTSEndpoint,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with incorrect endpoint",
|
name: "with incorrect aws endpoint",
|
||||||
provider: "aws",
|
provider: "aws",
|
||||||
stsSpec: &sourcev1.BucketSTSSpec{
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
Provider: "aws",
|
Provider: "aws",
|
||||||
Endpoint: fmt.Sprintf("http://localhost:%d", stsPort+1),
|
Endpoint: fmt.Sprintf("http://localhost:%d", awsSTSPort+1),
|
||||||
},
|
},
|
||||||
err: "connection refused",
|
err: "connection refused",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with correct endpoint and proxy",
|
name: "with correct aws endpoint and proxy",
|
||||||
provider: "aws",
|
provider: "aws",
|
||||||
stsSpec: &sourcev1.BucketSTSSpec{
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
Provider: "aws",
|
Provider: "aws",
|
||||||
Endpoint: stsEndpoint,
|
Endpoint: awsSTSEndpoint,
|
||||||
},
|
},
|
||||||
opts: []Option{WithProxyURL(&url.URL{Scheme: "http", Host: proxyAddr})},
|
opts: []Option{WithProxyURL(&url.URL{Scheme: "http", Host: proxyAddr})},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with correct endpoint and incorrect proxy",
|
name: "with correct aws endpoint and incorrect proxy",
|
||||||
provider: "aws",
|
provider: "aws",
|
||||||
stsSpec: &sourcev1.BucketSTSSpec{
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
Provider: "aws",
|
Provider: "aws",
|
||||||
Endpoint: stsEndpoint,
|
Endpoint: awsSTSEndpoint,
|
||||||
},
|
},
|
||||||
opts: []Option{WithProxyURL(&url.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", proxyPort+1)})},
|
opts: []Option{WithProxyURL(&url.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", proxyPort+1)})},
|
||||||
err: "connection refused",
|
err: "connection refused",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "with correct ldap endpoint",
|
||||||
|
provider: "generic",
|
||||||
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: ldapSTSEndpoint,
|
||||||
|
},
|
||||||
|
opts: []Option{WithSTSTLSConfig(testTLSConfig)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with incorrect ldap endpoint",
|
||||||
|
provider: "generic",
|
||||||
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: fmt.Sprintf("http://localhost:%d", ldapSTSPort+1),
|
||||||
|
},
|
||||||
|
err: "connection refused",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with correct ldap endpoint and secret",
|
||||||
|
provider: "generic",
|
||||||
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: ldapSTSEndpoint,
|
||||||
|
},
|
||||||
|
opts: []Option{
|
||||||
|
WithSTSTLSConfig(testTLSConfig),
|
||||||
|
WithSTSSecret(&corev1.Secret{
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"username": []byte("user"),
|
||||||
|
"password": []byte("password"),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
ldapUsername: "user",
|
||||||
|
ldapPassword: "password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with correct ldap endpoint and proxy",
|
||||||
|
provider: "generic",
|
||||||
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: ldapSTSEndpoint,
|
||||||
|
},
|
||||||
|
opts: []Option{
|
||||||
|
WithProxyURL(&url.URL{Scheme: "http", Host: proxyAddr}),
|
||||||
|
WithSTSTLSConfig(testTLSConfig),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with correct ldap endpoint and incorrect proxy",
|
||||||
|
provider: "generic",
|
||||||
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: ldapSTSEndpoint,
|
||||||
|
},
|
||||||
|
opts: []Option{
|
||||||
|
WithProxyURL(&url.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", proxyPort+1)}),
|
||||||
|
},
|
||||||
|
err: "connection refused",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with correct ldap endpoint and without client tls config",
|
||||||
|
provider: "generic",
|
||||||
|
stsSpec: &sourcev1.BucketSTSSpec{
|
||||||
|
Provider: "ldap",
|
||||||
|
Endpoint: ldapSTSEndpoint,
|
||||||
|
},
|
||||||
|
err: "tls: failed to verify certificate: x509: certificate signed by unknown authority",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
roleCredsRetrieved = false
|
credsRetrieved = false
|
||||||
|
ldapUsername = tt.ldapUsername
|
||||||
|
ldapPassword = tt.ldapPassword
|
||||||
|
|
||||||
bucket := bucketStub(bucket, testMinioAddress)
|
bucket := bucketStub(bucket, testMinioAddress)
|
||||||
bucket.Spec.Provider = tt.provider
|
bucket.Spec.Provider = tt.provider
|
||||||
bucket.Spec.STS = tt.stsSpec
|
bucket.Spec.STS = tt.stsSpec
|
||||||
minioClient, err := NewClient(bucket, append(tt.opts, WithTLSConfig(testTLSConfig))...)
|
|
||||||
|
opts := tt.opts
|
||||||
|
opts = append(opts, WithTLSConfig(testTLSConfig))
|
||||||
|
|
||||||
|
minioClient, err := NewClient(bucket, opts...)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Assert(t, minioClient != nil)
|
assert.Assert(t, minioClient != nil)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
tempDir := t.TempDir()
|
path := filepath.Join(t.TempDir(), sourceignore.IgnoreFile)
|
||||||
path := filepath.Join(tempDir, sourceignore.IgnoreFile)
|
|
||||||
_, err = minioClient.FGetObject(ctx, bucketName, objectName, path)
|
_, err = minioClient.FGetObject(ctx, bucketName, objectName, path)
|
||||||
if tt.err != "" {
|
if tt.err != "" {
|
||||||
assert.ErrorContains(t, err, tt.err)
|
assert.ErrorContains(t, err, tt.err)
|
||||||
} else {
|
} else {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Assert(t, roleCredsRetrieved)
|
assert.Assert(t, credsRetrieved)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -477,6 +588,8 @@ func TestValidateSTSProvider(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
bucketProvider string
|
bucketProvider string
|
||||||
stsProvider string
|
stsProvider string
|
||||||
|
withSecret bool
|
||||||
|
withCertSecret bool
|
||||||
err string
|
err string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -485,15 +598,52 @@ func TestValidateSTSProvider(t *testing.T) {
|
||||||
stsProvider: "aws",
|
stsProvider: "aws",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unsupported for aws",
|
name: "aws does not require a secret",
|
||||||
|
bucketProvider: "aws",
|
||||||
|
stsProvider: "aws",
|
||||||
|
withSecret: true,
|
||||||
|
err: "spec.sts.secretRef is not required for the 'aws' STS provider",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aws does not require a cert secret",
|
||||||
|
bucketProvider: "aws",
|
||||||
|
stsProvider: "aws",
|
||||||
|
withCertSecret: true,
|
||||||
|
err: "spec.sts.certSecretRef is not required for the 'aws' STS provider",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsProvider: "ldap",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap may use a secret",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsProvider: "ldap",
|
||||||
|
withSecret: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap may use a cert secret",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsProvider: "ldap",
|
||||||
|
withCertSecret: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap sts provider unsupported for aws bucket provider",
|
||||||
bucketProvider: "aws",
|
bucketProvider: "aws",
|
||||||
stsProvider: "ldap",
|
stsProvider: "ldap",
|
||||||
err: "STS provider 'ldap' is not supported for 'aws' bucket provider",
|
err: "STS provider 'ldap' is not supported for 'aws' bucket provider",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "aws sts provider unsupported for generic bucket provider",
|
||||||
|
bucketProvider: "generic",
|
||||||
|
stsProvider: "aws",
|
||||||
|
err: "STS provider 'aws' is not supported for 'generic' bucket provider",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "unsupported bucket provider",
|
name: "unsupported bucket provider",
|
||||||
bucketProvider: "gcp",
|
bucketProvider: "gcp",
|
||||||
stsProvider: "gcp",
|
stsProvider: "ldap",
|
||||||
err: "STS configuration is not supported for 'gcp' bucket provider",
|
err: "STS configuration is not supported for 'gcp' bucket provider",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -501,7 +651,102 @@ func TestValidateSTSProvider(t *testing.T) {
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
err := ValidateSTSProvider(tt.bucketProvider, tt.stsProvider)
|
sts := &sourcev1.BucketSTSSpec{
|
||||||
|
Provider: tt.stsProvider,
|
||||||
|
}
|
||||||
|
if tt.withSecret {
|
||||||
|
sts.SecretRef = &meta.LocalObjectReference{}
|
||||||
|
}
|
||||||
|
if tt.withCertSecret {
|
||||||
|
sts.CertSecretRef = &meta.LocalObjectReference{}
|
||||||
|
}
|
||||||
|
err := ValidateSTSProvider(tt.bucketProvider, sts)
|
||||||
|
if tt.err != "" {
|
||||||
|
assert.Error(t, err, tt.err)
|
||||||
|
} else {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateSTSSecret(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
provider string
|
||||||
|
secret *corev1.Secret
|
||||||
|
err string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ldap provider does not require a secret",
|
||||||
|
provider: "ldap",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid ldap secret",
|
||||||
|
provider: "ldap",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"username": []byte("user"),
|
||||||
|
"password": []byte("pass"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty ldap secret",
|
||||||
|
provider: "ldap",
|
||||||
|
secret: &corev1.Secret{ObjectMeta: v1.ObjectMeta{Name: "ldap-secret"}},
|
||||||
|
err: "invalid 'ldap-secret' secret data for 'ldap' STS provider: required fields username, password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap secret missing password",
|
||||||
|
provider: "ldap",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"username": []byte("user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: "invalid '' secret data for 'ldap' STS provider: required fields username, password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap secret missing username",
|
||||||
|
provider: "ldap",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"password": []byte("pass"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: "invalid '' secret data for 'ldap' STS provider: required fields username, password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap secret with empty username",
|
||||||
|
provider: "ldap",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"username": []byte(""),
|
||||||
|
"password": []byte("pass"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: "invalid '' secret data for 'ldap' STS provider: required fields username, password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ldap secret with empty password",
|
||||||
|
provider: "ldap",
|
||||||
|
secret: &corev1.Secret{
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"username": []byte("user"),
|
||||||
|
"password": []byte(""),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: "invalid '' secret data for 'ldap' STS provider: required fields username, password",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
err := ValidateSTSSecret(tt.provider, tt.secret)
|
||||||
if tt.err != "" {
|
if tt.err != "" {
|
||||||
assert.Error(t, err, tt.err)
|
assert.Error(t, err, tt.err)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue