Skip to content

Add connection to NGINX One Console #3676

New issue

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

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

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion charts/nginx-gateway-fabric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `certGenerator.ttlSecondsAfterFinished` | How long to wait after the cert generator job has finished before it is removed by the job controller. | int | `30` |
| `clusterDomain` | The DNS cluster domain of your Kubernetes cluster. | string | `"cluster.local"` |
| `gateways` | A list of Gateway objects. View https://gateway-api.sigs.k8s.io/reference/spec/#gateway for full Gateway reference. | list | `[]` |
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"config":{},"container":{"hostPorts":[],"lifecycle":{},"readinessProbe":{},"resources":{},"volumeMounts":[]},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","resolver":"","secretName":"nplus-license","skipVerify":false}}` |
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"config":{},"container":{"hostPorts":[],"lifecycle":{},"readinessProbe":{},"resources":{},"volumeMounts":[]},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","nginxOneConsole":{"dataplaneKeySecretName":"","endpointHost":"agent.connect.nginx.com","endpointPort":443,"tlsSkipVerify":false},"plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","resolver":"","secretName":"nplus-license","skipVerify":false}}` |
| `nginx.config` | The configuration for the data plane that is contained in the NginxProxy resource. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{}` |
| `nginx.container` | The container configuration for the NGINX container. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{"hostPorts":[],"lifecycle":{},"readinessProbe":{},"resources":{},"volumeMounts":[]}` |
| `nginx.container.hostPorts` | A list of HostPorts to expose on the host. This configuration allows containers to bind to a specific port on the host node, enabling external network traffic to reach the container directly through the host's IP address and port. Use this option when you need to expose container ports on the host for direct access, such as for debugging, legacy integrations, or when NodePort/LoadBalancer services are not suitable. Note: Using hostPort may have security and scheduling implications, as it ties pods to specific nodes and ports. | list | `[]` |
Expand All @@ -276,6 +276,10 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `nginx.imagePullSecret` | The name of the secret containing docker registry credentials. Secret must exist in the same namespace as the helm release. The control plane will copy this secret into any namespace where NGINX is deployed. | string | `""` |
| `nginx.imagePullSecrets` | A list of secret names containing docker registry credentials. Secrets must exist in the same namespace as the helm release. The control plane will copy these secrets into any namespace where NGINX is deployed. | list | `[]` |
| `nginx.kind` | The kind of NGINX deployment. | string | `"deployment"` |
| `nginx.nginxOneConsole` | Configuration for NGINX One Console. | object | `{"dataplaneKeySecretName":"","endpointHost":"agent.connect.nginx.com","endpointPort":443,"tlsSkipVerify":false}` |
| `nginx.nginxOneConsole.endpointHost` | The Endpoint host that the NGINX One Console telemetry metrics will be sent to. | string | `"agent.connect.nginx.com"` |
| `nginx.nginxOneConsole.endpointPort` | The endpoint port that the NGINX One Console telemetry metrics will be sent to. | int | `443` |
| `nginx.nginxOneConsole.tlsSkipVerify` | Skip TLS verification for NGINX One Console connections. | bool | `false` |
| `nginx.plus` | Is NGINX Plus image being used. | bool | `false` |
| `nginx.pod` | The pod configuration for the NGINX data plane pod. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{}` |
| `nginx.replicas` | The number of replicas of the NGINX Deployment. | int | `1` |
Expand Down
12 changes: 12 additions & 0 deletions charts/nginx-gateway-fabric/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ spec:
{{- if .Capabilities.APIVersions.Has "security.openshift.io/v1/SecurityContextConstraints" }}
- --nginx-scc={{ include "nginx-gateway.scc-name" . }}-nginx
{{- end}}
{{- if .Values.nginx.nginxOneConsole.dataplaneKeySecretName }}
- --nginx-one-dataplane-key-secret={{ .Values.nginx.nginxOneConsole.dataplaneKeySecretName }}
{{- if .Values.nginx.nginxOneConsole.endpointHost }}
- --nginx-one-telemetry-endpoint-host={{ .Values.nginx.nginxOneConsole.endpointHost }}
{{- end }}
{{- if .Values.nginx.nginxOneConsole.endpointPort }}
- --nginx-one-telemetry-endpoint-port={{ .Values.nginx.nginxOneConsole.endpointPort }}
{{- end }}
{{- if .Values.nginx.nginxOneConsole.tlsSkipVerify }}
- --nginx-one-tls-skip-verify
{{- end }}
{{- end }}
env:
- name: POD_NAMESPACE
valueFrom:
Expand Down
38 changes: 38 additions & 0 deletions charts/nginx-gateway-fabric/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,44 @@
"required": [],
"title": "kind"
},
"nginxOneConsole": {
"description": "Configuration for NGINX One Console.",
"properties": {
"dataplaneKeySecretName": {
"default": "",
"description": "Name of the secret which holds the dataplane key that is required to authenticate with the NGINX One Console.\nSecret must exist in the same namespace that the NGINX Gateway Fabric control plane is running in\n(default namespace: nginx-gateway).",
"required": [],
"title": "dataplaneKeySecretName",
"type": "string"
},
"endpointHost": {
"default": "agent.connect.nginx.com",
"description": "The Endpoint host that the NGINX One Console telemetry metrics will be sent to.",
"required": [],
"title": "endpointHost",
"type": "string"
},
"endpointPort": {
"default": 443,
"description": "The endpoint port that the NGINX One Console telemetry metrics will be sent to.",
"maximum": 65535,
"minimum": 1,
"required": [],
"title": "endpointPort",
"type": "integer"
},
"tlsSkipVerify": {
"default": false,
"description": "Skip TLS verification for NGINX One Console connections.",
"required": [],
"title": "tlsSkipVerify",
"type": "boolean"
}
},
"required": [],
"title": "nginxOneConsole",
"type": "object"
},
"plus": {
"default": false,
"description": "Is NGINX Plus image being used.",
Expand Down
21 changes: 21 additions & 0 deletions charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,27 @@ nginx:
# -- Is NGINX Plus image being used.
plus: false

# -- Configuration for NGINX One Console.
nginxOneConsole:
# Name of the secret which holds the dataplane key that is required to authenticate with the NGINX One Console.
# Secret must exist in the same namespace that the NGINX Gateway Fabric control plane is running in
# (default namespace: nginx-gateway).
dataplaneKeySecretName: ""

# -- The Endpoint host that the NGINX One Console telemetry metrics will be sent to.
endpointHost: "agent.connect.nginx.com"

# @schema
# type: integer
# minimum: 1
# maximum: 65535
# @schema
# -- The endpoint port that the NGINX One Console telemetry metrics will be sent to.
endpointPort: 443

# -- Skip TLS verification for NGINX One Console connections.
tlsSkipVerify: false

# -- The name of the secret containing docker registry credentials.
# Secret must exist in the same namespace as the helm release. The control
# plane will copy this secret into any namespace where NGINX is deployed.
Expand Down
101 changes: 71 additions & 30 deletions cmd/gateway/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"
"runtime/debug"
"strconv"
"strings"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -58,27 +57,31 @@ func createRootCommand() *cobra.Command {
func createControllerCommand() *cobra.Command {
// flag names
const (
configFlag = "config"
serviceFlag = "service"
agentTLSSecretFlag = "agent-tls-secret"
metricsDisableFlag = "metrics-disable"
metricsSecureFlag = "metrics-secure-serving"
metricsPortFlag = "metrics-port"
healthDisableFlag = "health-disable"
healthPortFlag = "health-port"
leaderElectionDisableFlag = "leader-election-disable"
leaderElectionLockNameFlag = "leader-election-lock-name"
productTelemetryDisableFlag = "product-telemetry-disable"
gwAPIExperimentalFlag = "gateway-api-experimental-features"
nginxDockerSecretFlag = "nginx-docker-secret" //nolint:gosec // not credentials
usageReportSecretFlag = "usage-report-secret"
usageReportEndpointFlag = "usage-report-endpoint"
usageReportResolverFlag = "usage-report-resolver"
usageReportSkipVerifyFlag = "usage-report-skip-verify"
usageReportClientSSLSecretFlag = "usage-report-client-ssl-secret" //nolint:gosec // not credentials
usageReportCASecretFlag = "usage-report-ca-secret" //nolint:gosec // not credentials
snippetsFiltersFlag = "snippets-filters"
nginxSCCFlag = "nginx-scc"
configFlag = "config"
serviceFlag = "service"
agentTLSSecretFlag = "agent-tls-secret"
nginxOneDataplaneKeySecretFlag = "nginx-one-dataplane-key-secret" //nolint:gosec // not credentials
nginxOneTelemetryEndpointHostFlag = "nginx-one-telemetry-endpoint-host"
nginxOneTelemetryEndpointPortFlag = "nginx-one-telemetry-endpoint-port"
nginxOneTLSSkipVerifyFlag = "nginx-one-tls-skip-verify"
metricsDisableFlag = "metrics-disable"
metricsSecureFlag = "metrics-secure-serving"
metricsPortFlag = "metrics-port"
healthDisableFlag = "health-disable"
healthPortFlag = "health-port"
leaderElectionDisableFlag = "leader-election-disable"
leaderElectionLockNameFlag = "leader-election-lock-name"
productTelemetryDisableFlag = "product-telemetry-disable"
gwAPIExperimentalFlag = "gateway-api-experimental-features"
nginxDockerSecretFlag = "nginx-docker-secret" //nolint:gosec // not credentials
usageReportSecretFlag = "usage-report-secret"
usageReportEndpointFlag = "usage-report-endpoint"
usageReportResolverFlag = "usage-report-resolver"
usageReportSkipVerifyFlag = "usage-report-skip-verify"
usageReportClientSSLSecretFlag = "usage-report-client-ssl-secret" //nolint:gosec // not credentials
usageReportCASecretFlag = "usage-report-ca-secret" //nolint:gosec // not credentials
snippetsFiltersFlag = "snippets-filters"
nginxSCCFlag = "nginx-scc"
)

// flag values
Expand All @@ -101,7 +104,19 @@ func createControllerCommand() *cobra.Command {
validator: validateResourceName,
value: agentTLSSecret,
}
nginxSCCName = stringValidatingValue{
nginxOneConsoleDataplaneKeySecretName = stringValidatingValue{
validator: validateResourceName,
}
nginxOneConsoleTelemetryEndpointHost = stringValidatingValue{
validator: validateResourceName,
value: "agent.connect.nginx.com",
}
nginxOneConsoleTelemetryEndpointPort = intValidatingValue{
validator: validateAnyPort,
value: 443,
}
nginxOneConsoleTLSSkipVerify bool
nginxSCCName = stringValidatingValue{
validator: validateResourceName,
}
disableMetrics bool
Expand Down Expand Up @@ -257,6 +272,12 @@ func createControllerCommand() *cobra.Command {
NginxDockerSecretNames: nginxDockerSecrets.values,
AgentTLSSecretName: agentTLSSecretName.value,
NGINXSCCName: nginxSCCName.value,
NginxOneConsoleTelemetryConfig: config.NginxOneConsoleTelemetryConfig{
DataplaneKeySecretName: nginxOneConsoleDataplaneKeySecretName.value,
EndpointHost: nginxOneConsoleTelemetryEndpointHost.value,
EndpointPort: nginxOneConsoleTelemetryEndpointPort.value,
EndpointTLSSkipVerify: nginxOneConsoleTLSSkipVerify,
},
}

if err := controller.StartManager(conf); err != nil {
Expand Down Expand Up @@ -304,6 +325,32 @@ func createControllerCommand() *cobra.Command {
`NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway).`,
)

cmd.Flags().Var(
&nginxOneConsoleDataplaneKeySecretName,
nginxOneDataplaneKeySecretFlag,
`The name of the Secret containing the NGINX One Console's dataplane key. Must exist in the same namespace that `+
`the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway).`,
)

