diff --git a/build/Dockerfile b/build/Dockerfile index c34bd3b9e..7488be2f2 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -21,15 +21,25 @@ FROM golang:1.24-alpine@sha256:7772cb5322baa875edd74705556d08f0eeca7b9c4b5367754 ############################################# Base image for Alpine ############################################# FROM nginx:1.27.4-alpine@sha256:4ff102c5d78d254a6f0da062b3cf39eaf07f01eec0927fd21e219d0af8bc0591 AS alpine -RUN apk add --no-cache libcap libstdc++ +RUN printf "%s%s%s\n" "http://nginx.org/packages/mainline/alpine/v" `egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release` "/main" >> /etc/apk/repositories \ + && apk add --no-cache libcap libstdc++ nginx-module-otel \ + && sed -i -e '/nginx.org/d' /etc/apk/repositories ############################################# Base image for Debian ############################################# FROM nginx:1.27.4@sha256:09369da6b10306312cd908661320086bf87fbae1b6b0c49a1f50ba531fef2eab AS debian RUN apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y libcap2-bin - + && apt-get install --no-install-recommends --no-install-suggests -y \ + libcap2-bin curl gnupg2 ca-certificates lsb-release debian-archive-keyring \ + && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor > /usr/share/keyrings/nginx-archive-keyring.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ + http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" > /etc/apt/sources.list.d/nginx.list \ + && printf "%s" "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" > /etc/apt/preferences.d/99nginx \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y nginx-module-otel \ + && apt-get purge --auto-remove -y gnupg2 lsb-release curl \ + && rm -rf /var/lib/apt/lists/* /etc/apt/preferences.d/99nginx /etc/apt/sources.list.d/nginx.list ############################################# NGINX files ############################################# FROM scratch AS nginx-files @@ -109,7 +119,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \ --mount=type=bind,from=nginx-files,src=tracking.info,target=/tmp/nginx/reporting/tracking.info \ export $(cat /tmp/user_agent) \ && printf "%s\n" "https://${PACKAGE_REPO}/plus/${NGINX_PLUS_VERSION}/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ - && apk add --no-cache nginx-plus nginx-plus-module-njs nginx-plus-module-fips-check libcap libcurl \ + && apk add --no-cache nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-plus-module-fips-check libcap libcurl \ && mkdir -p /etc/nginx/reporting/ && cp -av /tmp/nginx/reporting/tracking.info /etc/nginx/reporting/tracking.info \ && ldconfig /usr/local/lib/ \ && sed -i -e '/nginx.com/d' /etc/apk/repositories @@ -151,7 +161,7 @@ RUN --mount=type=bind,from=alpine-fips-3.19,target=/tmp/fips/ \ && printf "%s\n" "https://${PACKAGE_REPO}/app-protect/${NGINX_PLUS_VERSION}/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ && printf "%s\n" "https://pkgs.nginx.com/app-protect-security-updates/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ && printf "%s\n" "https://${PACKAGE_REPO}/nginx-agent/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ - && apk add --no-cache libcap-utils libcurl nginx-plus nginx-plus-module-njs nginx-plus-module-fips-check \ + && apk add --no-cache libcap-utils libcurl nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-plus-module-fips-check \ && if [ "${NGINX_AGENT}" = "true" ]; then apk add --no-cache nginx-agent; fi \ && mkdir -p /usr/ssl \ && cp -av /tmp/fips/usr/lib/ossl-modules/fips.so /usr/lib/ossl-modules/fips.so \ @@ -187,7 +197,7 @@ RUN --mount=type=bind,from=alpine-fips-3.19,target=/tmp/fips/ \ printf "%s\n" "https://${PACKAGE_REPO}/plus/${NGINX_PLUS_VERSION}/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ && printf "%s\n" "https://${PACKAGE_REPO}/app-protect-x-plus/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ && printf "%s\n" "https://${PACKAGE_REPO}/nginx-agent/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ - && apk add --no-cache libcap-utils libcurl nginx-plus nginx-plus-module-njs nginx-plus-module-fips-check \ + && apk add --no-cache libcap-utils libcurl nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-plus-module-fips-check \ && if [ "${NGINX_AGENT}" = "true" ]; then apk add --no-cache nginx-agent; fi \ && mkdir -p /usr/ssl \ && cp -av /tmp/fips/usr/lib/ossl-modules/fips.so /usr/lib/ossl-modules/fips.so \ @@ -226,7 +236,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode && gpg --dearmor -o /usr/share/keyrings/app-protect-archive-keyring.gpg /tmp/app-protect-security-updates.key \ && cp /tmp/nginx-plus.sources /etc/apt/sources.list.d/nginx-plus.sources \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y nginx-plus nginx-plus-module-njs nginx-plus-module-fips-check \ + && apt-get install --no-install-recommends --no-install-suggests -y nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-plus-module-fips-check \ && apt-get purge --auto-remove -y gpg \ && mkdir -p /etc/nginx/reporting/ \ && cp -av /tmp/nginx/reporting/tracking.info /etc/nginx/reporting/tracking.info \ @@ -346,7 +356,7 @@ RUN --mount=type=bind,from=nginx-files,src=nginx_signing.key,target=/tmp/nginx_s printf "%s\n" "[nginx]" "name=nginx repo" \ "baseurl=https://nginx.org/packages/mainline/centos/9/\$basearch/" \ "gpgcheck=1" "enabled=1" "module_hotfixes=true" > /etc/yum.repos.d/nginx.repo \ - && microdnf --nodocs install -y nginx nginx-module-njs nginx-module-image-filter nginx-module-xslt \ + && microdnf --nodocs install -y nginx nginx-module-njs nginx-module-otel nginx-module-image-filter nginx-module-xslt \ && rm /etc/yum.repos.d/nginx.repo; \ fi \ && ubi-clean.sh @@ -368,7 +378,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode --mount=type=bind,from=nginx-files,src=tracking.info,target=/tmp/nginx/reporting/tracking.info \ mkdir -p /etc/nginx/reporting/ && cp -av /tmp/nginx/reporting/tracking.info /etc/nginx/reporting/tracking.info \ && ubi-setup.sh \ - && microdnf --nodocs install -y nginx-plus nginx-plus-module-njs nginx-plus-module-fips-check \ + && microdnf --nodocs install -y nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-plus-module-fips-check \ && ubi-clean.sh @@ -447,7 +457,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode ############################################# Base image for UBI8 with NGINX Plus and App Protect WAF ############################################# -FROM redhat/ubi8@sha256:244e9858f9d8a2792a3dceb850b4fa8fdbd67babebfde42587bfa919d5d1ecef AS ubi-8-plus-nap +FROM redhat/ubi8@sha256:8bd1b6306f8164de7fb0974031a0f903bd3ab3e6bcab835854d3d9a1a74ea5db AS ubi-8-plus-nap ARG NAP_MODULES ARG NGINX_AGENT ARG NGINX_PLUS_VERSION @@ -473,7 +483,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode && groupadd --system --gid 101 nginx \ && useradd --system --gid nginx --no-create-home --home-dir /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \ && rpm --import /tmp/nginx_signing.key \ - && dnf --nodocs install -y nginx-plus nginx-plus-module-njs nginx-plus-module-fips-check \ + && dnf --nodocs install -y nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-plus-module-fips-check \ && if [ "${NGINX_AGENT}" = "true" ]; then dnf --nodocs install -y nginx-agent; fi \ && sed -i 's/\(def in_container():\)/\1\n return False/g' /usr/lib64/python*/*-packages/rhsm/config.py \ && subscription-manager register --org=${RHEL_ORGANIZATION} --activationkey=${RHEL_ACTIVATION_KEY} || true \ @@ -520,7 +530,7 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode && groupadd --system --gid 101 nginx \ && useradd --system --gid nginx --no-create-home --home-dir /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \ && rpm --import /tmp/nginx_signing.key \ - && dnf --nodocs install -y nginx-plus nginx-plus-module-njs nginx-plus-module-fips-check \ + && dnf --nodocs install -y nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-plus-module-fips-check \ && if [ "${NGINX_AGENT}" = "true" ]; then dnf --nodocs install -y nginx-agent; fi \ ## end of duplicated code && sed -i 's/\(def in_container():\)/\1\n return False/g' /usr/lib64/python*/*-packages/rhsm/config.py \ diff --git a/internal/configs/config_params.go b/internal/configs/config_params.go index ec8d0c8a6..f4e0e99ec 100644 --- a/internal/configs/config_params.go +++ b/internal/configs/config_params.go @@ -34,6 +34,14 @@ type ConfigParams struct { MainLogFormat []string MainLogFormatEscaping string MainMainSnippets []string + MainOtelEnabled bool + MainOtelLoadModule bool + MainOtelGlobalTraceEnabled bool + MainOtelExporterEndpoint string + MainOtelExporterTrustedCA string + MainOtelExporterHeaderName string + MainOtelExporterHeaderValue string + MainOtelServiceName string MainServerNamesHashBucketSize string MainServerNamesHashMaxSize string MainStreamLogFormat []string diff --git a/internal/configs/configmaps.go b/internal/configs/configmaps.go index 91a010c75..d50976911 100644 --- a/internal/configs/configmaps.go +++ b/internal/configs/configmaps.go @@ -530,6 +530,54 @@ func ParseConfigMap(ctx context.Context, cfgm *v1.ConfigMap, nginxPlus bool, has } } + if otelExporterEndpoint, exists := cfgm.Data["otel-exporter-endpoint"]; exists { + otelExporterEndpoint = strings.TrimSpace(otelExporterEndpoint) + if otelExporterEndpoint != "" { + cfgParams.MainOtelExporterEndpoint = otelExporterEndpoint + } + } + + if otelExporterTrustedCA, exists := cfgm.Data["otel-exporter-trusted-ca"]; exists { + otelExporterTrustedCA = strings.TrimSpace(otelExporterTrustedCA) + if otelExporterTrustedCA != "" { + cfgParams.MainOtelExporterTrustedCA = otelExporterTrustedCA + } + } + + if otelExporterHeaderName, exists := cfgm.Data["otel-exporter-header-name"]; exists { + otelExporterHeaderName = strings.TrimSpace(otelExporterHeaderName) + if otelExporterHeaderName != "" { + cfgParams.MainOtelExporterHeaderName = otelExporterHeaderName + } + } + + if otelExporterHeaderValue, exists := cfgm.Data["otel-exporter-header-value"]; exists { + otelExporterHeaderValue = strings.TrimSpace(otelExporterHeaderValue) + if otelExporterHeaderValue != "" { + cfgParams.MainOtelExporterHeaderValue = otelExporterHeaderValue + } + } + + if otelServiceName, exists := cfgm.Data["otel-service-name"]; exists { + otelServiceName = strings.TrimSpace(otelServiceName) + if otelServiceName != "" { + cfgParams.MainOtelServiceName = otelServiceName + } + } + + if otelGlobalTraceEnabled, exists, err := GetMapKeyAsBool(cfgm.Data, "otel-global-trace-enabled", cfgm); exists { + if err != nil { + nl.Error(l, err) + eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, err.Error()) + configOk = false + } + cfgParams.MainOtelGlobalTraceEnabled = otelGlobalTraceEnabled + } + + if cfgParams.MainOtelExporterEndpoint != "" { + cfgParams.MainOtelLoadModule = true + } + if hasAppProtect { if appProtectFailureModeAction, exists := cfgm.Data["app-protect-failure-mode-action"]; exists { if appProtectFailureModeAction == "pass" || appProtectFailureModeAction == "drop" { @@ -913,6 +961,14 @@ func GenerateNginxMainConfig(staticCfgParams *StaticConfigParams, config *Config NginxStatus: staticCfgParams.NginxStatus, NginxStatusAllowCIDRs: staticCfgParams.NginxStatusAllowCIDRs, NginxStatusPort: staticCfgParams.NginxStatusPort, + MainOtelEnabled: config.MainOtelEnabled, + MainOtelLoadModule: config.MainOtelLoadModule, + MainOtelGlobalTraceEnabled: config.MainOtelGlobalTraceEnabled, + MainOtelExporterEndpoint: config.MainOtelExporterEndpoint, + MainOtelExporterTrustedCA: config.MainOtelExporterTrustedCA, + MainOtelExporterHeaderName: config.MainOtelExporterHeaderName, + MainOtelExporterHeaderValue: config.MainOtelExporterHeaderValue, + MainOtelServiceName: config.MainOtelServiceName, ProxyProtocol: config.ProxyProtocol, ResolverAddresses: config.ResolverAddresses, ResolverIPV6: config.ResolverIPV6, diff --git a/internal/configs/version1/config.go b/internal/configs/version1/config.go index 8b510e315..6ac8647ff 100644 --- a/internal/configs/version1/config.go +++ b/internal/configs/version1/config.go @@ -240,6 +240,14 @@ type MainConfig struct { NginxStatus bool NginxStatusAllowCIDRs []string NginxStatusPort int + MainOtelEnabled bool + MainOtelLoadModule bool + MainOtelGlobalTraceEnabled bool + MainOtelExporterEndpoint string + MainOtelExporterTrustedCA string + MainOtelExporterHeaderName string + MainOtelExporterHeaderValue string + MainOtelServiceName string ProxyProtocol bool ResolverAddresses []string ResolverIPV6 bool diff --git a/internal/configs/version1/nginx-plus.tmpl b/internal/configs/version1/nginx-plus.tmpl index 9b5a0738c..eefd0a98e 100644 --- a/internal/configs/version1/nginx-plus.tmpl +++ b/internal/configs/version1/nginx-plus.tmpl @@ -12,6 +12,9 @@ daemon off; error_log stderr {{.ErrorLogLevel}}; pid /var/lib/nginx/nginx.pid; +{{- if .MainOtelLoadModule}} +load_module modules/ngx_otel_module.so; +{{- end}} {{- if .AppProtectLoadModule}} load_module modules/ngx_http_app_protect_module.so; {{- end}} @@ -143,6 +146,25 @@ http { ssl_dhparam {{.SSLDHParam}}; {{- end}} + {{- if .MainOtelLoadModule}} + otel_exporter { + endpoint {{ .MainOtelExporterEndpoint}}; + {{- if and .MainOtelExporterHeaderName .MainOtelExporterHeaderValue }} + header {{ .MainOtelExporterHeaderName }} "{{ .MainOtelExporterHeaderValue }}"; + {{- end }} + {{- if .MainOtelExporterTrustedCA}} + # trusted_certificate ; + {{- end }} + } + + {{- if .MainOtelServiceName}} + otel_service_name {{ .MainOtelServiceName }}; + {{- end }} + {{- if .MainOtelGlobalTraceEnabled }} + otel_trace on; + {{- end}} + {{- end}} + {{ $resolverIPV6HTTPBool := boolToPointerBool .ResolverIPV6 -}} {{ makeResolver .ResolverAddresses .ResolverValid $resolverIPV6HTTPBool }} {{if .ResolverTimeout}}resolver_timeout {{.ResolverTimeout}};{{end}} diff --git a/internal/configs/version1/nginx.tmpl b/internal/configs/version1/nginx.tmpl index 5b290b381..b54acefa6 100644 --- a/internal/configs/version1/nginx.tmpl +++ b/internal/configs/version1/nginx.tmpl @@ -11,6 +11,10 @@ daemon off; error_log stderr {{.ErrorLogLevel}}; pid /var/lib/nginx/nginx.pid; +{{- if .MainOtelLoadModule}} +load_module modules/ngx_otel_module.so; +{{- end}} + {{- if .MainSnippets}} {{range $value := .MainSnippets}} {{$value}}{{end}} @@ -104,6 +108,25 @@ http { ssl_dhparam {{.SSLDHParam}}; {{- end}} + {{- if .MainOtelLoadModule}} + otel_exporter { + endpoint {{ .MainOtelExporterEndpoint}}; + {{- if and .MainOtelExporterHeaderName .MainOtelExporterHeaderValue }} + header {{ .MainOtelExporterHeaderName }} "{{ .MainOtelExporterHeaderValue }}"; + {{- end }} + {{- if .MainOtelExporterTrustedCA}} + # trusted_certificate ; + {{- end }} + } + + {{- if .MainOtelServiceName}} + otel_service_name {{ .MainOtelServiceName }}; + {{- end }} + {{- if .MainOtelGlobalTraceEnabled }} + otel_trace on; + {{- end}} + {{- end}} + server { # required to support the Websocket protocol in VirtualServer/VirtualServerRoutes set $default_connection_header "";