Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions .github/actions/smoke-tests/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ inputs:
registry-token:
description: JWT token for accessing container registry
required: false
plus-jwt:
description: JWT for NGINX Plus
required: false

outputs:
test-results-name:
Expand Down Expand Up @@ -101,6 +104,7 @@ runs:
--durations=10 \
--show-ic-logs=yes \
--ad-secret=${{ inputs.azure-ad-secret }} \
--plus-jwt=${{ inputs.plus-jwt }} \
-m ${{ inputs.marker != '' && inputs.marker || '""' }}
working-directory: ./tests
shell: bash
1 change: 1 addition & 0 deletions .github/workflows/regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ jobs:
azure-ad-secret: ${{ secrets.AZURE_AD_AUTOMATION }}
registry-token: ${{ steps.auth.outputs.access_token }}
test-image: "gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/test-runner:${{ hashFiles('./tests/requirements.txt', './tests/Dockerfile') || 'latest' }}"
plus-jwt: ${{ secrets.PLUS_JWT }}

- name: Upload Test Results
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/setup-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ jobs:
azure-ad-secret: ${{ secrets.AZURE_AD_AUTOMATION }}
registry-token: ${{ steps.auth.outputs.access_token }}
test-image: "gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/test-runner:${{ hashFiles('./tests/requirements.txt', './tests/Dockerfile') || 'latest' }}"
plus-jwt: ${{ secrets.PLUS_JWT }}
if: ${{ steps.stable_exists.outputs.exists != 'true' }}

- name: Upload Test Results
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/single-image-regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ jobs:
azure-ad-secret: ${{ secrets.AZURE_AD_AUTOMATION }}
registry-token: ${{ steps.auth.outputs.access_token }}
test-image: "gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/test-runner:${{ inputs.test-image-tag }}"
plus-jwt: ${{ secrets.PLUS_JWT }}
12 changes: 6 additions & 6 deletions build/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.6
ARG BUILD_OS=debian
ARG NGINX_PLUS_VERSION=R32
ARG NGINX_PLUS_VERSION=R33
ARG DOWNLOAD_TAG=edge
ARG DEBIAN_FRONTEND=noninteractive
ARG PREBUILT_BASE_IMG=nginx/nginx-ingress:${DOWNLOAD_TAG}
Expand Down Expand Up @@ -198,7 +198,7 @@ RUN --mount=type=bind,from=alpine-fips-3.17,target=/tmp/fips/ \
&& cp -av /tmp/fips/etc/ssl/openssl.cnf /etc/ssl/openssl.cnf \
&& cp -av /tmp/ot/usr/local/lib/libjaegertracing*so* /tmp/ot/usr/local/lib/libzipkin*so* /tmp/ot/usr/local/lib/libdd*so* /tmp/ot/usr/local/lib/libyaml*so* /usr/local/lib/ \
&& ldconfig /usr/local/lib/ \
&& apk add --no-cache app-protect-module-plus~=32.5.144 \
&& apk add --no-cache app-protect-module-plus~=33.5.210 \
&& sed -i -e '/nginx.com/d' /etc/apk/repositories \
&& nap-waf.sh \
&& if [ "${NGINX_AGENT}" = "true" ]; then \
Expand Down Expand Up @@ -279,7 +279,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode
&& if [ "${NGINX_AGENT}" = "true" ]; then agent.sh; fi \
&& if [ -z "${NAP_MODULES##*dos*}" ]; then nap-dos.sh; fi