cmd.Flags().Var(
&nginxOneConsoleTelemetryEndpointHost,
nginxOneTelemetryEndpointHostFlag,
`The host of the NGINX One Console's telemetry endpoint.`,
)

cmd.Flags().Var(
&nginxOneConsoleTelemetryEndpointPort,
nginxOneTelemetryEndpointPortFlag,
`The port of the NGINX One Console's telemetry endpoint.`,
)

cmd.Flags().BoolVar(
&nginxOneConsoleTLSSkipVerify,
nginxOneTLSSkipVerifyFlag,
false,
"Disable client verification of the NGINX One Console's telemetry endpoint server certificate.",
)

cmd.Flags().BoolVar(
&disableMetrics,
metricsDisableFlag,
Expand Down Expand Up @@ -741,19 +788,13 @@ func createGatewayPodConfig(version, svcName string) (config.GatewayPodConfig, e
return config.GatewayPodConfig{}, err
}

// use image tag version if set, otherwise fall back to binary version
ngfVersion := version
if imageParts := strings.Split(image, ":"); len(imageParts) == 2 {
ngfVersion = imageParts[1]
}

c := config.GatewayPodConfig{
ServiceName: svcName,
Namespace: ns,
Name: name,
UID: podUID,
InstanceName: instance,
Version: ngfVersion,
Version: version,
Image: image,
}

Expand Down
74 changes: 65 additions & 9 deletions cmd/gateway/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ func TestControllerCmdFlagValidation(t *testing.T) {
"--usage-report-client-ssl-secret=client-secret",
"--snippets-filters",
"--nginx-scc=nginx-sscc-name",
"--nginx-one-dataplane-key-secret=dataplane-key-secret",
"--nginx-one-telemetry-endpoint-host=telemetry-endpoint-host",
"--nginx-one-telemetry-endpoint-port=443",
"--nginx-one-tls-skip-verify",
},
wantErr: false,
},
Expand Down Expand Up @@ -426,6 +430,66 @@ func TestControllerCmdFlagValidation(t *testing.T) {
wantErr: true,
expectedErrPrefix: `invalid argument "!@#$" for "--nginx-scc" flag: invalid format: `,
},
{
name: "nginx-one-dataplane-key-secret is set to empty string",
args: []string{
"--nginx-one-dataplane-key-secret=",
},
wantErr: true,
expectedErrPrefix: `invalid argument "" for "--nginx-one-dataplane-key-secret" flag: must be set`,
},
{
name: "nginx-one-dataplane-key-secret is invalid",
args: []string{
"--nginx-one-dataplane-key-secret=!@#$",
},
wantErr: true,
expectedErrPrefix: `invalid argument "!@#$" for "--nginx-one-dataplane-key-secret" flag: invalid format: `,
},
{
name: "nginx-one-telemetry-endpoint-host is set to empty string",
args: []string{
"--nginx-one-telemetry-endpoint-host=",
},
wantErr: true,
expectedErrPrefix: `invalid argument "" for "--nginx-one-telemetry-endpoint-host" flag: must be set`,
},
{
name: "nginx-one-telemetry-endpoint-host is invalid",
args: []string{
"--nginx-one-telemetry-endpoint-host=!@#$",
},
wantErr: true,
expectedErrPrefix: `invalid argument "!@#$" for "--nginx-one-telemetry-endpoint-host" ` +
`flag: invalid format: `,
},
{
name: "nginx-one-telemetry-endpoint-port is invalid type",
args: []string{
"--nginx-one-telemetry-endpoint-port=invalid", // not an int
},
wantErr: true,
expectedErrPrefix: `invalid argument "invalid" for "--nginx-one-telemetry-endpoint-port" ` +
`flag: failed to parse int value: strconv.ParseInt: parsing "invalid": invalid syntax`,
},
{
name: "nginx-one-telemetry-endpoint-port is outside of range",
args: []string{
"--nginx-one-telemetry-endpoint-port=65536", // outside of range
},
wantErr: true,
expectedErrPrefix: `invalid argument "65536" for "--nginx-one-telemetry-endpoint-port" flag:` +
` port outside of valid port range [1 - 65535]: 65536`,
},
{
name: "nginx-one-tls-skip-verify is not a bool",
expectedErrPrefix: `invalid argument "not-a-bool" for "--nginx-one-tls-skip-verify" flag:` +
` strconv.ParseBool: parsing "not-a-bool": invalid syntax`,
args: []string{
"--nginx-one-tls-skip-verify=not-a-bool",
},
wantErr: true,
},
}

