From 481c953f229b4fcfaeb69cc47325dd1e11b87fa2 Mon Sep 17 00:00:00 2001 From: Dragos Gabriel Ghinea <142506926+dgghinea@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:08:16 +0200 Subject: [PATCH] APIGOV-26780 - acl plugin changes (#50) * acl plugin changes * MR issues * MR fixes (by actually following the ticket this time) * gitignore change * readme updates * MR issues + config fix + default discovery yaml * readme updates * extra readme update * helm fixes + rename disabled + MR issues * remove hasACL * change info to debug * traceability helm update * remove discovery yaml file --- .gitignore | 4 +- README.md | 20 ++++---- .../traceability/kong_traceability_agent.yml | 2 +- .../templates/discovery-deployment.yaml | 13 +++-- .../templates/traceability-statefulset.yaml | 7 +-- helm/kong-agents/values.yaml | 2 + pkg/discovery/config/config.go | 50 +++++++++++-------- pkg/discovery/config/config_test.go | 12 +++-- pkg/discovery/gateway/client.go | 29 ++++++++--- pkg/discovery/subscription/access/access.go | 47 ++++++++++------- .../subscription/access/access_test.go | 42 +++++++++++++--- pkg/discovery/subscription/provision.go | 22 ++++++-- 12 files changed, 170 insertions(+), 80 deletions(-) diff --git a/.gitignore b/.gitignore index aad6325..b3cbff9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,8 @@ bin/ .run/ *.log -**/kong_discovery_agent.yml -/kong_traceability_agent.yml +pkg/discovery/main/kong_discovery_agent.yml +pkg/traceability/main/kong_traceability_agent.yml specs/ data/ diff --git a/README.md b/README.md index efc4376..a85dea7 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ The Kong agents are used to discover, provision access to, and track usages of K ## Discovery process -On startup the Kong discovery agent first validates that it is able to connect to all required services. Once connected to Kong the agent begins looking at the Plugins configured, as the ACL plugin is required for handling Amplify Central provisioning events. Then the agent will determine, from the plugins, which credential types the Kong Gateway has configured and create the Central representation of those types. +On startup the Kong discovery agent first validates that it is able to connect to all required services. Once connected to Kong the agent begins looking at the Plugins configured, more specifically for the ACL. The default option is to require having it. This can be changed from the config by disabling this check. By having the check disabled, it is assumed that access is allowed for everyone. Then the agent will determine, from the plugins, which credential types the Kong Gateway has configured and create the Central representation of those types. After that initial startup process the discovery agent begins running its main discovery loop. In this loop the agent first gets a list of all Gateway Services. With each service the agent looks for all configured routes. The agent then looks to gather the specification file, see [Specification discovery methods](#specification-discovery-methods), if found the process continues. Using the route the agent checks for plugins to determine the types of credentials to associate with it. After gathering all of this information the agent creates a new API service with the specification file and linking the appropriate credentials. The endpoints associated to the API service are constructed using the **KONG_PROXY_HOST**, **KONG_PROXY_PORTS_HTTP**, and **KONG_PROXY_PORTS_HTTPS** settings. @@ -52,7 +52,7 @@ A Marketplace application is created by a Marketplace user. When a resource with ### Access request -When a Marketplace user requests access to a resource, within the Kong environment, Central will create an AccessRequest resource in the same Kong environment. The agent receives this event and makes several changes within Kong. First the agent will add, or update, an ACL configuration on the Route being requested. This ACL will allow the Group ID created during the handling of the [Marketplace application](#marketplace-application) access to the route. Additionally, if a quota for this route has been set in Central in the product being handled the agent will add a Rate limiting plugin to reflect the quota that was set in Central for that product. Note: Quotas in Central can have a Weekly amount, this is not supported by Kong and the agent will reject the Access Request. +(Note: if the ACL plugin is not required, access request is skipped altogether). When a Marketplace user requests access to a resource, within the Kong environment, Central will create an AccessRequest resource in the same Kong environment. The agent receives this event and makes several changes within Kong. First the agent will add, or update, an ACL configuration on the Route being requested. This ACL will allow the Group ID created during the handling of the [Marketplace application](#marketplace-application) access to the route. Additionally, if a quota for this route has been set in Central in the product being handled the agent will add a Rate limiting plugin to reflect the quota that was set in Central for that product. Note: Quotas in Central can have a Weekly amount, this is not supported by Kong and the agent will reject the Access Request. ### Credential @@ -60,7 +60,7 @@ Finally, when a Marketplace user requests a credential, within the Kong environm ## Traceability process -On startup the Kong traceability agent first validates that it is able to connect to all required services. Once validation is complete the agent begins listening for log events to be sent to it. The agent receives these events and iterates through them to determine if any of the events should be sampled. If it is to be sampled the agent creates a transaction summary and leg sending that the Amplify Central. Regardless of the event being set for sampling the agent will update the proper API Metric and Usage details to be sent to Amplify Central on the interval configured. See [Usage](https://docs.axway.com/bundle/amplify-central/page/docs/connect_manage_environ/connected_agent_common_reference/traceability_usage/index.html). +On startup the Kong traceability agent first validates that it is able to connect to all required services. Once validation is complete the agent begins listening for log events to be sent to it. The agent receives these events and iterates through them to determine if any of the events should be sampled. If it is to be sampled the agent creates a transaction summary and leg sending that the Amplify Central. Regardless of the event being set for sampling the agent will update the proper API Metric and Usage details to be sent to Amplify Central on the interval configured. See [Usage](https://docs.axway.com/bundle/amplify-central/page/docs/connect_manage_environ/connected_agent_common_reference/traceability_usage/index.html). Note: if the ACL plugin is not required, the traceability agent cannot associate API traffic with a consumer application. ## Environment variables @@ -69,17 +69,19 @@ All Kong specific environment variables available are listed below | Name | Description | | -------------------------------------- | --------------------------------------------------------------------------------------------------------- | | Discovery Agent Variables | | +| **KONG_ACL_DISABLE** | Set to true to disable the check for a globally enabled ACL plugin on Kong. False by default. | | **KONG_ADMIN_URL** | The Kong admin API URL that the agent will query against | | **KONG_ADMIN_AUTH_APIKEY_HEADER** | The API Key header name the agent will use when authenticating | | **KONG_ADMIN_AUTH_APIKEY_VALUE** | The API Key value the agent will use when authenticating | | **KONG_ADMIN_AUTH_BASICAUTH_USERNAME** | The HTTP Basic username that the agent will use when authenticating | | **KONG_ADMIN_AUTH_BASICAUTH_PASSWORD** | The HTTP Basic password that the agent will use when authenticating | | **KONG_PROXY_HOST** | The proxy host that the agent will use in API Services when the Kong route does not specify hosts | -| **KONG_PROXY_PORTS_HTTP** | The HTTP port number that the agent will set for discovered APIS | -| **KONG_PROXY_PORTS_HTTPS** | The HTTPs port number that the agent will set for discovered APIS | -| **KONG_PROXY_PORTS_HTTP_DISABLE** | Set to true if the agent should ignore routes that serve over HTTP | -| **KONG_PROXY_PORTS_HTTPS_DISABLE** | Set to true if the agent should ignore routes that serve over HTTPs | +| **KONG_PROXY_PORTS_HTTP_VALUE** | The HTTP port value that the agent will set for discovered APIS | +| **KONG_PROXY_PORTS_HTTPS_VALUE** | The HTTPs port value that the agent will set for discovered APIS | +| **KONG_PROXY_PORTS_HTTP_DISABLE** | Set to true if the agent should ignore routes that serve over HTTP | +| **KONG_PROXY_PORTS_HTTPS_DISABLE** | Set to true if the agent should ignore routes that serve over HTTPs | | **KONG_PROXY_BASEPATH** | The proxy base path that will be added between the proxy host and Kong route path when building endpoints | +| **KONG_SPEC_FILTER** | The Agent SDK specific filter format for filtering out specific Kong services | | **KONG_SPEC_LOCALPATH** | The local path that the agent will look in for API definitions | | **KONG_SPEC_URLPATHS** | The URL paths that the agent will query on the gateway service for API definitions | | **KONG_SPEC_DEVPORTALENABLED** | Set to true if the agent should look for spec files in the Kong Dev Portal (default: `false`) | @@ -294,8 +296,8 @@ KONG_ADMIN_URL=https://kong.url.com:8444 KONG_ADMIN_AUTH_APIKEY_HEADER="apikey" KONG_ADMIN_AUTH_APIKEY_VALUE=123456789abcdefghijkl098765432109 KONG_PROXY_HOST=kong.proxy.endpoint.com -KONG_PROXY_PORTS_HTTP=8000 -KONG_PROXY_PORTS_HTTPS=8443 +KONG_PROXY_PORTS_HTTP_VALUE=8000 +KONG_PROXY_PORTS_HTTPS_VALUE=8443 KONG_SPEC_LOCALPATH=/specs CENTRAL_ORGANIZATIONID=123456789 diff --git a/build/traceability/kong_traceability_agent.yml b/build/traceability/kong_traceability_agent.yml index 932b449..aa70bab 100644 --- a/build/traceability/kong_traceability_agent.yml +++ b/build/traceability/kong_traceability_agent.yml @@ -5,7 +5,7 @@ kong_traceability_agent: http: path: ${KONG_LOGS_HTTP_PATH} port: ${KONG_LOGS_HTTP_PORT} - # Settings for connecting to Amplify Centralddd + # Settings for connecting to Amplify Central central: url: ${CENTRAL_URL:https://apicentral.axway.com} organizationID: ${CENTRAL_ORGANIZATIONID:""} diff --git a/helm/kong-agents/templates/discovery-deployment.yaml b/helm/kong-agents/templates/discovery-deployment.yaml index 1b6afbd..a5cc374 100644 --- a/helm/kong-agents/templates/discovery-deployment.yaml +++ b/helm/kong-agents/templates/discovery-deployment.yaml @@ -68,11 +68,13 @@ spec: env: - name: KONG_PROXY_HOST value: "{{ .Values.kong.proxy.host }}" + - name: KONG_ACL_DISABLE + value: "{{ .Values.kong.acl.disable }}" - name: KONG_PROXY_BASEPATH value: "{{ .Values.kong.proxy.basePath }}" - - name: KONG_PROXY_PORTS_HTTP + - name: KONG_PROXY_PORTS_HTTP_VALUE value: "{{ .Values.kong.proxy.ports.http.value }}" - - name: KONG_PROXY_PORTS_HTTPS + - name: KONG_PROXY_PORTS_HTTPS_VALUE value: "{{ .Values.kong.proxy.ports.https.value }}" - name: KONG_PROXY_PORTS_HTTP_DISABLE value: "{{ .Values.kong.proxy.ports.http.disable }}" @@ -120,15 +122,16 @@ spec: {{- range $key, $value := . }} {{- if and (not (eq (toString $value) "")) (not (eq (toString $key) "")) - (not (has (toString $key) (list "KONG_ADMIN_URL" + (not (has (toString $key) (list "KONG_ACL_DISABLE" + "KONG_ADMIN_URL" "KONG_ADMIN_AUTH_APIKEY_HEADER" "KONG_ADMIN_AUTH_APIKEY_VALUE" "KONG_ADMIN_AUTH_BASICAUTH_USERNAME" "KONG_ADMIN_AUTH_BASICAUTH_PASSWORD" "KONG_PROXY_HOST" "KONG_PROXY_BASEPATH" - "KONG_PROXY_PORTS_HTTP" - "KONG_PROXY_PORTS_HTTPS" + "KONG_PROXY_PORTS_HTTP_VALUE" + "KONG_PROXY_PORTS_HTTPS_VALUE" "KONG_PROXY_PORTS_HTTP_DISABLE" "KONG_PROXY_PORTS_HTTPS_DISABLE" "KONG_SPEC_LOCALPATH" diff --git a/helm/kong-agents/templates/traceability-statefulset.yaml b/helm/kong-agents/templates/traceability-statefulset.yaml index 41ef1fd..fddc2fc 100644 --- a/helm/kong-agents/templates/traceability-statefulset.yaml +++ b/helm/kong-agents/templates/traceability-statefulset.yaml @@ -78,15 +78,16 @@ spec: {{- range $key, $value := . }} {{- if and (not (eq (toString $value) "")) (not (eq (toString $key) "")) - (not (has (toString $key) (list "KONG_ADMIN_URL" + (not (has (toString $key) (list "KONG_ACL_DISABLE" + "KONG_ADMIN_URL" "KONG_ADMIN_AUTH_APIKEY_HEADER" "KONG_ADMIN_AUTH_APIKEY_VALUE" "KONG_ADMIN_AUTH_BASICAUTH_USERNAME" "KONG_ADMIN_AUTH_BASICAUTH_PASSWORD" "KONG_PROXY_HOST" "KONG_PROXY_BASEPATH" - "KONG_PROXY_PORTS_HTTP" - "KONG_PROXY_PORTS_HTTPS" + "KONG_PROXY_PORTS_HTTP_VALUE" + "KONG_PROXY_PORTS_HTTPS_VALUE" "KONG_PROXY_PORTS_HTTP_DISABLE" "KONG_PROXY_PORTS_HTTPS_DISABLE" "KONG_SPEC_LOCALPATH" diff --git a/helm/kong-agents/values.yaml b/helm/kong-agents/values.yaml index b053e29..89fbb50 100644 --- a/helm/kong-agents/values.yaml +++ b/helm/kong-agents/values.yaml @@ -21,6 +21,8 @@ fullnameOverride: "" statusPort: 8989 kong: + acl: + disable: false enable: traceability: false admin: diff --git a/pkg/discovery/config/config.go b/pkg/discovery/config/config.go index 4522da5..508d0f8 100644 --- a/pkg/discovery/config/config.go +++ b/pkg/discovery/config/config.go @@ -21,24 +21,26 @@ type props interface { } const ( - cfgKongAdminUrl = "kong.admin.url" - cfgKongAdminAPIKey = "kong.admin.auth.apiKey.value" - cfgKongAdminAPIKeyHeader = "kong.admin.auth.apiKey.header" - cfgKongAdminBasicUsername = "kong.admin.auth.basicauth.username" - cfgKongAdminBasicPassword = "kong.admin.auth.basicauth.password" - cfgKongProxyHost = "kong.proxy.host" - cfgKongProxyPortHttp = "kong.proxy.ports.http" - cfgKongProxyPortHttpDisabled = "kong.proxy.ports.http.disabled" - cfgKongProxyPortHttps = "kong.proxy.ports.https" - cfgKongProxyPortHttpsDisabled = "kong.proxy.ports.https.disabled" - cfgKongProxyBasePath = "kong.proxy.basePath" - cfgKongSpecURLPaths = "kong.spec.urlPaths" - cfgKongSpecLocalPath = "kong.spec.localPath" - cfgKongSpecFilter = "kong.spec.filter" - cfgKongSpecDevPortal = "kong.spec.devPortalEnabled" + cfgKongACLDisable = "kong.acl.disable" + cfgKongAdminUrl = "kong.admin.url" + cfgKongAdminAPIKey = "kong.admin.auth.apiKey.value" + cfgKongAdminAPIKeyHeader = "kong.admin.auth.apiKey.header" + cfgKongAdminBasicUsername = "kong.admin.auth.basicauth.username" + cfgKongAdminBasicPassword = "kong.admin.auth.basicauth.password" + cfgKongProxyHost = "kong.proxy.host" + cfgKongProxyPortHttp = "kong.proxy.ports.http.value" + cfgKongProxyPortHttpDisable = "kong.proxy.ports.http.disable" + cfgKongProxyPortHttps = "kong.proxy.ports.https.value" + cfgKongProxyPortHttpsDisable = "kong.proxy.ports.https.disable" + cfgKongProxyBasePath = "kong.proxy.basePath" + cfgKongSpecURLPaths = "kong.spec.urlPaths" + cfgKongSpecLocalPath = "kong.spec.localPath" + cfgKongSpecFilter = "kong.spec.filter" + cfgKongSpecDevPortal = "kong.spec.devPortalEnabled" ) func AddKongProperties(rootProps props) { + rootProps.AddBoolProperty(cfgKongACLDisable, false, "Disable the check for a globally enabled ACL plugin on Kong. False by default.") rootProps.AddStringProperty(cfgKongAdminUrl, "", "The Admin API url") rootProps.AddStringProperty(cfgKongAdminAPIKey, "", "API Key value to authenticate with Kong Gateway") rootProps.AddStringProperty(cfgKongAdminAPIKeyHeader, "", "API Key header to authenticate with Kong Gateway") @@ -46,9 +48,9 @@ func AddKongProperties(rootProps props) { rootProps.AddStringProperty(cfgKongAdminBasicPassword, "", "Password for basic auth to authenticate with Kong Admin API") rootProps.AddStringProperty(cfgKongProxyHost, "", "The Kong proxy endpoint") rootProps.AddIntProperty(cfgKongProxyPortHttp, 80, "The Kong proxy http port") - rootProps.AddBoolProperty(cfgKongProxyPortHttpDisabled, false, "Set to true to disable adding an http endpoint to discovered routes") + rootProps.AddBoolProperty(cfgKongProxyPortHttpDisable, false, "Set to true to disable adding an http endpoint to discovered routes") rootProps.AddIntProperty(cfgKongProxyPortHttps, 443, "The Kong proxy https port") - rootProps.AddBoolProperty(cfgKongProxyPortHttpsDisabled, false, "Set to true to disable adding an https endpoint to discovered routes") + rootProps.AddBoolProperty(cfgKongProxyPortHttpsDisable, false, "Set to true to disable adding an https endpoint to discovered routes") rootProps.AddStringProperty(cfgKongProxyBasePath, "", "The base path for the Kong proxy endpoint") rootProps.AddStringSliceProperty(cfgKongSpecURLPaths, []string{}, "URL paths that the agent will look in for spec files") rootProps.AddStringProperty(cfgKongSpecLocalPath, "", "Local paths where the agent will look for spec files") @@ -95,7 +97,7 @@ type KongPortConfig struct { type KongPortSettingsConfig struct { Value int `config:"value"` - Disable bool `config:"disabled"` + Disable bool `config:"disable"` } type KongSpecConfig struct { @@ -105,12 +107,17 @@ type KongSpecConfig struct { Filter string `config:"filter"` } +type KongACLConfig struct { + Disable bool `config:"disable"` +} + // KongGatewayConfig - represents the config for gateway type KongGatewayConfig struct { corecfg.IConfigValidator Admin KongAdminConfig `config:"admin"` Proxy KongProxyConfig `config:"proxy"` Spec KongSpecConfig `config:"spec"` + ACL KongACLConfig `config:"acl"` } const ( @@ -196,7 +203,7 @@ func invalidCredentialConfig(c *KongGatewayConfig) bool { func ParseProperties(rootProps props) *KongGatewayConfig { // Parse the config from bound properties and setup gateway config httpPortConf := KongPortSettingsConfig{ - Disable: rootProps.BoolPropertyValue(cfgKongProxyPortHttpDisabled), + Disable: rootProps.BoolPropertyValue(cfgKongProxyPortHttpDisable), Value: rootProps.IntPropertyValue(cfgKongProxyPortHttp), } if httpPortConf.Disable { @@ -204,7 +211,7 @@ func ParseProperties(rootProps props) *KongGatewayConfig { } httpsPortConf := KongPortSettingsConfig{ - Disable: rootProps.BoolPropertyValue(cfgKongProxyPortHttpsDisabled), + Disable: rootProps.BoolPropertyValue(cfgKongProxyPortHttpsDisable), Value: rootProps.IntPropertyValue(cfgKongProxyPortHttps), } if httpsPortConf.Disable { @@ -212,6 +219,9 @@ func ParseProperties(rootProps props) *KongGatewayConfig { } return &KongGatewayConfig{ + ACL: KongACLConfig{ + Disable: rootProps.BoolPropertyValue(cfgKongACLDisable), + }, Admin: KongAdminConfig{ Url: rootProps.StringPropertyValue(cfgKongAdminUrl), Auth: KongAdminAuthConfig{ diff --git a/pkg/discovery/config/config_test.go b/pkg/discovery/config/config_test.go index 6c5e7c0..99c6d4c 100644 --- a/pkg/discovery/config/config_test.go +++ b/pkg/discovery/config/config_test.go @@ -120,6 +120,7 @@ func TestKongProperties(t *testing.T) { // validate add props AddKongProperties(newProps) + assert.Contains(t, newProps.props, cfgKongACLDisable) assert.Contains(t, newProps.props, cfgKongAdminUrl) assert.Contains(t, newProps.props, cfgKongAdminAPIKey) assert.Contains(t, newProps.props, cfgKongAdminAPIKeyHeader) @@ -127,9 +128,9 @@ func TestKongProperties(t *testing.T) { assert.Contains(t, newProps.props, cfgKongAdminBasicPassword) assert.Contains(t, newProps.props, cfgKongProxyHost) assert.Contains(t, newProps.props, cfgKongProxyPortHttp) - assert.Contains(t, newProps.props, cfgKongProxyPortHttpDisabled) + assert.Contains(t, newProps.props, cfgKongProxyPortHttpDisable) assert.Contains(t, newProps.props, cfgKongProxyPortHttps) - assert.Contains(t, newProps.props, cfgKongProxyPortHttpsDisabled) + assert.Contains(t, newProps.props, cfgKongProxyPortHttpsDisable) assert.Contains(t, newProps.props, cfgKongProxyBasePath) assert.Contains(t, newProps.props, cfgKongSpecURLPaths) assert.Contains(t, newProps.props, cfgKongSpecLocalPath) @@ -138,6 +139,7 @@ func TestKongProperties(t *testing.T) { // validate defaults cfg := ParseProperties(newProps) + assert.Equal(t, false, cfg.ACL.Disable) assert.Equal(t, "", cfg.Admin.Url) assert.Equal(t, "", cfg.Admin.Auth.APIKey.Value) assert.Equal(t, "", cfg.Admin.Auth.APIKey.Header) @@ -155,6 +157,7 @@ func TestKongProperties(t *testing.T) { assert.Equal(t, false, cfg.Spec.DevPortalEnabled) // validate changed values + newProps.props[cfgKongACLDisable] = propData{"bool", "", true} newProps.props[cfgKongAdminUrl] = propData{"string", "", "http://host:port/path"} newProps.props[cfgKongAdminAPIKey] = propData{"string", "", "apikey"} newProps.props[cfgKongAdminAPIKeyHeader] = propData{"string", "", "header"} @@ -169,6 +172,7 @@ func TestKongProperties(t *testing.T) { newProps.props[cfgKongSpecFilter] = propData{"string", "", "tag_filter"} newProps.props[cfgKongSpecDevPortal] = propData{"bool", "", true} cfg = ParseProperties(newProps) + assert.Equal(t, true, cfg.ACL.Disable) assert.Equal(t, "http://host:port/path", cfg.Admin.Url) assert.Equal(t, "apikey", cfg.Admin.Auth.APIKey.Value) assert.Equal(t, "header", cfg.Admin.Auth.APIKey.Header) @@ -186,8 +190,8 @@ func TestKongProperties(t *testing.T) { assert.Equal(t, true, cfg.Spec.DevPortalEnabled) // validate no port configured when port type disabled - newProps.props[cfgKongProxyPortHttpDisabled] = propData{"bool", "", true} - newProps.props[cfgKongProxyPortHttpsDisabled] = propData{"bool", "", true} + newProps.props[cfgKongProxyPortHttpDisable] = propData{"bool", "", true} + newProps.props[cfgKongProxyPortHttpsDisable] = propData{"bool", "", true} cfg = ParseProperties(newProps) assert.Equal(t, 0, cfg.Proxy.Ports.HTTP.Value) assert.Equal(t, 0, cfg.Proxy.Ports.HTTPS.Value) diff --git a/pkg/discovery/gateway/client.go b/pkg/discovery/gateway/client.go index 86e0bf6..35df3f9 100644 --- a/pkg/discovery/gateway/client.go +++ b/pkg/discovery/gateway/client.go @@ -49,12 +49,17 @@ func NewClient(agentConfig config.AgentConfig) (*Client, error) { return nil, err } - if err := hasACLEnabledInPlugins(plugins); err != nil { + if err = hasGlobalACLEnabledInPlugins(logger, plugins, agentConfig.KongGatewayCfg.ACL.Disable); err != nil { + logger.WithError(err).Error("ACL Plugin configured as required, but none found in Kong plugins.") return nil, err } provisionLogger := log.NewFieldLogger().WithComponent("provision").WithPackage("kong") - subscription.NewProvisioner(kongClient, provisionLogger) + opts := []subscription.ProvisionerOption{} + if agentConfig.KongGatewayCfg.ACL.Disable { + opts = append(opts, subscription.WithACLDisable()) + } + subscription.NewProvisioner(kongClient, provisionLogger, opts...) return &Client{ logger: logger, @@ -67,14 +72,26 @@ func NewClient(agentConfig config.AgentConfig) (*Client, error) { }, nil } -// Returns no error in case an ACL plugin which is enabled is found -func hasACLEnabledInPlugins(plugins []*klib.Plugin) error { +func pluginIsGlobal(p *klib.Plugin) bool { + if p.Service == nil && p.Route == nil { + return true + } + return false +} + +// Returns no error in case a global ACL plugin which is enabled is found +func hasGlobalACLEnabledInPlugins(logger log.FieldLogger, plugins []*klib.Plugin, aclDisable bool) error { + if aclDisable { + logger.Warn("ACL Plugin check disabled. Assuming global access is allowed for all services.") + return nil + } for _, plugin := range plugins { - if *plugin.Name == "acl" && *plugin.Enabled { + if *plugin.Name == "acl" && *plugin.Enabled && pluginIsGlobal(plugin) { return nil } } - return fmt.Errorf("failed to find acl plugin is enabled and installed") + return fmt.Errorf("failed to find acl plugin is enabled and installed on the Kong Gateway. " + + "Enable in on the Gateway or change the config to disable this check.") } func (gc *Client) DiscoverAPIs() error { diff --git a/pkg/discovery/subscription/access/access.go b/pkg/discovery/subscription/access/access.go index 817aacc..dc9aa4e 100644 --- a/pkg/discovery/subscription/access/access.go +++ b/pkg/discovery/subscription/access/access.go @@ -29,27 +29,30 @@ type accessRequest interface { } type AccessProvisioner struct { - ctx context.Context - logger log.FieldLogger - client accessClient - quota provisioning.Quota - routeID string - appID string + ctx context.Context + logger log.FieldLogger + client accessClient + quota provisioning.Quota + routeID string + appID string + aclDisable bool } -func NewAccessProvisioner(ctx context.Context, client accessClient, request accessRequest) AccessProvisioner { +func NewAccessProvisioner(ctx context.Context, client accessClient, request accessRequest, aclDisable bool) AccessProvisioner { instDetails := request.GetInstanceDetails() routeID := sdkUtil.ToString(instDetails[common.AttrRouteID]) + logger := log.NewFieldLogger(). + WithComponent("AccessProvisioner"). + WithPackage("access") a := AccessProvisioner{ - ctx: context.Background(), - logger: log.NewFieldLogger(). - WithComponent("AccessProvisioner"). - WithPackage("access"), - client: client, - quota: request.GetQuota(), - routeID: routeID, - appID: request.GetApplicationDetailsValue(common.AttrAppID), + ctx: context.Background(), + logger: logger, + client: client, + quota: request.GetQuota(), + routeID: routeID, + appID: request.GetApplicationDetailsValue(common.AttrAppID), + aclDisable: aclDisable, } if a.routeID != "" { @@ -63,8 +66,8 @@ func NewAccessProvisioner(ctx context.Context, client accessClient, request acce func (a AccessProvisioner) Provision() (provisioning.RequestStatus, provisioning.AccessData) { a.logger.Info("provisioning access") - rs := provisioning.NewRequestStatusBuilder() + if a.appID == "" { a.logger.Error("could not find the managed application ID on the resource") return rs.SetMessage("managed application ID not found").Failed(), nil @@ -75,6 +78,11 @@ func (a AccessProvisioner) Provision() (provisioning.RequestStatus, provisioning return rs.SetMessage("route ID not found").Failed(), nil } + if a.aclDisable { + a.logger.Debug("ACL plugin check is disabled or not existing for current spec. Skipping access request provisioning") + return rs.Success(), nil + } + if a.quota != nil && a.quota.GetInterval().String() == provisioning.Weekly.String() { a.logger.Debug("weekly quota interval is not supported") return rs.SetMessage("weekly quota is not supported by kong").Failed(), nil @@ -105,8 +113,8 @@ func (a AccessProvisioner) Provision() (provisioning.RequestStatus, provisioning func (a AccessProvisioner) Deprovision() provisioning.RequestStatus { a.logger.Info("deprovisioning access") - rs := provisioning.NewRequestStatusBuilder() + if a.appID == "" { a.logger.Error("could not find the managed application ID on the resource") return rs.SetMessage("managed application ID not found").Failed() @@ -117,6 +125,11 @@ func (a AccessProvisioner) Deprovision() provisioning.RequestStatus { return rs.SetMessage("route ID not found").Failed() } + if a.aclDisable { + a.logger.Debug("ACL plugin check is disabled or not existing for current spec. Skipping access request deprovisioning") + return rs.Success() + } + err := a.client.RemoveRouteACL(a.ctx, a.routeID, a.appID) if err != nil { a.logger.WithError(err).Error("failed to remove managed app from ACL") diff --git a/pkg/discovery/subscription/access/access_test.go b/pkg/discovery/subscription/access/access_test.go index d46ad66..63085a2 100644 --- a/pkg/discovery/subscription/access/access_test.go +++ b/pkg/discovery/subscription/access/access_test.go @@ -93,9 +93,10 @@ func (q *mockQuota) GetPlanName() string { func TestProvision(t *testing.T) { cases := map[string]struct { - client mockAccessClient - request mockAccessRequest - result provisioning.Status + client mockAccessClient + request mockAccessRequest + result provisioning.Status + aclDisable bool }{ "no app id configured": { result: provisioning.Error, @@ -108,6 +109,18 @@ func TestProvision(t *testing.T) { }, result: provisioning.Error, }, + "ACL disable is active": { + request: mockAccessRequest{ + values: map[string]string{ + common.AttrAppID: "appID", + }, + details: map[string]interface{}{ + common.AttrRouteID: "routeID", + }, + }, + result: provisioning.Success, + aclDisable: true, + }, "unsupported quota interval": { request: mockAccessRequest{ values: map[string]string{ @@ -184,7 +197,7 @@ func TestProvision(t *testing.T) { t.Run(name, func(t *testing.T) { ctx := context.WithValue(context.Background(), testName, name) - result, _ := NewAccessProvisioner(ctx, tc.client, &tc.request).Provision() + result, _ := NewAccessProvisioner(ctx, tc.client, &tc.request, tc.aclDisable).Provision() assert.Equal(t, tc.result, result.GetStatus()) }) } @@ -192,9 +205,10 @@ func TestProvision(t *testing.T) { func TestDeprovision(t *testing.T) { cases := map[string]struct { - client mockAccessClient - request mockAccessRequest - result provisioning.Status + client mockAccessClient + request mockAccessRequest + result provisioning.Status + aclDisable bool }{ "no app id configured": { result: provisioning.Error, @@ -207,6 +221,18 @@ func TestDeprovision(t *testing.T) { }, result: provisioning.Error, }, + "ACL disable is active": { + request: mockAccessRequest{ + values: map[string]string{ + common.AttrAppID: "appID", + }, + details: map[string]interface{}{ + common.AttrRouteID: "routeID", + }, + }, + result: provisioning.Success, + aclDisable: true, + }, "error revoking access for managed app": { client: mockAccessClient{ removeManagedAppErr: true, @@ -248,7 +274,7 @@ func TestDeprovision(t *testing.T) { t.Run(name, func(t *testing.T) { ctx := context.WithValue(context.Background(), testName, name) - result := NewAccessProvisioner(ctx, tc.client, &tc.request).Deprovision() + result := NewAccessProvisioner(ctx, tc.client, &tc.request, tc.aclDisable).Deprovision() assert.Equal(t, tc.result, result.GetStatus()) }) } diff --git a/pkg/discovery/subscription/provision.go b/pkg/discovery/subscription/provision.go index 2576732..c340455 100644 --- a/pkg/discovery/subscription/provision.go +++ b/pkg/discovery/subscription/provision.go @@ -13,24 +13,36 @@ import ( "github.com/Axway/agents-kong/pkg/discovery/subscription/credential" ) +type ProvisionerOption func(*provisioner) + type provisioner struct { - logger log.FieldLogger - client kong.KongAPIClient + logger log.FieldLogger + client kong.KongAPIClient + aclDisable bool } // NewProvisioner creates a type to implement the SDK Provisioning methods for handling subscriptions -func NewProvisioner(client kong.KongAPIClient, logger log.FieldLogger) { +func NewProvisioner(client kong.KongAPIClient, logger log.FieldLogger, opts ...ProvisionerOption) { logger.Info("Registering provisioning callbacks") provisioner := &provisioner{ client: client, logger: logger, } + for _, o := range opts { + o(provisioner) + } agent.RegisterProvisioner(provisioner) registerOauth2() registerBasicAuth() registerKeyAuth() } +func WithACLDisable() ProvisionerOption { + return func(p *provisioner) { + p.aclDisable = true + } +} + func (p provisioner) ApplicationRequestProvision(request provisioning.ApplicationRequest) provisioning.RequestStatus { return application.NewApplicationProvisioner(context.Background(), p.client, request).Provision() } @@ -52,9 +64,9 @@ func (p provisioner) CredentialUpdate(request provisioning.CredentialRequest) (p } func (p provisioner) AccessRequestProvision(request provisioning.AccessRequest) (provisioning.RequestStatus, provisioning.AccessData) { - return access.NewAccessProvisioner(context.Background(), p.client, request).Provision() + return access.NewAccessProvisioner(context.Background(), p.client, request, p.aclDisable).Provision() } func (p provisioner) AccessRequestDeprovision(request provisioning.AccessRequest) provisioning.RequestStatus { - return access.NewAccessProvisioner(context.Background(), p.client, request).Deprovision() + return access.NewAccessProvisioner(context.Background(), p.client, request, p.aclDisable).Deprovision() }