############################################# Base image for Debian with NGINX Plus and App Protect WAFv5/DoS #############################################
############################################# Base image for Debian with NGINX Plus and App Protect WAFv5 #############################################
FROM debian-plus AS debian-plus-nap-v5
ARG NAP_MODULES
ARG NGINX_AGENT
Expand All @@ -300,7 +300,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode
&& apt-get update \
&& if [ "${NGINX_AGENT}" = "true" ]; then apt-get install --no-install-recommends --no-install-suggests -y nginx-agent; fi \
&& if [ -z "${NAP_MODULES##*waf*}" ]; then \
apt-get install --no-install-recommends --no-install-suggests -y app-protect-plugin=6.3.0* app-protect-module-plus=32+5.144* nginx-plus-module-appprotect=32+5.144*; \
apt-get install --no-install-recommends --no-install-suggests -y app-protect-module-plus=33+5.210*; \
rm -f /etc/apt/sources.list.d/app-protect.sources; \
nap-waf.sh; \
fi \
Expand Down Expand Up @@ -430,7 +430,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode
&& if [ "${NGINX_AGENT}" = "true" ]; then microdnf --nodocs install -y nginx-agent; fi \
&& if [ -z "${NAP_MODULES##*waf*}" ]; then \
cp /tmp/app-protect-9.repo /etc/yum.repos.d/app-protect-9.repo \
&& microdnf --nodocs install -y app-protect-module-plus-32+5.144* \
&& microdnf --nodocs install -y app-protect-module-plus-33+5.210* \
&& nap-waf.sh \
&& rm -f /etc/yum.repos.d/app-protect-9.repo; \
fi \
Expand Down Expand Up @@ -517,7 +517,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode
&& dnf config-manager --set-enabled codeready-builder-for-rhel-8-x86_64-rpms \
&& dnf --nodocs install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \
&& if [ -z "${NAP_MODULES##*waf*}" ]; then \
dnf --nodocs install -y app-protect-module-plus-32+5.144*; \
dnf --nodocs install -y app-protect-module-plus-33+5.210*; \
fi \
&& subscription-manager unregister \
&& if [ -z "${NAP_MODULES##*waf*}" ]; then \
Expand Down
2 changes: 1 addition & 1 deletion build/dependencies/Dockerfile.ubi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1.8
FROM nginx:1.27.2@sha256:bc5eac5eafc581aeda3008b4b1f07ebba230de2f27d47767129a6a905c84f470 AS nginx
FROM nginx:1.27.3@sha256:0c86dddac19f2ce4fd716ac58c0fd87bf69bfd4edabfd6971fb885bafd12a00b AS nginx

FROM redhat/ubi9:9.4@sha256:ee0b908e958a1822afc57e5d386d1ea128eebe492cb2e01b6903ee19c133ea75 AS rpm-build
ARG NGINX
Expand Down
21 changes: 21 additions & 0 deletions charts/nginx-ingress/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,24 @@ Expand the name of the configmap used for NGINX Agent.
{{- end -}}
{{- end -}}

{{/*
Expand the name of the mgmt configmap.
*/}}
{{- define "nginx-ingress.mgmtConfigName" -}}
{{- if .Values.controller.mgmt.customConfigMap -}}
{{ .Values.controller.mgmt.customConfigMap }}
{{- else -}}
{{- default (printf "%s-mgmt" (include "nginx-ingress.fullname" .)) -}}
{{- end -}}
{{- end -}}

{{/*
Expand license token secret name.
*/}}
{{- define "nginx-ingress.licenseTokenSecretName" -}}
{{- .Values.controller.mgmt.licenseTokenSecretName -}}
{{- end -}}