// common flags validation is tested separately
Expand Down Expand Up @@ -753,21 +817,13 @@ func TestCreateGatewayPodConfig(t *testing.T) {
Name: "my-pod",
UID: "1234",
InstanceName: "my-pod-xyz",
Version: "tag",
Version: "0.0.0",
Image: "my-pod-image:tag",
}
cfg, err := createGatewayPodConfig(version, "svc")
g.Expect(err).To(Not(HaveOccurred()))
g.Expect(cfg).To(Equal(expCfg))

// unset image tag and use provided version
g.Expect(os.Setenv("IMAGE_NAME", "my-pod-image")).To(Succeed())
expCfg.Version = version
expCfg.Image = "my-pod-image"
cfg, err = createGatewayPodConfig(version, "svc")
g.Expect(err).To(Not(HaveOccurred()))
g.Expect(cfg).To(Equal(expCfg))

// unset image name
g.Expect(os.Unsetenv("IMAGE_NAME")).To(Succeed())
cfg, err = createGatewayPodConfig(version, "svc")
Expand Down
9 changes: 9 additions & 0 deletions cmd/gateway/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,15 @@ func validatePort(port int) error {
return nil
}

// validateAnyPort makes sure a given port is inside the valid range for all ports.
// This includes protected ports (1-1023) and unprivileged ports (1024-65535).
func validateAnyPort(port int) error {
if port < 1 || port > 65535 {
return fmt.Errorf("port outside of valid port range [1 - 65535]: %v", port)
}
return nil
}

// ensureNoPortCollisions checks if the same port has been defined multiple times.
func ensureNoPortCollisions(ports ...int) error {
seen := make(map[int]struct{})
Expand Down
Loading
Loading