diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 03a7d6c5..ec0e2159 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -105,7 +105,19 @@ jobs: run: | sudo ls /etc/cni/net.d sudo rm /etc/cni/net.d/87-podman-bridge.conflist + - name: Verify Rego file presence + run: ls -l ${{ github.workspace }}/docs/sample-rego-policies/default.rego + - name: Set Rego file path + run: echo "REGO_FILE_PATH=${{ github.workspace }}/docs/sample-rego-policies/default.rego" >> $GITHUB_ENV + - name: Start finch-daemon with opa Authz + run: sudo bin/finch-daemon --debug --enable-middleware --rego-file ${{ github.workspace }}/docs/sample-rego-policies/default.rego --skip-rego-perm-check --socket-owner $UID --socket-addr /run/finch.sock --pidfile /run/finch.pid & + - name: Run opa e2e tests + run: sudo -E make test-e2e-opa + - name: Clean up Daemon socket + run: sudo rm /run/finch.sock && sudo rm /run/finch.pid - name: Start finch-daemon run: sudo bin/finch-daemon --debug --socket-owner $UID & - name: Run e2e test run: sudo make test-e2e + - name: Clean up Daemon socket + run: sudo rm /var/run/finch.sock && sudo rm /run/finch.pid diff --git a/Makefile b/Makefile index bef8415e..695c3b5c 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,15 @@ test-e2e: linux TEST_E2E=1 \ $(GINKGO) $(GFLAGS) ./e2e/... +.PHONY: test-e2e-opa +test-e2e-opa: linux + DOCKER_HOST="unix:///run/finch.sock" \ + DOCKER_API_VERSION="v1.41" \ + MIDDLEWARE_E2E=1 \ + TEST_E2E=0 \ + DAEMON_ROOT="$(BIN)/finch-daemon" \ + $(GINKGO) $(GFLAGS) ./e2e/... + .PHONY: licenses licenses: PATH=$(BIN):$(PATH) go-licenses report --template="scripts/third-party-license.tpl" --ignore github.com/runfinch ./... > THIRD_PARTY_LICENSES diff --git a/api/router/router.go b/api/router/router.go index 65066929..d95ca9d4 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -5,6 +5,7 @@ package router import ( "context" + "errors" "fmt" "net/http" "os" @@ -15,6 +16,7 @@ import ( "github.com/moby/moby/api/server/httputils" "github.com/moby/moby/api/types/versions" + "github.com/open-policy-agent/opa/v1/rego" "github.com/runfinch/finch-daemon/api/handlers/builder" "github.com/runfinch/finch-daemon/api/handlers/container" "github.com/runfinch/finch-daemon/api/handlers/distribution" @@ -30,6 +32,14 @@ import ( "github.com/runfinch/finch-daemon/version" ) +var errRego = errors.New("error in rego policy file") +var errInput = errors.New("error in HTTP request") + +type inputRegoRequest struct { + Method string + Path string +} + // Options defines the router options to be passed into the handlers. type Options struct { Config *config.Config @@ -41,6 +51,7 @@ type Options struct { VolumeService volume.Service ExecService exec.Service DistributionService distribution.Service + RegoFilePath string // NerdctlWrapper wraps the interactions with nerdctl to build NerdctlWrapper *backend.NerdctlWrapper @@ -48,9 +59,16 @@ type Options struct { // New creates a new router and registers the handlers to it. Returns a handler object // The struct definitions of the HTTP responses come from https://github.com/moby/moby/tree/master/api/types. -func New(opts *Options) http.Handler { +func New(opts *Options) (http.Handler, error) { r := mux.NewRouter() r.Use(VersionMiddleware) + if opts.RegoFilePath != "" { + regoMiddleware, err := CreateRegoMiddleware(opts.RegoFilePath) + if err != nil { + return nil, err + } + r.Use(regoMiddleware) + } vr := types.VersionedRouter{Router: r} logger := flog.NewLogrus() @@ -62,7 +80,7 @@ func New(opts *Options) http.Handler { volume.RegisterHandlers(vr, opts.VolumeService, opts.Config, logger) exec.RegisterHandlers(vr, opts.ExecService, opts.Config, logger) distribution.RegisterHandlers(vr, opts.DistributionService, opts.Config, logger) - return ghandlers.LoggingHandler(os.Stderr, r) + return ghandlers.LoggingHandler(os.Stderr, r), nil } // VersionMiddleware checks for the requested version of the api and makes sure it falls within the bounds @@ -90,3 +108,55 @@ func VersionMiddleware(next http.Handler) http.Handler { next.ServeHTTP(w, newReq) }) } + +// CreateRegoMiddleware dynamically parses the rego file at the path specified in options +// and allows or denies the request based on the policy. +// Will return a nil function and an error if the given file path is blank or invalid. +func CreateRegoMiddleware(regoFilePath string) (func(next http.Handler) http.Handler, error) { + if regoFilePath == "" { + return nil, errRego + } + + query := "data.finch.authz.allow" + nr := rego.New( + rego.Load([]string{regoFilePath}, nil), + rego.Query(query), + ) + + preppedQuery, err := nr.PrepareForEval(context.Background()) + if err != nil { + return nil, err + } + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + input := inputRegoRequest{ + Method: r.Method, + Path: r.URL.Path, + } + + fmt.Printf("[OPA Debug] Input being evaluated: Method=%s, Path=%s\n", input.Method, input.Path) + fmt.Printf("[OPA Debug] Query being executed: %s\n", query) + + rs, err := preppedQuery.Eval(r.Context(), rego.EvalInput(input)) + if err != nil { + fmt.Printf("[OPA Error] Policy evaluation failed: %v\n", err) + response.SendErrorResponse(w, http.StatusInternalServerError, errInput) + return + } + + fmt.Printf("[OPA Debug] Evaluation results: %+v\n", rs) + fmt.Printf("[OPA Debug] Number of results: %d\n", len(rs)) + + if !rs.Allowed() { + fmt.Printf("[OPA Denied] Request denied: Method=%s, Path=%s\n", r.Method, r.URL.Path) + response.SendErrorResponse(w, http.StatusForbidden, + fmt.Errorf("method %s not allowed for path %s", r.Method, r.URL.Path)) + return + } + fmt.Printf("[OPA Allowed] Request allowed: Method=%s, Path=%s\n", r.Method, r.URL.Path) + newReq := r.WithContext(r.Context()) + next.ServeHTTP(w, newReq) + }) + }, nil +} diff --git a/api/router/router_test.go b/api/router/router_test.go index df4f69f8..1d05cd27 100644 --- a/api/router/router_test.go +++ b/api/router/router_test.go @@ -8,6 +8,8 @@ import ( "fmt" "net/http" "net/http/httptest" + "os" + "path/filepath" "testing" "github.com/containerd/nerdctl/v2/pkg/config" @@ -51,8 +53,9 @@ var _ = Describe("version middleware test", func() { BuilderService: nil, VolumeService: nil, NerdctlWrapper: nil, + RegoFilePath: "", } - h = New(opts) + h, _ = New(opts) rr = httptest.NewRecorder() expected = types.VersionInfo{ Platform: struct { @@ -126,3 +129,69 @@ var _ = Describe("version middleware test", func() { Expect(v).Should(Equal(expected)) }) }) + +// Unit tests for the rego handler. +var _ = Describe("rego middleware test", func() { + var ( + opts *Options + rr *httptest.ResponseRecorder + expected types.VersionInfo + sysSvc *mocks_system.MockService + regoFilePath string + ) + + BeforeEach(func() { + mockCtrl := gomock.NewController(GinkgoT()) + defer mockCtrl.Finish() + + tempDirPath := GinkgoT().TempDir() + regoFilePath = filepath.Join(tempDirPath, "authz.rego") + os.Create(regoFilePath) + + c := config.Config{} + sysSvc = mocks_system.NewMockService(mockCtrl) + opts = &Options{ + Config: &c, + SystemService: sysSvc, + } + rr = httptest.NewRecorder() + expected = types.VersionInfo{} + sysSvc.EXPECT().GetVersion(gomock.Any()).Return(&expected, nil).AnyTimes() + }) + It("should return a 200 error for calls by default", func() { + h, err := New(opts) + Expect(err).Should(BeNil()) + + req, _ := http.NewRequest(http.MethodGet, "/version", nil) + h.ServeHTTP(rr, req) + + Expect(rr).Should(HaveHTTPStatus(http.StatusOK)) + }) + + It("should return a 400 error for disallowed calls", func() { + regoPolicy := `package finch.authz +import rego.v1 + +default allow = false` + + os.WriteFile(regoFilePath, []byte(regoPolicy), 0644) + opts.RegoFilePath = regoFilePath + h, err := New(opts) + Expect(err).Should(BeNil()) + + req, _ := http.NewRequest(http.MethodGet, "/version", nil) + h.ServeHTTP(rr, req) + + Expect(rr).Should(HaveHTTPStatus(http.StatusForbidden)) + }) + + It("should return an error for poorly formed rego files", func() { + regoPolicy := `poorly formed rego file` + + os.WriteFile(regoFilePath, []byte(regoPolicy), 0644) + opts.RegoFilePath = regoFilePath + _, err := New(opts) + + Expect(err).Should(Not(BeNil())) + }) +}) diff --git a/cmd/finch-daemon/main.go b/cmd/finch-daemon/main.go index 314a83f8..68fd38be 100644 --- a/cmd/finch-daemon/main.go +++ b/cmd/finch-daemon/main.go @@ -43,12 +43,16 @@ const ( ) type DaemonOptions struct { - debug bool - socketAddr string - socketOwner int - debugAddress string - configPath string - pidFile string + debug bool + socketAddr string + socketOwner int + debugAddress string + configPath string + pidFile string + regoFilePath string + enableMiddleware bool + skipRegoPermCheck bool + regoFileLock *flock.Flock } var options = new(DaemonOptions) @@ -67,6 +71,10 @@ func main() { rootCmd.Flags().StringVar(&options.debugAddress, "debug-addr", "", "") rootCmd.Flags().StringVar(&options.configPath, "config-file", defaultConfigPath, "Daemon Config Path") rootCmd.Flags().StringVar(&options.pidFile, "pidfile", defaultPidFile, "pid file location") + rootCmd.Flags().StringVar(&options.regoFilePath, "rego-file", "", "Rego Policy Path") + rootCmd.Flags().BoolVar(&options.enableMiddleware, "enable-middleware", false, "turn on middleware for allowlisting") + rootCmd.Flags().BoolVar(&options.skipRegoPermCheck, "skip-rego-perm-check", false, "skip the rego file permission check (allows permissions more permissive than 0600)") + if err := rootCmd.Execute(); err != nil { log.Printf("got error: %v", err) log.Fatal(err) @@ -144,6 +152,10 @@ func run(options *DaemonOptions) error { logger := flog.NewLogrus() r, err := newRouter(options, logger) if err != nil { + // call regoFile cleanup function here to unlock previously locked file + if options.regoFilePath != "" { + cleanupRegoFile(options, logger) + } return fmt.Errorf("failed to create a router: %w", err) } @@ -193,6 +205,8 @@ func run(options *DaemonOptions) error { } }() + defer cleanupRegoFile(options, logger) + sdNotify(daemon.SdNotifyReady, logger) serverWg.Wait() logger.Debugln("Server stopped. Exiting...") @@ -215,8 +229,20 @@ func newRouter(options *DaemonOptions, logger *flog.Logrus) (http.Handler, error return nil, err } - opts := createRouterOptions(conf, clientWrapper, ncWrapper, logger) - return router.New(opts), nil + var regoFilePath string + if options.enableMiddleware { + regoFilePath, err = sanitizeRegoFile(options, logger) + if err != nil { + return nil, err + } + } + + opts := createRouterOptions(conf, clientWrapper, ncWrapper, logger, regoFilePath) + newRouter, err := router.New(opts) + if err != nil { + return nil, err + } + return newRouter, nil } func handleSignal(socket string, server *http.Server, logger *flog.Logrus) { diff --git a/cmd/finch-daemon/router_utils.go b/cmd/finch-daemon/router_utils.go index 0261f4c4..82d28e3f 100644 --- a/cmd/finch-daemon/router_utils.go +++ b/cmd/finch-daemon/router_utils.go @@ -7,6 +7,8 @@ import ( "errors" "fmt" "os" + "path/filepath" + "strings" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" @@ -26,6 +28,7 @@ import ( "github.com/runfinch/finch-daemon/pkg/archive" "github.com/runfinch/finch-daemon/pkg/ecc" "github.com/runfinch/finch-daemon/pkg/flog" + "github.com/sirupsen/logrus" "github.com/spf13/afero" ) @@ -90,12 +93,47 @@ func createContainerdClient(conf *config.Config) (*backend.ContainerdClientWrapp return backend.NewContainerdClientWrapper(client), nil } +// sanitizeRegoFile validates and prepares the Rego policy file for use. +// It checks validates the file, acquires a file lock, +// and sets rego file to be read-only. +func sanitizeRegoFile(options *DaemonOptions, logger *flog.Logrus) (string, error) { + if options.regoFilePath != "" { + if !options.enableMiddleware { + return "", fmt.Errorf("rego file path was provided without the --enable-middleware flag, please provide the --enable-middleware flag") // todo, can we default to setting this flag ourselves is this better UX? + } + + if err := checkRegoFileValidity(options.regoFilePath); err != nil { + return "", err + } + + if !options.skipRegoPermCheck { + fileInfo, err := os.Stat(options.regoFilePath) + if err != nil { + return "", fmt.Errorf("error checking rego file permissions: %v", err) + } + + if fileInfo.Mode().Perm() > 0600 { + return "", fmt.Errorf("rego file permissions %o are too permissive - must be no more permissive than 0600", fileInfo.Mode().Perm()) + } + logger.Debugf("rego file permissions check passed: %o", fileInfo.Mode().Perm()) + } else { + logger.Warnf("skipping rego file permission check - file may have permissions more permissive than 0600") + } + } + + if options.enableMiddleware && options.regoFilePath == "" { + return "", fmt.Errorf("rego file path not provided, please provide the policy file path using the --rego-file flag") + } + return options.regoFilePath, nil +} + // createRouterOptions creates router options by initializing all required services. func createRouterOptions( conf *config.Config, clientWrapper *backend.ContainerdClientWrapper, ncWrapper *backend.NerdctlWrapper, logger *flog.Logrus, + regoFilePath string, ) *router.Options { fs := afero.NewOsFs() tarCreator := archive.NewTarCreator(ecc.NewExecCmdCreator(), logger) @@ -112,5 +150,41 @@ func createRouterOptions( ExecService: exec.NewService(clientWrapper, logger), DistributionService: distribution.NewService(clientWrapper, ncWrapper, logger), NerdctlWrapper: ncWrapper, + RegoFilePath: regoFilePath, + } +} + +// checkRegoFileValidity verifies that the given rego file exists and has the right file extension. +func checkRegoFileValidity(regoFilePath string) error { + if _, err := os.Stat(regoFilePath); os.IsNotExist(err) { + return fmt.Errorf("provided Rego file path does not exist: %s", regoFilePath) } + + // Check if the file has a valid extension (.rego) + fileExt := strings.ToLower(filepath.Ext(regoFilePath)) + + if fileExt != ".rego" { + return fmt.Errorf("invalid file extension for Rego file. Only .rego files are supported") + } + + return nil +} + +func cleanupRegoFile(options *DaemonOptions, logger *flog.Logrus) { + if options.regoFileLock == nil { + return // Already cleaned up or nothing to clean + } + + // unlock the rego file + if err := options.regoFileLock.Unlock(); err != nil { + logrus.Errorf("failed to unlock Rego file: %v", err) + } + logger.Infof("rego file unlocked") + + // make rego file editable + if err := os.Chmod(options.regoFilePath, 0600); err != nil { + logrus.Errorf("failed to change file permissions of rego file: %v", err) + } + + options.regoFileLock = nil } diff --git a/cmd/finch-daemon/router_utils_test.go b/cmd/finch-daemon/router_utils_test.go index c5ee9537..ddd585cd 100644 --- a/cmd/finch-daemon/router_utils_test.go +++ b/cmd/finch-daemon/router_utils_test.go @@ -4,10 +4,14 @@ package main import ( + "fmt" "os" + "path/filepath" "testing" "github.com/containerd/nerdctl/v2/pkg/config" + "github.com/gofrs/flock" + "github.com/runfinch/finch-daemon/pkg/flog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -71,3 +75,129 @@ namespace = "test_namespace" assert.Equal(t, "test_address", cfg.Address) assert.Equal(t, "test_namespace", cfg.Namespace) } + +func TestCleanupRegoFile(t *testing.T) { + tests := []struct { + name string + setupFunc func() (*DaemonOptions, *flog.Logrus, func()) + }{ + { + name: "successful cleanup", + setupFunc: func() (*DaemonOptions, *flog.Logrus, func()) { + tmpFile, err := os.CreateTemp("", "test.rego") + require.NoError(t, err) + + fileLock := flock.New(tmpFile.Name()) + _, err = fileLock.TryLock() + require.NoError(t, err) + + err = os.Chmod(tmpFile.Name(), 0400) + require.NoError(t, err) + + logger := flog.NewLogrus() + + cleanup := func() { + os.Remove(tmpFile.Name()) + } + + return &DaemonOptions{ + regoFilePath: tmpFile.Name(), + regoFileLock: fileLock, + }, logger, cleanup + }, + }, + { + name: "nil lock handle", + setupFunc: func() (*DaemonOptions, *flog.Logrus, func()) { + return &DaemonOptions{ + regoFileLock: nil, + }, flog.NewLogrus(), func() {} + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + options, logger, cleanup := tt.setupFunc() + defer cleanup() + + cleanupRegoFile(options, logger) + + if options.regoFilePath != "" { + // Verify file permissions are restored + info, err := os.Stat(options.regoFilePath) + require.NoError(t, err) + assert.Equal(t, os.FileMode(0600), info.Mode().Perm()) + } + + // Verify lock is released + assert.Nil(t, options.regoFileLock) + }) + } +} + +func TestCheckRegoFileValidity(t *testing.T) { + tests := []struct { + name string + setupFunc func() (string, func()) + expectedError string + }{ + { + name: "valid rego file", + setupFunc: func() (string, func()) { + // Create a temporary directory + tmpDir, err := os.MkdirTemp("", "rego_test") + require.NoError(t, err) + + // Create a file with .rego extension and proper content + regoPath := filepath.Join(tmpDir, "test.rego") + regoContent := `package finch.authz + +import future.keywords.if +import rego.v1 + +default allow = false +` + fmt.Println("regopath = ", regoPath) + err = os.WriteFile(regoPath, []byte(regoContent), 0600) + require.NoError(t, err) + + return regoPath, func() { + os.RemoveAll(tmpDir) + } + }, + expectedError: "", + }, + { + name: "non-existent file", + setupFunc: func() (string, func()) { + return filepath.Join(os.TempDir(), "nonexistent.rego"), func() {} + }, + expectedError: "provided Rego file path does not exist", + }, + { + name: "wrong extension", + setupFunc: func() (string, func()) { + tmpFile, err := os.CreateTemp("", "test.txt") + require.NoError(t, err) + return tmpFile.Name(), func() { os.Remove(tmpFile.Name()) } + }, + expectedError: "invalid file extension", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + filePath, cleanup := tt.setupFunc() + defer cleanup() + + err := checkRegoFileValidity(filePath) + + if tt.expectedError != "" { + assert.ErrorContains(t, err, tt.expectedError) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/docs/opa-middleware.md b/docs/opa-middleware.md new file mode 100644 index 00000000..36d579c9 --- /dev/null +++ b/docs/opa-middleware.md @@ -0,0 +1,151 @@ +# Applying OPA authz policies + +This guide provides instructions for setting up [OPA](https://github.com/open-policy-agent/opa) authz policies with the finch-daemon. Authz policies allow users to allowlist or deny certain resources based on policy rules. + +## What Is OPA Authz implementation +Open Policy Agent (OPA) is an open-source, general-purpose policy engine that enables unified, context-aware policy enforcement across the entire stack. OPA provides a high-level declarative language, Rego, for specifying policy as code and simple APIs to offload policy decision-making from your software. + +In the current implementation, users can use OPA Rego policies to filter API requests at the Daemon level. It's important to note that the current implementation only supports allowlisting of requests. This means you can specify which requests should be allowed, and all others will be denied by default. + +## Setting up a policy + +Use the [sample rego](../docs/sample-rego-policies/default.rego) policy template to build your policy rules. + +The package name must be `finch.authz`, the daemon middleware will look for the result of the `allow` key on each API call to determine wether to allow/deny the request. +An approved request will go through without any events, a rejected request will fail with status code 403 + +Example: + +The following policy blocks all API requests made to the daemon. +``` +package finch.authz + +default allow = false + +``` +`allow` can be modified based on the business requirements for example we can prevent users from creating new containers by preventing them from accessing the create API + +``` +allow if { + not (input.Method == "POST" and input.Path == "/v1.43/containers/create") +} +``` +Use the [Rego playground](https://play.openpolicyagent.org/) to fine tune your rego policies + +## Enable OPA Middleware + +Once you are ready with your policy document, use the `--enable-middleware` flag to tell the finch-daemon to enable the OPA middleware. The daemon will then look for the policy document provided by the `--rego-file` flag. + +Note: The `--rego-file` flag is required when `--enable-middleware` is set. + +The daemon enforces strict permissions (0600 or more restrictive) on the Rego policy file to prevent unauthorized modifications. You can bypass this check using the `--skip-rego-perm-check` flag. + +Examples: + +Standard secure usage: +```bash +sudo bin/finch-daemon --debug --socket-owner $UID --socket-addr /run/finch-test.sock --pidfile /run/finch-test.pid --enable-middleware --rego-file /path/to/policy.rego +``` + +With permission check bypassed: +```bash +sudo bin/finch-daemon --debug --socket-owner $UID --socket-addr /run/finch-test.sock --pidfile /run/finch-test.pid --enable-middleware --rego-file /path/to/policy.rego --skip-rego-perm-check +``` + + +# Best practices for secure rego policies + +## Comprehensive API Path Protection + +When writing Rego policies, use pattern matching for API paths to prevent unauthorized access. Simple string matching can be bypassed by adding prefixes to API paths. + +Consider this potentially vulnerable policy that tries to restrict access to a specific container: +``` +# INCORRECT: Can be bypassed +allow if { + not (input.Path == "/v1.43/containers/sensitive-container/json") +} +``` +This policy can be bypassed in multiple ways: +1. Using container ID instead of name: `/v1.43/containers/abc123.../json` +2. Adding path prefixes: `/custom/v1.43/containers/sensitive-container/json` + +Follow the path matching best practices below to properly secure your resources. + +## Path Matching Best Practices + +``` +package finch.authz + +import future.keywords.if +import rego.v1 + +# Use pattern matching for comprehensive path protection +is_container_api if { + glob.match("/*/containers/*", [], input.Path) +} + +is_container_create if { + input.Method == "POST" + glob.match("/*/containers/create", [], input.Path) +} + +# Protect against path variations +allow if { + not is_container_api # Blocks all container-related paths + not is_container_create # Specifically blocks container creation +} +``` +Use these [example policies](https://github.com/open-policy-agent/opa-docker-authz/blob/2c7eb5c729fca70a3e5cda6f15c2d9cc121b9481/example.rego) to build your opa policy + +Remember that only `Method` and `Path` is the only values that +gets passed to the opa middleware. + + +### Common Security Pitfalls + +- **Incomplete Path Matching**: Always use pattern matching functions like glob.match() instead of exact string matching to catch path variations. +- **Missing HTTP Methods**: Consider all HTTP methods that could access a resource (GET, POST, PUT, DELETE). +- **Alternative API Endpoints**: Be aware that some operations can be performed through multiple endpoints. + +### Monitoring and Alerting +The finch-daemon's inability to start due to policy issues could impact system operations. Implement System Service Monitoring in order to be on top of any such failures. + +### Security Recommendations +- Policy Testing + - Test policies in a non-production environment + - Use the [rego playground](https://play.openpolicyagent.org/) to test policies +- Logging and Audit + - Enable comprehensive logging of policy decisions + - Monitor for unexpected denials + + +### Critical Security Considerations: Rego Policy File Protection + +### Rego File Permissions +By default, the daemon requires the Rego policy file to have permissions no more permissive than 0600 (readable and writable only by the owner). This restriction helps prevent unauthorized modifications to the policy file. + +The `--skip-rego-perm-check` flag can be used to bypass this permission check. However, using this flag comes with significant security risks: +- More permissive file permissions could allow unauthorized users to modify the policy +- Changes to the policy file could go unnoticed +- Security controls could be weakened without proper oversight + +It is strongly recommended to: +- Avoid using `--skip-rego-perm-check` in production environments +- Always use proper file permissions (0600 or more restrictive) +- Implement additional monitoring if the flag must be used + +The Rego policy file is a critical security control. +Any user with sudo privileges can: + +- Modify the policy file to weaken security controls +- Replace the policy with a more permissive version +- Disable policy enforcement entirely + +#### Recomended Security Controls + +- Access Controls + - Restrict sudo access to specific commands +- Monitoring + - Monitor policy file changes + - Monitor daemon service status diff --git a/docs/sample-rego-policies/default.rego b/docs/sample-rego-policies/default.rego new file mode 100644 index 00000000..3d52a8c1 --- /dev/null +++ b/docs/sample-rego-policies/default.rego @@ -0,0 +1,38 @@ +package finch.authz + +import future.keywords.if +import rego.v1 + +default allow = false + +allow if { + not is_container_create + not is_networks_api + not is_swarm_api + not is_plugins +} + +is_container_create if { + input.Method == "POST" + glob.match("/**/containers/create", ["/"], input.Path) +} + +is_networks_api if { + input.Method == "GET" + glob.match("/**/networks", ["/"], input.Path) +} + +is_swarm_api if { + input.Method == "GET" + glob.match("/**/swarm", ["/"], input.Path) +} + +is_plugins if { + input.Method == "GET" + glob.match("/**/plugins", ["/"], input.Path) +} + +is_forbidden_container if { + input.Method == "GET" + glob.match("/**/container/1f576a797a486438548377124f6cb7770a5cb7c8ff6a11c069cb4128d3f59462/json", ["/"], input.Path) +} diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index c6e45491..fde29726 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -19,23 +19,36 @@ import ( "github.com/runfinch/finch-daemon/e2e/util" ) -// Subject defines which CLI the tests are run against, defaults to \"nerdctl\" in the user's PATH. -var Subject = flag.String("subject", "nerdctl", `which CLI the tests are run against, defaults to "nerdctl" in the user's PATH.`) -var SubjectPrefix = flag.String("daemon-context-subject-prefix", "", `A string which prefixes the command the tests are run against, defaults to "". This string will be split by spaces.`) -var PrefixedSubjectEnv = flag.String("daemon-context-subject-env", "", `Environment to add when running a prefixed subject, in the form of a string like "EXAMPLE=foo EXAMPLE2=bar"`) +const ( + defaultNamespace = "finch" + testE2EEnv = "TEST_E2E" + middlewareE2EEnv = "MIDDLEWARE_E2E" + opaTestDescription = "Finch Daemon OPA E2E Tests" + e2eTestDescription = "Finch Daemon Functional test" +) -func TestRun(t *testing.T) { - if os.Getenv("TEST_E2E") != "1" { - t.Skip("E2E tests skipped. Set TEST_E2E=1 to run these tests") - } +var ( + Subject = flag.String("subject", "nerdctl", `which CLI the tests are run against, defaults to "nerdctl" in the user's PATH.`) + SubjectPrefix = flag.String("daemon-context-subject-prefix", "", `A string which prefixes the command the tests are run against, defaults to "". This string will be split by spaces.`) + PrefixedSubjectEnv = flag.String("daemon-context-subject-env", "", `Environment to add when running a prefixed subject, in the form of a string like "EXAMPLE=foo EXAMPLE2=bar"`) +) - if err := parseTestFlags(); err != nil { - log.Println("failed to parse go test flags", err) - os.Exit(1) +func TestRun(t *testing.T) { + switch { + case os.Getenv(middlewareE2EEnv) == "1": + runOPATests(t) + case os.Getenv(testE2EEnv) == "1": + runE2ETests(t) + default: + t.Skip("E2E tests skipped. Set TEST_E2E=1 to run regular E2E tests or MIDDLEWARE_E2E=1 to run OPA middleware tests") } +} - opt, _ := option.New([]string{*Subject, "--namespace", "finch"}) +func createTestOption() (*option.Option, error) { + return option.New([]string{*Subject, "--namespace", defaultNamespace}) +} +func setupTestSuite(opt *option.Option) { ginkgo.SynchronizedBeforeSuite(func() []byte { tests.SetupLocalRegistry(opt) return nil @@ -43,66 +56,123 @@ func TestRun(t *testing.T) { ginkgo.SynchronizedAfterSuite(func() { tests.CleanupLocalRegistry(opt) - // clean up everything after the local registry is cleaned up command.RemoveAll(opt) }, func() {}) +} - var pOpt = option.New - if *SubjectPrefix != "" { - var modifiers []option.Modifier - if *PrefixedSubjectEnv != "" { - modifiers = append(modifiers, option.Env(strings.Split(*PrefixedSubjectEnv, " "))) - } - pOpt = util.WrappedOption(strings.Split(*SubjectPrefix, " "), modifiers...) +func runOPATests(t *testing.T) { + if err := parseTestFlags(); err != nil { + log.Fatal("failed to parse go test flags:", err) + } + + opt, err := createTestOption() + if err != nil { + log.Fatal("failed to create test option:", err) + } + + setupTestSuite(opt) + + ginkgo.Describe(opaTestDescription, func() { + tests.OpaMiddlewareTest(opt) + }) + + runTests(t, opaTestDescription) +} + +func runE2ETests(t *testing.T) { + if err := parseTestFlags(); err != nil { + log.Fatal("failed to parse go test flags:", err) + } + + opt, err := createTestOption() + if err != nil { + log.Fatal("failed to create test option:", err) } - const description = "Finch Daemon Functional test" - ginkgo.Describe(description, func() { - // functional test for container APIs - tests.ContainerCreate(opt) - tests.ContainerStart(opt) - tests.ContainerStop(opt) - tests.ContainerRestart(opt) - tests.ContainerRemove(opt) - tests.ContainerList(opt) - tests.ContainerRename(opt) - tests.ContainerStats(opt) - tests.ContainerAttach(opt) - tests.ContainerLogs(opt) - tests.ContainerKill(opt) - tests.ContainerInspect(opt) - tests.ContainerWait(opt) - tests.ContainerPause(opt) - tests.ContainerUnpause(opt) - - // functional test for volume APIs - tests.VolumeList(opt) - tests.VolumeInspect(opt) - tests.VolumeRemove(opt) - - // functional test for network APIs - tests.NetworkCreate(opt, pOpt) - tests.NetworkRemove(opt) - tests.NetworkList(opt) - tests.NetworkInspect(opt) - - // functional test for image APIs - tests.ImageRemove(opt) - tests.ImagePush(opt) - tests.ImagePull(opt) - - // functional test for system api - tests.SystemVersion(opt) - tests.SystemEvents(opt) - - // functional test for distribution api - tests.DistributionInspect(opt) + setupTestSuite(opt) + + pOpt := createPrefixedOption() + + ginkgo.Describe(e2eTestDescription, func() { + runContainerTests(opt) + runVolumeTests(opt) + runNetworkTests(opt, pOpt) + runImageTests(opt) + runSystemTests(opt) + runDistributionTests(opt) }) + runTests(t, e2eTestDescription) +} + +func createPrefixedOption() func([]string, ...option.Modifier) (*option.Option, error) { + if *SubjectPrefix == "" { + return option.New + } + + var modifiers []option.Modifier + if *PrefixedSubjectEnv != "" { + modifiers = append(modifiers, option.Env(strings.Split(*PrefixedSubjectEnv, " "))) + } + return util.WrappedOption(strings.Split(*SubjectPrefix, " "), modifiers...) +} + +func runTests(t *testing.T, description string) { gomega.RegisterFailHandler(ginkgo.Fail) ginkgo.RunSpecs(t, description) } +// functional test for container APIs. +func runContainerTests(opt *option.Option) { + tests.ContainerCreate(opt) + tests.ContainerStart(opt) + tests.ContainerStop(opt) + tests.ContainerRestart(opt) + tests.ContainerRemove(opt) + tests.ContainerList(opt) + tests.ContainerRename(opt) + tests.ContainerStats(opt) + tests.ContainerAttach(opt) + tests.ContainerLogs(opt) + tests.ContainerKill(opt) + tests.ContainerInspect(opt) + tests.ContainerWait(opt) + tests.ContainerPause(opt) +} + +// functional test for volume APIs. +func runVolumeTests(opt *option.Option) { + tests.VolumeList(opt) + tests.VolumeInspect(opt) + tests.VolumeRemove(opt) +} + +// functional test for network APIs. +func runNetworkTests(opt *option.Option, pOpt func([]string, ...option.Modifier) (*option.Option, error)) { + tests.NetworkCreate(opt, pOpt) + tests.NetworkRemove(opt) + tests.NetworkList(opt) + tests.NetworkInspect(opt) +} + +// functional test for image APIs. +func runImageTests(opt *option.Option) { + tests.ImageRemove(opt) + tests.ImagePush(opt) + tests.ImagePull(opt) +} + +// . +func runSystemTests(opt *option.Option) { + tests.SystemVersion(opt) + tests.SystemEvents(opt) +} + +// functional test for distribution api. +func runDistributionTests(opt *option.Option) { + tests.DistributionInspect(opt) +} + // parseTestFlags parses go test flags because pflag package ignores flags with '-test.' prefix // Related issues: // https://github.com/spf13/pflag/issues/63 diff --git a/e2e/tests/opa_middleware.go b/e2e/tests/opa_middleware.go new file mode 100644 index 00000000..26a4d1fb --- /dev/null +++ b/e2e/tests/opa_middleware.go @@ -0,0 +1,157 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package tests + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "os" + "os/exec" + "path/filepath" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/runfinch/common-tests/command" + "github.com/runfinch/common-tests/option" + + "github.com/runfinch/finch-daemon/api/types" + "github.com/runfinch/finch-daemon/e2e/client" +) + +// OpaMiddlewareTest tests the OPA functionality. +func OpaMiddlewareTest(opt *option.Option) { + Describe("test opa middleware functionality", func() { + var ( + uClient *http.Client + version string + wantContainerName string + containerCreateOptions types.ContainerCreateRequest + createUrl string + unimplementedUnspecifiedUrl string + unimplementedSpecifiedUrl string + ) + BeforeEach(func() { + // create a custom client to use http over unix sockets + uClient = client.NewClient(GetDockerHostUrl()) + // get the docker api version that will be tested + version = GetDockerApiVersion() + wantContainerName = fmt.Sprintf("/%s", testContainerName) + // set default container containerCreateOptions + containerCreateOptions = types.ContainerCreateRequest{} + containerCreateOptions.Image = defaultImage + createUrl = client.ConvertToFinchUrl(version, "/containers/create") + unimplementedUnspecifiedUrl = client.ConvertToFinchUrl(version, "/secrets") + unimplementedSpecifiedUrl = client.ConvertToFinchUrl(version, "/swarm") + }) + AfterEach(func() { + command.RemoveAll(opt) + }) + It("should allow GET version API request", func() { + res, err := uClient.Get(client.ConvertToFinchUrl("", "/version")) + Expect(err).ShouldNot(HaveOccurred()) + jd := json.NewDecoder(res.Body) + var v types.VersionInfo + err = jd.Decode(&v) + Expect(err).ShouldNot(HaveOccurred()) + Expect(v.Version).ShouldNot(BeNil()) + Expect(v.ApiVersion).Should(Equal("1.43")) + fmt.Println(version) + }) + + It("shold allow GET containers API request", func() { + id := command.StdoutStr(opt, "run", "-d", "--name", testContainerName, defaultImage, "sleep", "infinity") + want := []types.ContainerListItem{ + { + Id: id[:12], + Names: []string{wantContainerName}, + }, + } + + res, err := uClient.Get(client.ConvertToFinchUrl(version, "/containers/json")) + Expect(err).Should(BeNil()) + Expect(res.StatusCode).Should(Equal(http.StatusOK)) + var got []types.ContainerListItem + err = json.NewDecoder(res.Body).Decode(&got) + Expect(err).Should(BeNil()) + Expect(len(got)).Should(Equal(2)) + got = filterContainerList(got) + Expect(got).Should(ContainElements(want)) + }) + + It("shold disallow POST containers/create API request", func() { + containerCreateOptions.Cmd = []string{"echo", "hello world"} + + reqBody, err := json.Marshal(containerCreateOptions) + Expect(err).Should(BeNil()) + + fmt.Println("createUrl = ", createUrl) + res, _ := uClient.Post(createUrl, "application/json", bytes.NewReader(reqBody)) + + Expect(res.StatusCode).Should(Equal(http.StatusForbidden)) + }) + + It("should fail unimplemented API calls, fail via daemon", func() { + fmt.Println("incompatibleUrl = ", unimplementedUnspecifiedUrl) + res, _ := uClient.Get(unimplementedUnspecifiedUrl) + + Expect(res.StatusCode).Should(Equal(http.StatusNotFound)) + }) + + It("should fail non implemented API calls,even if specified in the rego file", func() { + fmt.Println("incompatibleUrl = ", unimplementedSpecifiedUrl) + res, _ := uClient.Get(unimplementedSpecifiedUrl) + + Expect(res.StatusCode).Should(Equal(http.StatusNotFound)) + }) + + // Add this test to OpaMiddlewareTest function + It("should handle rego file permissions correctly", func() { + // Create a temporary rego file with overly permissive permissions + tmpDir, err := os.MkdirTemp("", "rego_test") + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(tmpDir) + + regoPath := filepath.Join(tmpDir, "test.rego") + regoContent := []byte(`package finch.authz + default allow = false`) + + err = os.WriteFile(regoPath, regoContent, 0644) + Expect(err).NotTo(HaveOccurred()) + + // Try to start daemon with overly permissive file + cmd := exec.Command(GetFinchDaemonExe(), //nolint:gosec // G204: This is a test file with controlled inputs + "--socket-addr", "/run/test.sock", + "--pidfile", "/run/test.pid", + "--rego-file", regoPath, + "--enable-middleware") + output, err := cmd.CombinedOutput() + + // Should fail due to permissions + Expect(err).To(HaveOccurred()) + Expect(string(output)).To(ContainSubstring("rego file permissions 644 are too permissive - must be no more permissive than 0600")) + + // For the second test with skip-check: + cmd = exec.Command(GetFinchDaemonExe(), //nolint:gosec // G204: This is a test file with controlled inputs + "--socket-addr", "/run/test.sock", + "--pidfile", "/run/test.pid", + "--rego-file", regoPath, + "--enable-middleware", + "--skip-rego-perm-check") + + // Start the process in background + err = cmd.Start() + Expect(err).NotTo(HaveOccurred()) + + // Give it a moment to initialize + time.Sleep(1 * time.Second) + + // Kill the process + err = cmd.Process.Kill() + Expect(err).NotTo(HaveOccurred()) + }) + }) +} diff --git a/e2e/tests/tests.go b/e2e/tests/tests.go index 932827a0..c26f0d67 100644 --- a/e2e/tests/tests.go +++ b/e2e/tests/tests.go @@ -226,3 +226,11 @@ func GetFinchExe() string { } return finchexe } + +func GetFinchDaemonExe() string { + daemonPath := os.Getenv("DAEMON_ROOT") + if daemonPath == "" { + daemonPath = "./bin/finch-daemon" // fallback + } + return daemonPath +} diff --git a/go.mod b/go.mod index 6d58d256..14f370f2 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,29 @@ require ( google.golang.org/protobuf v1.36.6 ) +require ( + github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/agnivade/levenshtein v1.2.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect + github.com/tchap/go-patricia/v2 v2.3.2 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/yashtewari/glob-intersection v0.2.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect @@ -115,11 +138,11 @@ require ( github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-varint v0.0.7 // indirect + github.com/open-policy-agent/opa v1.1.0 github.com/opencontainers/selinux v1.11.1 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rootless-containers/bypass4netns v0.4.2 // indirect github.com/rootless-containers/rootlesskit/v2 v2.3.4 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect @@ -131,20 +154,20 @@ require ( github.com/vbatts/tar-split v0.11.6 // indirect github.com/yuchanns/srslog v1.1.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect - go.opentelemetry.io/otel v1.31.0 // indirect - go.opentelemetry.io/otel/metric v1.31.0 // indirect - go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/term v0.31.0 // indirect golang.org/x/text v0.24.0 // indirect - golang.org/x/time v0.8.0 // indirect + golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.31.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect - google.golang.org/grpc v1.69.4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.70.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 243590ac..5b746d5a 100644 --- a/go.sum +++ b/go.sum @@ -14,9 +14,23 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg= github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= +github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -86,6 +100,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v4 v4.5.1 h1:7DCIXrQjo1LKmM96YD+hLVJ2EEsyyoWxJfpdd56HLps= +github.com/dgraph-io/badger/v4 v4.5.1/go.mod h1:qn3Be0j3TfV4kPbVoK0arXCD1/nr1ftth6sbL5jxdoA= +github.com/dgraph-io/ristretto/v2 v2.1.0 h1:59LjpOJLNDULHh8MC4UaegN52lC4JnO2dITsie/Pa8I= +github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4TypQYLJZzBSAemuB4= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= @@ -100,6 +120,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -110,12 +132,18 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fluent/fluent-logger-golang v1.9.0 h1:zUdY44CHX2oIUc7VTNZc+4m+ORuO/mldQDA7czhWXEg= github.com/fluent/fluent-logger-golang v1.9.0/go.mod h1:2/HCT/jTy78yGyeNGQLGQsjF3zzzAuy6Xlk6FCMV5eU= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= +github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/getlantern/httptest v0.0.0-20161025015934-4b40f4c7e590 h1:OhyiFx+yBN30O3IHrIq+9LAEhy6o7fin21wUQxF8NiE= github.com/getlantern/httptest v0.0.0-20161025015934-4b40f4c7e590/go.mod h1:rE/jidqqHHG9sjSxC24Gd5YCfZ1AT91C2wjJ28TAOfA= github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848 h1:2MhMMVBTnaHrst6HyWFDhwQCaJ05PZuOv1bE2gN8WFY= github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848/go.mod h1:+F5GJ7qGpQ03DBtcOEyQpM30ix4BLswdaojecFtsdy8= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -129,6 +157,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -154,12 +184,15 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/flatbuffers v24.12.23+incompatible h1:ubBKR94NR4pXUCY/MUsRVzd9umNW7ht7EG9hHfS9FX8= +github.com/google/flatbuffers v24.12.23+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -172,6 +205,8 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= @@ -196,6 +231,8 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= @@ -244,10 +281,14 @@ github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7B github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/open-policy-agent/opa v1.1.0 h1:HMz2evdEMTyNqtdLjmu3Vyx06BmhNYAx67Yz3Ll9q2s= +github.com/open-policy-agent/opa v1.1.0/go.mod h1:T1pASQ1/vwfTa+e2fYcfpLCvWgYtqtiUv+IuA/dLPQs= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -269,11 +310,19 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rootless-containers/bypass4netns v0.4.2 h1:JUZcpX7VLRfDkLxBPC6fyNalJGv9MjnjECOilZIvKRc= github.com/rootless-containers/bypass4netns v0.4.2/go.mod h1:iOY28IeFVqFHnK0qkBCQ3eKzKQgSW5DtlXFQJyJMAQk= github.com/rootless-containers/rootlesskit/v2 v2.3.4 h1:EHiqqiq+ntTfdnQtIgDR3etiuqKkRCPr1qpoizJxW/E= @@ -308,6 +357,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM= +github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tinylib/msgp v1.2.0 h1:0uKB/662twsVBpYUPbokj4sTSKhWFKB7LopO2kWK8lY= github.com/tinylib/msgp v1.2.0/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= @@ -325,6 +376,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= +github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/yuchanns/srslog v1.1.0 h1:CEm97Xxxd8XpJThE0gc/XsqUGgPufh5u5MUjC27/KOk= github.com/yuchanns/srslog v1.1.0/go.mod h1:HsLjdv3XV02C3kgBW2bTyW6i88OQE+VYJZIxrPKPPak= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -333,18 +386,26 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= -go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= -go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= -go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= @@ -375,6 +436,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -455,8 +518,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -481,15 +544,18 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -516,3 +582,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=