Skip to content

Commit

Permalink
Add support for STS endpoint in the Bucket API
Browse files Browse the repository at this point in the history
Signed-off-by: Matheus Pimenta <[email protected]>
  • Loading branch information
matheuscscp committed Jul 20, 2024
1 parent 58b4e6d commit 2f84ddd
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 2 deletions.
8 changes: 8 additions & 0 deletions api/v1beta2/bucket_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ type BucketSpec struct {
// +required
Endpoint string `json:"endpoint"`

// STSEndpoint is the HTTP/S endpoint of the Security Token Service from
// where temporary credentials will automatically be fetched in the
// absence of a Secret reference.
//
// This field is only supported for the `generic` and `aws` providers.
// +optional
STSEndpoint string `json:"stsEndpoint,omitempty"`

// Insecure allows connecting to a non-TLS HTTP Endpoint.
// +optional
Insecure bool `json:"insecure,omitempty"`
Expand Down
9 changes: 9 additions & 0 deletions config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,15 @@ spec:
required:
- name
type: object
stsEndpoint:
description: |-
STSEndpoint is the HTTP/S endpoint of the Security Token Service from
where temporary credentials will automatically be fetched in the
absence of a Secret reference.
This field is only supported for the `generic` and `aws` providers.
type: string
suspend:
description: |-
Suspend tells the controller to suspend the reconciliation of this
Expand Down
30 changes: 30 additions & 0 deletions docs/api/v1beta2/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ string
</tr>
<tr>
<td>
<code>stsEndpoint</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>STSEndpoint is the HTTP/S endpoint of the Security Token Service from
where temporary credentials will automatically be fetched in the
absence of a Secret reference.</p>
<p>This field is only supported for the <code>generic</code> and <code>aws</code> providers.</p>
</td>
</tr>
<tr>
<td>
<code>insecure</code><br>
<em>
bool
Expand Down Expand Up @@ -1480,6 +1495,21 @@ string
</tr>
<tr>
<td>
<code>stsEndpoint</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>STSEndpoint is the HTTP/S endpoint of the Security Token Service from
where temporary credentials will automatically be fetched in the
absence of a Secret reference.</p>
<p>This field is only supported for the <code>generic</code> and <code>aws</code> providers.</p>
</td>
</tr>
<tr>
<td>
<code>insecure</code><br>
<em>
bool
Expand Down
8 changes: 8 additions & 0 deletions docs/spec/v1beta2/buckets.md
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,14 @@ HTTP endpoint requires enabling [`.spec.insecure`](#insecure).
Some endpoints require the specification of a [`.spec.region`](#region),
see [Provider](#provider) for more (provider specific) examples.

### STS Endpoint

`.spec.stsEndpoint` is an optional field that specifies the HTTP/S endpoint
of the Security Token Service from where temporary credentials will automatically
be fetched in the absence of a Secret reference.

This field is only supported for the `generic` and `aws` providers.

### Bucket name

`.spec.bucketName` is a required field that specifies which object storage
Expand Down
4 changes: 2 additions & 2 deletions pkg/minio/minio.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ func NewClient(bucket *sourcev1.Bucket, opts ...Option) (*MinioClient, error) {
if accessKey != "" && secretKey != "" {
minioOpts.Creds = credentials.NewStaticV4(accessKey, secretKey, "")
}
} else if bucket.Spec.Provider == sourcev1.AmazonBucketProvider {
minioOpts.Creds = credentials.NewIAM("")
} else if bucket.Spec.Provider == sourcev1.AmazonBucketProvider || bucket.Spec.STSEndpoint != "" {
minioOpts.Creds = credentials.NewIAM(bucket.Spec.STSEndpoint)
}

var transportOpts []func(*http.Transport)
Expand Down
50 changes: 50 additions & 0 deletions pkg/minio/minio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"log"
Expand All @@ -35,6 +36,7 @@ import (
"github.com/elazarl/goproxy"
"github.com/google/uuid"
miniov7 "github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
"gotest.tools/assert"
Expand Down Expand Up @@ -244,6 +246,54 @@ func TestFGetObject(t *testing.T) {
assert.NilError(t, err)
}

func TestFGetObjectWithSTSEndpoint(t *testing.T) {
// start a mock STS server
stsListener, err := net.Listen("tcp", ":0")
assert.NilError(t, err, "could not start STS listener")
defer stsListener.Close()
stsAddr := stsListener.Addr().String()
stsHandler := http.NewServeMux()
stsHandler.HandleFunc("PUT "+credentials.TokenPath, func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("mock-token"))
assert.NilError(t, err)
})
stsHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath, func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("mock-role"))
assert.NilError(t, err)
})
roleCredsRetrieved := false
stsHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath+"mock-role", func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get(credentials.TokenRequestHeader)
assert.Equal(t, token, "mock-token")
err := json.NewEncoder(w).Encode(map[string]any{
"Code": "Success",
"AccessKeyID": testMinioRootUser,
"SecretAccessKey": testMinioRootPassword,
})
assert.NilError(t, err)
roleCredsRetrieved = true
})
stsServer := &http.Server{
Addr: stsAddr,
Handler: stsHandler,
}
go stsServer.Serve(stsListener)
defer stsServer.Shutdown(context.Background())

// test FGetObject with STS endpoint
bucket := bucketStub(bucket, testMinioAddress)
bucket.Spec.STSEndpoint = fmt.Sprintf("http://%s", stsAddr)
minioClient, err := NewClient(bucket, WithTLSConfig(testTLSConfig))
assert.NilError(t, err)
assert.Assert(t, minioClient != nil)
ctx := context.Background()
tempDir := t.TempDir()
path := filepath.Join(tempDir, sourceignore.IgnoreFile)
_, err = minioClient.FGetObject(ctx, bucketName, objectName, path)
assert.NilError(t, err)
assert.Assert(t, roleCredsRetrieved)
}

func TestNewClientAndFGetObjectWithProxy(t *testing.T) {
// start proxy
proxyListener, err := net.Listen("tcp", ":0")
Expand Down

0 comments on commit 2f84ddd

Please sign in to comment.