Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions internal/controller/bucket_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ type BucketReconciler struct {
kuberecorder.EventRecorder
helper.Metrics

Storage *storage.Storage
ControllerName string
TokenCache *cache.TokenCache
Storage *storage.Storage
ControllerName string
TokenCache *cache.TokenCache
AllowInsecureHTTP bool

patchOptions []patch.Option
}
Expand Down Expand Up @@ -860,6 +861,26 @@ func (r *BucketReconciler) setupCredentials(ctx context.Context, obj *sourcev1.B
// createBucketProvider creates a provider-specific bucket client using the given credentials and configuration.
// It handles different bucket providers (AWS, GCP, Azure, generic) and returns the appropriate client.
func (r *BucketReconciler) createBucketProvider(ctx context.Context, obj *sourcev1.Bucket, creds *bucketCredentials) (BucketProvider, error) {
provider := obj.Spec.Provider
if (provider == sourcev1.BucketProviderAzure || provider == sourcev1.BucketProviderGoogle) && obj.Spec.Insecure {
return nil, serror.NewStalling(
fmt.Errorf("use of insecure HTTP connections isn't allowed for %s storage", provider),
meta.UnsupportedConnectionTypeReason,
)
}
if obj.Spec.Insecure && !r.AllowInsecureHTTP {
return nil, serror.NewStalling(
fmt.Errorf("%w", helper.ErrInsecureHTTPBlocked),
meta.InsecureConnectionsDisallowedReason,
)
}
if creds.proxyURL != nil && creds.proxyURL.Scheme == "http" && !r.AllowInsecureHTTP {
return nil, serror.NewStalling(
fmt.Errorf("%w", helper.ErrInsecureHTTPBlocked),
meta.InsecureConnectionsDisallowedReason,
)
}

authOpts := []auth.Option{
auth.WithClient(r.Client),
auth.WithServiceAccountNamespace(obj.GetNamespace()),
Expand Down
119 changes: 102 additions & 17 deletions internal/controller/bucket_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/fluxcd/pkg/runtime/patch"

sourcev1 "github.com/fluxcd/source-controller/api/v1"
serror "github.com/fluxcd/source-controller/internal/error"
"github.com/fluxcd/source-controller/internal/index"
gcsmock "github.com/fluxcd/source-controller/internal/mock/gcs"
s3mock "github.com/fluxcd/source-controller/internal/mock/s3"
Expand Down Expand Up @@ -84,9 +85,10 @@ func TestBucketReconciler_deleteBeforeFinalizer(t *testing.T) {
g.Expect(k8sClient.Delete(ctx, bucket)).NotTo(HaveOccurred())

r := &BucketReconciler{
Client: k8sClient,
EventRecorder: record.NewFakeRecorder(32),
Storage: testStorage,
AllowInsecureHTTP: true,
Client: k8sClient,
EventRecorder: record.NewFakeRecorder(32),
Storage: testStorage,
}
// NOTE: Only a real API server responds with an error in this scenario.
_, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(bucket)})
Expand Down Expand Up @@ -379,6 +381,7 @@ func TestBucketReconciler_reconcileStorage(t *testing.T) {
}()

r := &BucketReconciler{
AllowInsecureHTTP: true,
Client: fakeclient.NewClientBuilder().
WithScheme(testEnv.GetScheme()).
WithStatusSubresource(&sourcev1.Bucket{}).
Expand Down Expand Up @@ -918,10 +921,11 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
}

r := &BucketReconciler{
EventRecorder: record.NewFakeRecorder(32),
Client: clientBuilder.Build(),
Storage: testStorage,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
AllowInsecureHTTP: true,
EventRecorder: record.NewFakeRecorder(32),
Client: clientBuilder.Build(),
Storage: testStorage,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
}
tmpDir := t.TempDir()

Expand Down Expand Up @@ -977,6 +981,84 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
}
}

