Skip to content

Implement Policy Fetcher in framework #3539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

ciarams87
Copy link
Contributor

Proposed changes

Problem: As a user
I want NGF to fetch my NAP WAF Policy bundle from a remote location
So that this bundle can be applied by the data plane to protect my traffic

Solution: Create a Policy Fetcher utility in framework which can pull files from a remote location, as configured in the WafPolicy resource.
Implement policy validation via Checksum validation (compute checksum locally and compare to remote provided checksum).

Testing: Ensured the policy can be downloaded from an http location as part of #3454

Closes #3456

Checklist

Before creating a PR, run through this checklist and mark each as complete.

  • I have read the CONTRIBUTING doc
  • I have added tests that prove my fix is effective or that my feature works
  • I have checked that all unit tests pass after adding my changes
  • I have updated necessary documentation
  • I have rebased my branch onto main
  • I will ensure my PR is targeting the main branch and pulling from my branch from my own fork

Release notes

If this PR introduces a change that affects users and needs to be mentioned in the release notes,
please add a brief note that summarizes the change.

NONE

@ciarams87 ciarams87 requested a review from a team as a code owner June 24, 2025 10:09
@github-actions github-actions bot added the enhancement New feature or request label Jun 24, 2025
@ciarams87 ciarams87 linked an issue Jun 24, 2025 that may be closed by this pull request
@ciarams87 ciarams87 force-pushed the feat/waf-policy-fetcher branch from dbe430a to 0d658ad Compare June 25, 2025 08:05
@github-project-automation github-project-automation bot moved this from 🆕 New to 🏗 In Progress in NGINX Gateway Fabric Jun 25, 2025
@ciarams87 ciarams87 marked this pull request as draft June 26, 2025 13:33
@github-actions github-actions bot added the dependencies Pull requests that update a dependency file label Jun 28, 2025
@ciarams87 ciarams87 requested review from bjee19 and salonichf5 June 28, 2025 19:56
@ciarams87 ciarams87 marked this pull request as ready for review June 28, 2025 19:57

// S3Client defines the interface for S3 operations.
//
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . S3Client
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nowadays you can just specify //go:generate go tool counterfeiter -generate once per package, and then wherever you need it, set //counterfeiter:generate . <interface-name>.

// Try to load AWS config
// Note: We don't return an error if AWS config fails - HTTP fetching should still work
var s3Client S3Client
cfg, err := config.LoadDefaultConfig(context.TODO())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use a sane context here.

Comment on lines +219 to +222
options := defaults()
for _, opt := range opts {
opt(&options)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like we should set these in the constructor, no?

Comment on lines +284 to +286
if err != nil {
return nil, fmt.Errorf("failed to fetch S3 file after retries: %w", err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the only way this gets returned is if the checksum doesn't match. If so, then this message could probably be tweaked a bit.

return false, nil //nolint:nilerr // Retry on S3 errors
}

if opts.checksumEnabled {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the validate function already checks this boolean

Comment on lines +23 to +25
if err != nil {
t.Fatalf("NewDefaultFetcher() failed: %v", err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still use the gomega library to call the matchers to check for success.

See some of our other test files for examples. (g.Expect(err).ToNot(HaveOccurred()))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a lot in this test file that can be updated to use the proper matchers.

tests := []struct {
setupServer func() *httptest.Server
setupFetcher func() Fetcher
validateFunc func(t *testing.T, data []byte, err error)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't look like this is used.

Comment on lines +50 to +53
} else {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(fileContent))
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these secondary conditions called at all in these test cases?

Comment on lines +302 to +313
// Error cases specific to parsing logic (not covered in integration tests)
{
name: "missing key with trailing slash",
url: "s3://bucket/",
expectErr: true,
},
{
name: "invalid URL encoding",
url: "s3://bucket/invalid%gg",
expectErr: true,
},
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, with how simple this test is, I wouldn't even both with the test loop and just call the function directly with these two inputs.

Comment on lines +412 to +414
data, err := fetcher.GetRemoteFile(server.URL,
WithRetryAttempts(3),
WithRetryBackoff(RetryBackoffLinear))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data, err := fetcher.GetRemoteFile(server.URL,
WithRetryAttempts(3),
WithRetryBackoff(RetryBackoffLinear))
data, err := fetcher.GetRemoteFile(
server.URL,
WithRetryAttempts(3),
WithRetryBackoff(RetryBackoffLinear,
))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same formatting suggestion elsewhere)

@ciarams87
Copy link
Contributor Author

I think I've fallen into an over engineering loop - I started out just converting s3 urls to https (no aws sdk required!), then I investigated using an aws library for this parsing, then I moved to using the s3 library with auth.

I'm going to strip the whole thing back and remove s3 protocol support altogether - we can require https protocol for s3 objects and shift that requirement to the user. In the auth story we can decide how we want to implement aws auth - we can just use http support without the sdk there too. For clarity I'm going to close this PR and start again with a simpler set of changes. Thanks for the reviews, sorry for the time waste @salonichf5 @sjberman @bjee19!!

@ciarams87 ciarams87 closed this Jul 1, 2025
@github-project-automation github-project-automation bot moved this from 🏗 In Progress to ✅ Done in NGINX Gateway Fabric Jul 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file enhancement New feature or request
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Implement Policy Fetcher implementation for WafPolicy
4 participants