{{/*
Expand leader election lock name.
*/}}
Expand Down Expand Up @@ -226,6 +244,9 @@ Build the args for the service binary.
- -app-protect-dos-memory={{ .Values.controller.appprotectdos.memory }}
{{ end }}
- -nginx-configmaps=$(POD_NAMESPACE)/{{ include "nginx-ingress.configName" . }}
{{- if .Values.controller.nginxplus }}
- -mgmt-configmap=$(POD_NAMESPACE)/{{ include "nginx-ingress.mgmtConfigName" . }}
{{- end }}
{{- if .Values.controller.defaultTLS.secret }}
- -default-server-tls-secret={{ .Values.controller.defaultTLS.secret }}
{{ else if and (.Values.controller.defaultTLS.cert) (.Values.controller.defaultTLS.key) }}
Expand Down
19 changes: 19 additions & 0 deletions charts/nginx-ingress/templates/controller-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,22 @@ data:
nginx-agent.conf: |-
{{ include "nginx-ingress.agentConfiguration" . | indent 4 }}
{{- end }}
---
{{- if and .Values.controller.nginxplus (eq (.Values.controller.mgmt.customConfigMap | default "") "") }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "nginx-ingress.mgmtConfigName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "nginx-ingress.labels" . | nindent 4 }}
{{- if .Values.controller.config.annotations }}
annotations:
{{ toYaml .Values.controller.config.annotations | indent 4 }}
{{- end }}
data:
license-token-secret-name: {{ include "nginx-ingress.licenseTokenSecretName" . }}
{{- if hasKey .Values.controller.mgmt "enforceInitialReport" }}
enforce-initial-report: {{ quote .Values.controller.mgmt.enforceInitialReport }}
{{- end }}
{{- end }}
31 changes: 31 additions & 0 deletions charts/nginx-ingress/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,37 @@
}
]
},
"mgmt": {
"type": "object",
"default": {},
"title": "The mgmt block Schema",
"properties": {
"licenseTokenSecretName": {
"type": "string",
"default": "",
"title": "The licenseTokenSecretName Schema",
"examples": [
"nginx-plus-secret",
"license-token",
"license"
]
},
"enforceInitialReport": {
"type": "boolean",
"default": false,
"title": "The enforceInitialReport Schema",
"examples": [
true,
false
]
}
},
"examples": [
{
"licenseTokenSecretName": "license-token"
}
]
},
"nginxReloadTimeout": {
"type": "integer",
"default": 0,
Expand Down
8 changes: 8 additions & 0 deletions charts/nginx-ingress/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ controller:
## Deploys the Ingress Controller for NGINX Plus.
nginxplus: false

## Configures NGINX mgmt block for NGINX Plus
mgmt:
## Secret name of license token for NGINX Plus
licenseTokenSecretName: "license-token" # required for NGINX Plus

## Enables the 180-day grace period for sending the initial usage report
# enforceInitialReport: false

## Timeout in milliseconds which the Ingress Controller will wait for a successful NGINX reload after a change or at the initial start.
nginxReloadTimeout: 60000

Expand Down
14 changes: 14 additions & 0 deletions cmd/nginx-ingress/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
but the Ingress Controller is not able to fetch it from Kubernetes API, the Ingress Controller will fail to start.
Format: <namespace>/<name>`)

mgmtConfigMap = flag.String("mgmt-configmap", "",
`A ConfigMap resource for customizing NGINX configuration. If a ConfigMap is set,
but the Ingress Controller is not able to fetch it from Kubernetes API, the Ingress Controller will fail to start.
Format: <namespace>/<name>`)

nginxPlus = flag.Bool("nginx-plus", false, "Enable support for NGINX Plus")

appProtect = flag.Bool("enable-app-protect", false, "Enable support for NGINX App Protect. Requires -nginx-plus.")
Expand Down Expand Up @@ -258,6 +263,11 @@
*enableDynamicWeightChangesReload = false
}

if *mgmtConfigMap != "" && !*nginxPlus {
nl.Warn(l, "mgmt-configmap flag requires -nginx-plus, mgmt configmap will not be used")
*mgmtConfigMap = ""
}

Check warning on line 269 in cmd/nginx-ingress/flags.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/flags.go#L266-L269

Added lines #L266 - L269 were not covered by tests

mustValidateInitialChecks(ctx)
mustValidateWatchedNamespaces(ctx)
mustValidateFlags(ctx)
Expand Down Expand Up @@ -419,6 +429,10 @@
if *agent && !*appProtect {
nl.Fatal(l, "NGINX Agent is used to enable the Security Monitoring dashboard and requires NGINX App Protect to be enabled")
}

if *nginxPlus && *mgmtConfigMap == "" {
nl.Fatal(l, "NGINX Plus requires a mgmt ConfigMap to be set")
}

Check warning on line 435 in cmd/nginx-ingress/flags.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/flags.go#L433-L435

Added lines #L433 - L435 were not covered by tests
}

// validateNamespaceNames validates the namespaces are in the correct format
Expand Down
58 changes: 54 additions & 4 deletions cmd/nginx-ingress/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
appProtectv5BundleFolder = "/etc/app_protect/bundles/"
fatalEventFlushTime = 200 * time.Millisecond
secretErrorReason = "SecretError"
configMapErrorReason = "ConfigMapError"
)

func main() {
Expand Down Expand Up @@ -150,6 +151,14 @@

go updateSelfWithVersionInfo(ctx, eventRecorder, kubeClient, version, appProtectVersion, agentVersion, nginxVersion, 10, time.Second*5)

var mgmtCfgParams *configs.MGMTConfigParams
if *nginxPlus {
mgmtCfgParams = processMGMTConfigMap(kubeClient, configs.NewDefaultMGMTConfigParams(ctx), eventRecorder, pod)
if err := processLicenseSecret(kubeClient, nginxManager, mgmtCfgParams, controllerNamespace); err != nil {
logEventAndExit(ctx, eventRecorder, pod, secretErrorReason, err)
}

Check warning on line 159 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L154-L159

Added lines #L154 - L159 were not covered by tests
}

templateExecutor, templateExecutorV2 := createTemplateExecutors(ctx)

sslRejectHandshake, err := processDefaultServerSecret(kubeClient, nginxManager)
Expand Down Expand Up @@ -199,7 +208,7 @@
AppProtectBundlePath: appProtectBundlePath,
}

mustProcessNginxConfig(staticCfgParams, cfgParams, templateExecutor, nginxManager)
mustProcessNginxConfig(staticCfgParams, cfgParams, mgmtCfgParams, templateExecutor, nginxManager)

Check warning on line 211 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L211

Added line #L211 was not covered by tests

if *enableTLSPassthrough {
var emptyFile []byte
Expand All @@ -215,6 +224,7 @@
NginxManager: nginxManager,
StaticCfgParams: staticCfgParams,
Config: cfgParams,
MGMTCfgParams: mgmtCfgParams,

Check warning on line 227 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L227

Added line #L227 was not covered by tests
TemplateExecutor: templateExecutor,
TemplateExecutorV2: templateExecutorV2,
LatencyCollector: latencyCollector,
Expand Down Expand Up @@ -266,6 +276,7 @@
LeaderElectionLockName: *leaderElectionLockName,
WildcardTLSSecret: *wildcardTLSSecret,
ConfigMaps: *nginxConfigMaps,
MGMTConfigMap: *mgmtConfigMap,

Check warning on line 279 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L279

Added line #L279 was not covered by tests
GlobalConfiguration: *globalConfiguration,
AreCustomResourcesEnabled: *enableCustomResources,
EnableOIDC: *enableOIDC,
Expand Down Expand Up @@ -616,6 +627,22 @@
return isWildcardEnabled, nil
}

func processLicenseSecret(kubeClient *kubernetes.Clientset, nginxManager nginx.Manager, mgmtCfgParams *configs.MGMTConfigParams, controllerNamespace string) error {
licenseSecretNsName := controllerNamespace + "/" + mgmtCfgParams.Secrets.License

secret, err := getAndValidateSecret(kubeClient, licenseSecretNsName, secrets.SecretTypeLicense)
if err != nil {
return fmt.Errorf("license secret: %w", err)
}

Check warning on line 636 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L630-L636

Added lines #L630 - L636 were not covered by tests

bytes, err := configs.GenerateLicenseSecret(secret)
if err != nil {
return err
}
nginxManager.CreateSecret(configs.LicenseSecretFileName, bytes, nginx.ReadWriteOnlyFileMode)
return nil

Check warning on line 643 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L638-L643

Added lines #L638 - L643 were not covered by tests
}

func createGlobalConfigurationValidator() *cr_validation.GlobalConfigurationValidator {
forbiddenListenerPorts := map[int]bool{
80: true,
Expand All @@ -642,9 +669,9 @@

// mustProcessNginxConfig calls internally os.Exit
// if can't generate a valid NGINX config.
func mustProcessNginxConfig(staticCfgParams *configs.StaticConfigParams, cfgParams *configs.ConfigParams, templateExecutor *version1.TemplateExecutor, nginxManager nginx.Manager) {
func mustProcessNginxConfig(staticCfgParams *configs.StaticConfigParams, cfgParams *configs.ConfigParams, mgmtCfgParams *configs.MGMTConfigParams, templateExecutor *version1.TemplateExecutor, nginxManager nginx.Manager) {

Check warning on line 672 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L672

Added line #L672 was not covered by tests
l := nl.LoggerFromContext(cfgParams.Context)
ngxConfig := configs.GenerateNginxMainConfig(staticCfgParams, cfgParams)
ngxConfig := configs.GenerateNginxMainConfig(staticCfgParams, cfgParams, mgmtCfgParams)

Check warning on line 674 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L674

Added line #L674 was not covered by tests
content, err := templateExecutor.ExecuteMainConfigTemplate(ngxConfig)
if err != nil {
nl.Fatalf(l, "Error generating NGINX main config: %v", err)
Expand Down Expand Up @@ -683,14 +710,19 @@
}
secret, err = kubeClient.CoreV1().Secrets(ns).Get(context.TODO(), name, meta_v1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("could not get %v: %w", secretNsName, err)
return nil, fmt.Errorf("could not find %v: %w", secretNsName, err)

Check warning on line 713 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L713

Added line #L713 was not covered by tests
}
switch secretType {
case api_v1.SecretTypeTLS:
err = secrets.ValidateTLSSecret(secret)
if err != nil {
return nil, fmt.Errorf("%v is invalid: %w", secretNsName, err)
}
case secrets.SecretTypeLicense:
err = secrets.ValidateLicenseSecret(secret)
if err != nil {
return secret, err
}

Check warning on line 725 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L721-L725

Added lines #L721 - L725 were not covered by tests
}
return secret, nil
}
Expand Down Expand Up @@ -909,6 +941,24 @@
return cfgParams
}

func processMGMTConfigMap(kubeClient *kubernetes.Clientset, mgmtCfgParams *configs.MGMTConfigParams, eventLog record.EventRecorder, pod *api_v1.Pod) *configs.MGMTConfigParams {
ctx := mgmtCfgParams.Context
var fatalErr error

ns, name, err := k8s.ParseNamespaceName(*mgmtConfigMap)
if err != nil {
logEventAndExit(ctx, eventLog, pod, configMapErrorReason, fmt.Errorf("error parsing the mgmt-configmap argument: %w", err))
}
cfm, err := kubeClient.CoreV1().ConfigMaps(ns).Get(context.TODO(), name, meta_v1.GetOptions{})
if err != nil {
logEventAndExit(ctx, eventLog, cfm, configMapErrorReason, fmt.Errorf("error when getting mgmt-configmap [%v]: %w", *mgmtConfigMap, err))
}
if mgmtCfgParams, _, fatalErr = configs.ParseMGMTConfigMap(ctx, cfm, eventLog); fatalErr != nil {
logEventAndExit(ctx, eventLog, cfm, secretErrorReason, fatalErr)
}
return mgmtCfgParams

Check warning on line 959 in cmd/nginx-ingress/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/main.go#L944-L959

Added lines #L944 - L959 were not covered by tests
}

func updateSelfWithVersionInfo(ctx context.Context, eventLog record.EventRecorder, kubeClient *kubernetes.Clientset, version, appProtectVersion, agentVersion string, nginxVersion nginx.Version, maxRetries int, waitTime time.Duration) {
l := nl.LoggerFromContext(ctx)
podUpdated := false
Expand Down
7 changes: 7 additions & 0 deletions deployments/common/plus-mgmt-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config-mgmt
namespace: nginx-ingress
data:
license-token-secret-name: "license-token"
1 change: 1 addition & 0 deletions deployments/deployment/nginx-plus-ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ spec:
args:
- -nginx-plus
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -mgmt-configmap=$(POD_NAMESPACE)/nginx-config-mgmt
- -report-ingress-status
- -external-service=nginx-ingress
#- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
Expand Down
Loading
Loading