func TestBucketReconciler_reconcileSource_insecureHTTP(t *testing.T) {
tests := []struct {
name string
provider string
insecure bool
allowInsecureHTTP bool
wantReason string
wantMsg string
}{
{
name: "generic insecure with AllowInsecureHTTP false stalls",
provider: sourcev1.BucketProviderGeneric,
insecure: true,
allowInsecureHTTP: false,
wantReason: meta.InsecureConnectionsDisallowedReason,
wantMsg: "use of insecure plain HTTP connections is blocked",
},
{
name: "azure insecure is unsupported",
provider: sourcev1.BucketProviderAzure,
insecure: true,
allowInsecureHTTP: true,
wantReason: meta.UnsupportedConnectionTypeReason,
wantMsg: "use of insecure HTTP connections isn't allowed for azure storage",
},
{
name: "gcp insecure is unsupported",
provider: sourcev1.BucketProviderGoogle,
insecure: true,
allowInsecureHTTP: true,
wantReason: meta.UnsupportedConnectionTypeReason,
wantMsg: "use of insecure HTTP connections isn't allowed for gcp storage",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

obj := &sourcev1.Bucket{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "insecure-http-",
Generation: 1,
},
Spec: sourcev1.BucketSpec{
Provider: tt.provider,
BucketName: "dummy",
Endpoint: "example.com",
Insecure: tt.insecure,
Timeout: &metav1.Duration{Duration: timeout},
},
}

r := &BucketReconciler{
AllowInsecureHTTP: tt.allowInsecureHTTP,
Client: fakeclient.NewClientBuilder().
WithScheme(testEnv.GetScheme()).
WithStatusSubresource(&sourcev1.Bucket{}).
Build(),
EventRecorder: record.NewFakeRecorder(32),
Storage: testStorage,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
}

sp := patch.NewSerialPatcher(obj, r.Client)
_, err := r.reconcileSource(context.TODO(), sp, obj, index.NewDigester(), t.TempDir())
g.Expect(err).To(HaveOccurred())
var stalling *serror.Stalling
g.Expect(errors.As(err, &stalling)).To(BeTrue())
g.Expect(stalling.Reason).To(Equal(tt.wantReason))
g.Expect(err.Error()).To(ContainSubstring(tt.wantMsg))
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{
*conditions.TrueCondition(sourcev1.FetchFailedCondition, tt.wantReason, "%s", tt.wantMsg),
}))
})
}
}

func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -1385,10 +1467,11 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
}

r := &BucketReconciler{
EventRecorder: record.NewFakeRecorder(32),
Client: clientBuilder.Build(),
Storage: testStorage,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
AllowInsecureHTTP: true,
EventRecorder: record.NewFakeRecorder(32),
Client: clientBuilder.Build(),
Storage: testStorage,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
}

// Handle ObjectLevelWorkloadIdentity feature gate
Expand Down Expand Up @@ -1588,10 +1671,11 @@ func TestBucketReconciler_reconcileArtifact(t *testing.T) {
WithStatusSubresource(&sourcev1.Bucket{})

r := &BucketReconciler{
Client: clientBuilder.Build(),
EventRecorder: record.NewFakeRecorder(32),
Storage: testStorage,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
AllowInsecureHTTP: true,
Client: clientBuilder.Build(),
EventRecorder: record.NewFakeRecorder(32),
Storage: testStorage,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
}

obj := &sourcev1.Bucket{
Expand Down Expand Up @@ -1821,8 +1905,9 @@ func TestBucketReconciler_notify(t *testing.T) {
}

reconciler := &BucketReconciler{
EventRecorder: recorder,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
AllowInsecureHTTP: true,
EventRecorder: recorder,
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
}
index := index.NewDigester(index.WithIndex(map[string]string{
"zzz": "qqq",
Expand Down
23 changes: 20 additions & 3 deletions internal/controller/gitrepository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,10 @@ type GitRepositoryReconciler struct {
kuberecorder.EventRecorder
helper.Metrics

Storage *storage.Storage
ControllerName string
TokenCache *cache.TokenCache
Storage *storage.Storage
ControllerName string
TokenCache *cache.TokenCache
AllowInsecureHTTP bool

requeueDependency time.Duration
features map[string]bool
Expand Down Expand Up @@ -544,6 +545,22 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}
if u.Scheme == "http" && !r.AllowInsecureHTTP {
e := serror.NewStalling(
fmt.Errorf("%w", helper.ErrInsecureHTTPBlocked),
meta.InsecureConnectionsDisallowedReason,
)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}
if proxyURL != nil && proxyURL.Scheme == "http" && !r.AllowInsecureHTTP {
e := serror.NewStalling(
fmt.Errorf("%w", helper.ErrInsecureHTTPBlocked),
meta.InsecureConnectionsDisallowedReason,
)
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
return sreconcile.ResultEmpty, e
}

authOpts, err := r.getAuthOpts(ctx, obj, *u, proxyURL)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions internal/controller/gitrepository_controller_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,9 @@ func ensureDependencies() error {

startEnvServer(func(m manager.Manager) {
utilruntime.Must((&GitRepositoryReconciler{
Client: m.GetClient(),
Storage: storage,
AllowInsecureHTTP: true,
Client: m.GetClient(),
Storage: storage,
}).SetupWithManagerAndOptions(m, GitRepositoryReconcilerOptions{
RateLimiter: controller.GetDefaultRateLimiter(),
}))
Expand Down
Loading