├── CODEOWNERS ├── config ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml │ └── kustomization.yaml ├── samples │ ├── kustomization.yaml │ └── charts_v1alpha1_nginxingress.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── default │ ├── manager_config_patch.yaml │ ├── kustomization.yaml │ └── manager_auth_proxy_patch.yaml ├── manifests │ ├── kustomization.yaml │ └── bases │ │ └── kustomization.yaml ├── crd │ ├── kustomization.yaml │ ├── patches │ │ ├── cainjection_in_nginxingresscontrollers.yaml │ │ └── webhook_in_nginxingresscontrollers.yaml │ ├── kustomizeconfig.yaml │ └── bases │ │ └── charts.nginx.org_nginxingresses.yaml └── rbac │ ├── service_account.yaml │ ├── nginxingress_viewer_role.yaml │ ├── nginxingresscontroller_viewer_role.yaml │ ├── nginxingress_editor_role.yaml │ ├── auth_proxy_client_clusterrole.yaml │ ├── nginxingresscontroller_editor_role.yaml │ ├── role_binding.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_role.yaml │ ├── leader_election_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ └── role.yaml ├── examples ├── ns.yaml ├── deployment-oss-min │ ├── ns.yaml │ ├── global-configuration.yaml │ ├── nginx-ingress-controller.yaml │ └── README.md ├── deployment-plus-min │ ├── ns.yaml │ ├── global-configuration.yaml │ ├── nginx-ingress-controller.yaml │ └── README.md └── default-server-secret.yaml ├── helm-charts └── nginx-ingress │ ├── .helmignore │ ├── values-plus.yaml │ ├── values-nsm.yaml │ ├── README.md │ ├── values-icp.yaml │ ├── templates │ ├── controller-ingress-class.yaml │ ├── controller-lease.yaml │ ├── controller-leader-election-configmap.yaml │ ├── controller-secret.yaml │ ├── controller-wildcard-secret.yaml │ ├── clusterrolebinding.yaml │ ├── controller-globalconfiguration.yaml │ ├── controller-prometheus-service.yaml │ ├── NOTES.txt │ ├── controller-servicemonitor.yaml │ ├── controller-pdb.yaml │ ├── controller-serviceaccount.yaml │ ├── controller-hpa.yaml │ ├── controller-configmap.yaml │ ├── controller-service.yaml │ └── controller-daemonset.yaml │ ├── Chart.yaml │ └── crds │ ├── appprotectdos.f5.com_apdospolicy.yaml │ ├── appprotectdos.f5.com_apdoslogconfs.yaml │ ├── k8s.nginx.org_globalconfigurations.yaml │ ├── appprotect.f5.com_aplogconfs.yaml │ ├── appprotect.f5.com_apusersigs.yaml │ ├── externaldns.nginx.org_dnsendpoints.yaml │ └── appprotectdos.f5.com_dosprotectedresources.yaml ├── docs ├── images │ ├── openshift1.png │ ├── openshift2.png │ ├── openshift3.png │ └── openshift4.png ├── installation.md ├── openshift-installation.md ├── manual-installation.md ├── upgrades.md └── nginx-ingress-controller.md ├── CHANGELOG.md ├── resources ├── ingress-class.yaml ├── rbac-example.yaml ├── scc.yaml └── scc-daemonset.yaml ├── Dockerfile ├── watches.yaml ├── .gitignore ├── PROJECT ├── .github ├── workflows │ ├── labeler.yml │ ├── delete-operator-branch.yml │ ├── close-operator-pr.yml │ ├── dependabot-auto-merge.yml │ ├── dependency-review.yml │ ├── dockerhub-description.yml │ ├── stale.yml │ ├── update-openshift-versions.yml │ ├── f5-cla.yml │ ├── create-operator-pr.yml │ ├── scorecard.yml │ ├── notifications.yml │ ├── sync-chart.yml │ ├── ci.yml │ └── e2e-test.yml ├── labeler.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── release.yml └── PULL_REQUEST_TEMPLATE.md ├── bundle ├── manifests │ ├── nginx-ingress-operator-manager-config_v1_configmap.yaml │ ├── nginx-ingress-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── nginx-ingress-operator-controller-manager-metrics-service_v1_service.yaml │ ├── charts.nginx.org_nginxingresses.yaml │ └── nginx-ingress-operator-nginx-ingress-admin_rbac.authorization.k8s.io_v1_clusterrole.yaml ├── metadata │ └── annotations.yaml └── tests │ └── scorecard │ └── config.yaml ├── tests └── nginx-ingress-controller-oss.yaml ├── SECURITY.md ├── bundle.Dockerfile ├── ISSUE_LIFECYCLE.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── scripts └── update-openshift-versions.sh ├── README.md └── renovate.json /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @nginx/kic 2 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /examples/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: nginx-ingress 5 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | *.png 3 | -------------------------------------------------------------------------------- /examples/deployment-oss-min/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: my-nginx-ingress -------------------------------------------------------------------------------- /examples/deployment-plus-min/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: my-nginx-ingress 5 | -------------------------------------------------------------------------------- /docs/images/openshift1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/HEAD/docs/images/openshift1.png -------------------------------------------------------------------------------- /docs/images/openshift2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/HEAD/docs/images/openshift2.png -------------------------------------------------------------------------------- /docs/images/openshift3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/HEAD/docs/images/openshift3.png -------------------------------------------------------------------------------- /docs/images/openshift4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/HEAD/docs/images/openshift4.png -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/values-plus.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | name: controller 3 | nginxplus: true 4 | image: 5 | repository: nginx-plus-ingress 6 | tag: "5.3.1" 7 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/values-nsm.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | name: controller 3 | enableLatencyMetrics: true 4 | nginxServiceMesh: 5 | enable: true 6 | enableEgress: true 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | An automatically generated list of changes can be found on the [GitHub Releases page](https://github.com/nginx/nginx-ingress-helm-operator/releases). 4 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /examples/deployment-oss-min/global-configuration.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: k8s.nginx.org/v1alpha1 2 | kind: GlobalConfiguration 3 | metadata: 4 | name: nginx-configuration 5 | namespace: my-nginx-ingress 6 | -------------------------------------------------------------------------------- /examples/deployment-plus-min/global-configuration.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: k8s.nginx.org/v1alpha1 2 | kind: GlobalConfiguration 3 | metadata: 4 | name: nginx-configuration 5 | namespace: my-nginx-ingress 6 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - charts_v1alpha1_nginxingress.yaml 4 | #+kubebuilder:scaffold:manifestskustomizesamples 5 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/README.md: -------------------------------------------------------------------------------- 1 | # Helm Documentation 2 | 3 | Please refer to the [Installation with Helm](https://docs.nginx.com/nginx-ingress-controller/install/helm/) guide in the NGINX Ingress Controller documentation site. 4 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | images: 6 | - name: controller 7 | newName: quay.io/nginx/nginx-ingress-operator 8 | newTag: 3.4.1 9 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | -------------------------------------------------------------------------------- /resources/ingress-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: IngressClass 3 | metadata: 4 | name: nginx 5 | # annotations: 6 | # ingressclass.kubernetes.io/is-default-class: "true" 7 | spec: 8 | controller: nginx.org/ingress-controller 9 | -------------------------------------------------------------------------------- /config/manifests/bases/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - nginx-ingress-operator.clusterserviceversion.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | commonAnnotations: 6 | containerImage: quay.io/nginx/nginx-ingress-operator:3.4.1 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.20 2 | FROM quay.io/operator-framework/helm-operator:v1.41.1 3 | 4 | ENV HOME=/opt/helm 5 | COPY watches.yaml ${HOME}/watches.yaml 6 | COPY helm-charts ${HOME}/helm-charts 7 | WORKDIR ${HOME} 8 | 9 | COPY LICENSE /licenses/ 10 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.40.0 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/charts.nginx.org_nginxingresses.yaml 6 | apiVersion: kustomize.config.k8s.io/v1beta1 7 | kind: Kustomization 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_nginxingresscontrollers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: nginxingresscontrollers.k8s.nginx.org 8 | -------------------------------------------------------------------------------- /watches.yaml: -------------------------------------------------------------------------------- 1 | # Use the 'create api' subcommand to add watches to this file. 2 | - group: charts.nginx.org 3 | version: v1alpha1 4 | kind: NginxIngress 5 | chart: helm-charts/nginx-ingress 6 | overrideValues: 7 | controller.serviceAccount.name: nginx-ingress 8 | controller.defaultTLS.cert: "" 9 | controller.defaultTLS.key: "" 10 | controller.wildcardTLS.cert: "" 11 | controller.wildcardTLS.key: "" 12 | #+kubebuilder:scaffold:watch 13 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: serviceaccount 6 | app.kubernetes.io/instance: controller-manager-sa 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: nginx-ingress-operator 9 | app.kubernetes.io/part-of: nginx-ingress-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_nginxingresscontrollers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: nginxingresscontrollers.k8s.nginx.org 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | -------------------------------------------------------------------------------- /config/rbac/nginxingress_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nginxingresses. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: nginxingress-viewer-role 6 | rules: 7 | - apiGroups: 8 | - charts.nginx.org 9 | resources: 10 | - nginxingresses 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - charts.nginx.org 17 | resources: 18 | - nginxingresses/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | patchesJson6902: 4 | - path: patches/basic.config.yaml 5 | target: 6 | group: scorecard.operatorframework.io 7 | version: v1alpha3 8 | kind: Configuration 9 | name: config 10 | - path: patches/olm.config.yaml 11 | target: 12 | group: scorecard.operatorframework.io 13 | version: v1alpha3 14 | kind: Configuration 15 | name: config 16 | #+kubebuilder:scaffold:patchesJson6902 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin 8 | testbin/* 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Kubernetes Generated files - skip generated files, except for vendored files 17 | 18 | !vendor/**/zz_generated.* 19 | 20 | # editor and IDE paraphernalia 21 | .idea 22 | *.swp 23 | *.swo 24 | *~ 25 | .vscode/ 26 | 27 | dist 28 | -------------------------------------------------------------------------------- /config/rbac/nginxingresscontroller_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nginxingresscontrollers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: nginxingresscontroller-viewer-role 6 | rules: 7 | - apiGroups: 8 | - k8s.nginx.org 9 | resources: 10 | - nginxingresscontrollers 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - k8s.nginx.org 17 | resources: 18 | - nginxingresscontrollers/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/values-icp.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | name: controller 3 | kind: daemonset 4 | nginxplus: true 5 | image: 6 | repository: mycluster.icp:8500/kube-system/nginx-plus-ingress 7 | tag: "5.3.1" 8 | nodeSelector: 9 | beta.kubernetes.io/arch: "amd64" 10 | proxy: true 11 | terminationGracePeriodSeconds: 60 12 | tolerations: 13 | - key: "dedicated" 14 | operator: "Exists" 15 | effect: "NoSchedule" 16 | - key: "CriticalAddonsOnly" 17 | operator: "Exists" 18 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: nginx.org 2 | layout: 3 | - helm.sdk.operatorframework.io/v1 4 | plugins: 5 | manifests.sdk.operatorframework.io/v2: {} 6 | scorecard.sdk.operatorframework.io/v2: {} 7 | sdk.x-openshift.io/v1: {} 8 | projectName: nginx-ingress-operator 9 | repo: github.com/nginx/nginx-ingress-helm-operator 10 | resources: 11 | - api: 12 | crdVersion: v1 13 | namespaced: true 14 | controller: true 15 | domain: nginx.org 16 | group: charts 17 | kind: NginxIngress 18 | version: v1alpha1 19 | version: "3" 20 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | triage: 10 | permissions: 11 | contents: read 12 | pull-requests: write # for actions/labeler to add labels 13 | runs-on: ubuntu-24.04 14 | steps: 15 | - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1-alpha.1 16 | with: 17 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 18 | sync-labels: true 19 | -------------------------------------------------------------------------------- /bundle/manifests/nginx-ingress-operator-manager-config_v1_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | controller_manager_config.yaml: | 4 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 5 | kind: ControllerManagerConfig 6 | health: 7 | healthProbeBindAddress: :8081 8 | metrics: 9 | bindAddress: 127.0.0.1:8080 10 | 11 | leaderElection: 12 | leaderElect: true 13 | resourceName: 811c9dc5.nginx.org 14 | kind: ConfigMap 15 | metadata: 16 | name: nginx-ingress-operator-manager-config 17 | -------------------------------------------------------------------------------- /config/rbac/nginxingress_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nginxingresses. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: nginxingress-editor-role 6 | rules: 7 | - apiGroups: 8 | - charts.nginx.org 9 | resources: 10 | - nginxingresses 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - charts.nginx.org 21 | resources: 22 | - nginxingresses/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-ingress-class.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.controller.ingressClass.create }} 2 | apiVersion: networking.k8s.io/v1 3 | kind: IngressClass 4 | metadata: 5 | name: {{ .Values.controller.ingressClass.name }} 6 | labels: 7 | {{- include "nginx-ingress.labels" . | nindent 4 }} 8 | {{- if .Values.controller.ingressClass.setAsDefaultIngress }} 9 | annotations: 10 | ingressclass.kubernetes.io/is-default-class: "true" 11 | {{- end }} 12 | spec: 13 | controller: nginx.org/ingress-controller 14 | {{ end }} 15 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | change: 2 | - head-branch: ['^change/'] 3 | 4 | enhancement: 5 | - head-branch: ['^feature/', '^feat/', '^enhancement/', '^enh/'] 6 | 7 | bug: 8 | - head-branch: ['^fix/', '^bug/'] 9 | 10 | chore: 11 | - head-branch: ['^chore/'] 12 | 13 | tests: 14 | - head-branch: ['^tests/', '^test/'] 15 | 16 | documentation: 17 | - head-branch: ['^docs/', '^doc/'] 18 | - changed-files: 19 | - any-glob-to-any-file: '**/*.md' 20 | 21 | dependencies: 22 | - head-branch: ['^deps/', '^dep/', '^dependabot/', '^renovate/', '^mend/'] 23 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrole 6 | app.kubernetes.io/instance: metrics-reader 7 | app.kubernetes.io/component: kube-rbac-proxy 8 | app.kubernetes.io/created-by: nginx-ingress-operator 9 | app.kubernetes.io/part-of: nginx-ingress-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: metrics-reader 12 | rules: 13 | - nonResourceURLs: 14 | - "/metrics" 15 | verbs: 16 | - get 17 | -------------------------------------------------------------------------------- /tests/nginx-ingress-controller-oss.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: charts.nginx.org/v1alpha1 2 | kind: NginxIngress 3 | metadata: 4 | name: my-nginx-ingress 5 | namespace: nginx-ingress 6 | spec: 7 | controller: 8 | defaultTLS: 9 | secret: "" 10 | enableCustomResources: true 11 | image: 12 | pullPolicy: Always 13 | repository: nginx/nginx-ingress 14 | tag: 5.3.1 15 | ingressClass: 16 | name: nginx 17 | kind: deployment 18 | nginxplus: false 19 | service: 20 | type: NodePort 21 | rbac: 22 | create: true 23 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-lease.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.controller.reportIngressStatus.enableLeaderElection }} 2 | apiVersion: coordination.k8s.io/v1 3 | kind: Lease 4 | metadata: 5 | name: {{ include "nginx-ingress.leaderElectionName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.controller.reportIngressStatus.annotations }} 10 | annotations: 11 | {{ toYaml .Values.controller.reportIngressStatus.annotations | indent 4 }} 12 | {{- end }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-leader-election-configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.controller.reportIngressStatus.enableLeaderElection }} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ include "nginx-ingress.leaderElectionName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.controller.reportIngressStatus.annotations }} 10 | annotations: 11 | {{ toYaml .Values.controller.reportIngressStatus.annotations | indent 4 }} 12 | {{- end }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-secret.yaml: -------------------------------------------------------------------------------- 1 | {{ if and (not .Values.controller.defaultTLS.secret) (.Values.controller.defaultTLS.cert) (.Values.controller.defaultTLS.key) }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ include "nginx-ingress.defaultTLSName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | type: kubernetes.io/tls 10 | data: 11 | tls.crt: {{ .Values.controller.defaultTLS.cert }} 12 | tls.key: {{ .Values.controller.defaultTLS.key }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /config/rbac/nginxingresscontroller_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nginxingresscontrollers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: nginxingresscontroller-editor-role 6 | rules: 7 | - apiGroups: 8 | - k8s.nginx.org 9 | resources: 10 | - nginxingresscontrollers 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - k8s.nginx.org 21 | resources: 22 | - nginxingresscontrollers/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 5.3.1 3 | description: NGINX Ingress Controller 4 | home: https://github.com/nginx/kubernetes-ingress 5 | icon: https://raw.githubusercontent.com/nginx/kubernetes-ingress/v5.3.1/charts/nginx-ingress/chart-icon.png 6 | keywords: 7 | - ingress 8 | - nginx 9 | kubeVersion: '>= 1.25.0-0' 10 | maintainers: 11 | - email: kubernetes@nginx.com 12 | name: nginx 13 | name: nginx-ingress 14 | sources: 15 | - https://github.com/nginx/kubernetes-ingress/tree/v5.3.1/charts/nginx-ingress 16 | type: application 17 | version: 2.4.1 18 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-wildcard-secret.yaml: -------------------------------------------------------------------------------- 1 | {{ if and (not .Values.controller.wildcardTLS.secret) (and .Values.controller.wildcardTLS.cert .Values.controller.wildcardTLS.key) }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ include "nginx-ingress.wildcardTLSName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | type: kubernetes.io/tls 10 | data: 11 | tls.crt: {{ .Values.controller.wildcardTLS.cert }} 12 | tls.key: {{ .Values.controller.wildcardTLS.key }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /resources/rbac-example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: nginx-ingress-independent 5 | namespace: nginx-ingress 6 | # imagePullSecrets: 7 | # - name: myImagePullSecretName 8 | --- 9 | kind: ClusterRoleBinding 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | metadata: 12 | name: nginx-ingress-independent 13 | subjects: 14 | - kind: ServiceAccount 15 | name: nginx-ingress-independent 16 | namespace: nginx-ingress 17 | roleRef: 18 | kind: ClusterRole 19 | name: nginx-ingress-operator-nginx-ingress-admin 20 | apiGroup: rbac.authorization.k8s.io 21 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.create }} 2 | kind: ClusterRoleBinding 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | metadata: 5 | name: {{ include "nginx-ingress.fullname" . }} 6 | labels: 7 | {{- include "nginx-ingress.labels" . | nindent 4 }} 8 | subjects: 9 | - kind: ServiceAccount 10 | name: {{ include "nginx-ingress.serviceAccountName" . }} 11 | namespace: {{ .Release.Namespace }} 12 | roleRef: 13 | kind: ClusterRole 14 | name: nginx-ingress-operator-nginx-ingress-admin 15 | apiGroup: rbac.authorization.k8s.io 16 | {{- end }} 17 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /examples/deployment-oss-min/nginx-ingress-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: charts.nginx.org/v1alpha1 2 | kind: NginxIngress 3 | metadata: 4 | name: nginxingress-sample 5 | spec: 6 | # Default values copied from /helm-charts/nginx-ingress/values.yaml 7 | controller: 8 | defaultTLS: 9 | secret: "" 10 | enableCustomResources: true 11 | image: 12 | pullPolicy: IfNotPresent 13 | repository: nginx/nginx-ingress 14 | tag: 5.3.1-ubi 15 | ingressClass: 16 | name: nginx 17 | kind: deployment 18 | nginxplus: false 19 | service: 20 | type: NodePort 21 | rbac: 22 | create: true 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /bundle/manifests/nginx-ingress-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/component: kube-rbac-proxy 7 | app.kubernetes.io/created-by: nginx-ingress-operator 8 | app.kubernetes.io/instance: metrics-reader 9 | app.kubernetes.io/managed-by: kustomize 10 | app.kubernetes.io/name: clusterrole 11 | app.kubernetes.io/part-of: nginx-ingress-operator 12 | name: nginx-ingress-operator-metrics-reader 13 | rules: 14 | - nonResourceURLs: 15 | - /metrics 16 | verbs: 17 | - get 18 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrolebinding 6 | app.kubernetes.io/instance: manager-rolebinding 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: nginx-ingress-operator 9 | app.kubernetes.io/part-of: nginx-ingress-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: manager-rolebinding 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: ClusterRole 15 | name: manager-role 16 | subjects: 17 | - kind: ServiceAccount 18 | name: controller-manager 19 | namespace: system 20 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - skip changelog 5 | categories: 6 | - title: 💣 Breaking Changes 7 | labels: 8 | - change 9 | - title: 🚀 Features 10 | labels: 11 | - enhancement 12 | - title: 🐛 Bug Fixes 13 | labels: 14 | - bug 15 | - title: 🧪 Tests 16 | labels: 17 | - tests 18 | - title: 🔨 Maintenance 19 | labels: 20 | - chore 21 | - title: 📝 Documentation 22 | labels: 23 | - documentation 24 | - title: ⬆️ Dependencies 25 | labels: 26 | - dependencies 27 | - title: Other Changes 28 | labels: 29 | - "*" 30 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrolebinding 6 | app.kubernetes.io/instance: proxy-rolebinding 7 | app.kubernetes.io/component: kube-rbac-proxy 8 | app.kubernetes.io/created-by: nginx-ingress-operator 9 | app.kubernetes.io/part-of: nginx-ingress-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: proxy-rolebinding 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: ClusterRole 15 | name: proxy-role 16 | subjects: 17 | - kind: ServiceAccount 18 | name: controller-manager 19 | namespace: system 20 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrole 6 | app.kubernetes.io/instance: proxy-role 7 | app.kubernetes.io/component: kube-rbac-proxy 8 | app.kubernetes.io/created-by: nginx-ingress-operator 9 | app.kubernetes.io/part-of: nginx-ingress-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: proxy-role 12 | rules: 13 | - apiGroups: 14 | - authentication.k8s.io 15 | resources: 16 | - tokenreviews 17 | verbs: 18 | - create 19 | - apiGroups: 20 | - authorization.k8s.io 21 | resources: 22 | - subjectaccessreviews 23 | verbs: 24 | - create 25 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: rolebinding 6 | app.kubernetes.io/instance: leader-election-rolebinding 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: nginx-ingress-operator 9 | app.kubernetes.io/part-of: nginx-ingress-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: leader-election-rolebinding 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: ClusterRole 15 | name: leader-election-role 16 | subjects: 17 | - kind: ServiceAccount 18 | name: controller-manager 19 | namespace: system 20 | -------------------------------------------------------------------------------- /.github/workflows/delete-operator-branch.yml: -------------------------------------------------------------------------------- 1 | name: Delete Operator Branch 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | branch: 7 | description: "Operator Branch to delete" 8 | default: "update-nginx-ingress-operator-to-v2.3.0" 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | branch-delete: 15 | runs-on: ubuntu-24.04 16 | steps: 17 | - name: Delete branch 18 | uses: dawidd6/action-delete-branch@d1efac9a6f7a9b408d4e8ff663a99c1fbac17b3f # v3.1.0 19 | with: 20 | github_token: ${{ secrets.NGINX_PAT }} 21 | branches: ${{ inputs.branch }} 22 | repository: certified-operators 23 | owner: nginx-bot 24 | -------------------------------------------------------------------------------- /examples/deployment-plus-min/nginx-ingress-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: charts.nginx.org/v1alpha1 2 | kind: NginxIngress 3 | metadata: 4 | name: nginxingress-sample 5 | spec: 6 | # Default values copied from /helm-charts/nginx-ingress/values.yaml 7 | controller: 8 | defaultTLS: 9 | secret: "" 10 | enableCustomResources: true 11 | image: 12 | pullPolicy: IfNotPresent 13 | repository: nginx/nginx-ingress 14 | tag: 5.3.1-ubi 15 | ingressClass: 16 | name: nginx 17 | kind: deployment 18 | nginxplus: true 19 | mgmt: 20 | licenseTokenSecretName: "license-token" 21 | service: 22 | type: NodePort 23 | rbac: 24 | create: true 25 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | labels: 6 | control-plane: controller-manager 7 | app.kubernetes.io/name: service 8 | app.kubernetes.io/instance: controller-manager-metrics-service 9 | app.kubernetes.io/component: kube-rbac-proxy 10 | app.kubernetes.io/created-by: nginx-ingress-operator 11 | app.kubernetes.io/part-of: nginx-ingress-operator 12 | app.kubernetes.io/managed-by: kustomize 13 | name: controller-manager-metrics-service 14 | namespace: system 15 | spec: 16 | ports: 17 | - name: https 18 | port: 8443 19 | protocol: TCP 20 | targetPort: https 21 | selector: 22 | control-plane: controller-manager 23 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-globalconfiguration.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.controller.globalConfiguration.create }} 2 | apiVersion: k8s.nginx.org/v1 3 | kind: GlobalConfiguration 4 | metadata: 5 | {{- if not .Values.controller.globalConfiguration.customName }} 6 | name: {{ include "nginx-ingress.controller.fullname" . }} 7 | namespace: {{ .Release.Namespace }} 8 | {{- else }} 9 | name: {{ include "nginx-ingress.globalConfiguration.customName" . }} 10 | namespace: {{ include "nginx-ingress.globalConfiguration.customNamespace" . }} 11 | {{- end }} 12 | labels: 13 | {{- include "nginx-ingress.labels" . | nindent 4 }} 14 | spec: 15 | {{ toYaml .Values.controller.globalConfiguration.spec | indent 2 }} 16 | {{- end }} 17 | -------------------------------------------------------------------------------- /.github/workflows/close-operator-pr.yml: -------------------------------------------------------------------------------- 1 | name: Close Operator PR 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | url: 7 | description: "Operator PR URL to close" 8 | required: true 9 | 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | pr-update: 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - name: Close PR 19 | run: | 20 | gh pr view ${{ inputs.url }} 21 | rc=$? 22 | if [ ${rc} -eq 0 ]; then 23 | echo "Closing PR ${{ inputs.url }}" 24 | gh pr close ${{ inputs.url }} 25 | echo $? 26 | else 27 | echo "${{ inputs.url }} does not exist" 28 | fi 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.NGINX_PAT }} 31 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # Comment the following 4 lines if you want to disable 13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 | # which protects your /metrics endpoint. 15 | - auth_proxy_service.yaml 16 | - auth_proxy_role.yaml 17 | - auth_proxy_role_binding.yaml 18 | - auth_proxy_client_clusterrole.yaml 19 | -------------------------------------------------------------------------------- /resources/scc.yaml: -------------------------------------------------------------------------------- 1 | # Create SCC for IC resources 2 | kind: SecurityContextConstraints 3 | apiVersion: security.openshift.io/v1 4 | metadata: 5 | name: nginx-ingress-admin 6 | allowPrivilegedContainer: false 7 | runAsUser: 8 | type: MustRunAs 9 | uid: 101 10 | seLinuxContext: 11 | type: MustRunAs 12 | fsGroup: 13 | type: MustRunAs 14 | supplementalGroups: 15 | type: MustRunAs 16 | allowHostNetwork: false 17 | allowHostPID: false 18 | allowHostPorts: false 19 | allowHostDirVolumePlugin: false 20 | allowHostIPC: false 21 | readOnlyRootFilesystem: false 22 | seccompProfiles: 23 | - runtime/default 24 | volumes: 25 | - secret 26 | requiredDropCapabilities: 27 | - ALL 28 | users: 29 | - 'system:serviceaccount:*:nginx-ingress' 30 | allowedCapabilities: 31 | - NET_BIND_SERVICE 32 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | dependabot: 9 | runs-on: ubuntu-24.04 10 | if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} 11 | permissions: 12 | pull-requests: write 13 | contents: write 14 | steps: 15 | - name: Dependabot metadata 16 | id: dependabot-metadata 17 | uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0 18 | 19 | - name: Enable auto-merge for Dependabot PRs 20 | run: gh pr merge --auto --squash "$PR_URL" 21 | env: 22 | PR_URL: ${{ github.event.pull_request.html_url }} 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /resources/scc-daemonset.yaml: -------------------------------------------------------------------------------- 1 | # Create SCC for IC resources 2 | kind: SecurityContextConstraints 3 | apiVersion: security.openshift.io/v1 4 | metadata: 5 | name: nginx-ingress-admin 6 | allowPrivilegedContainer: false 7 | runAsUser: 8 | type: MustRunAs 9 | uid: 101 10 | seLinuxContext: 11 | type: MustRunAs 12 | fsGroup: 13 | type: MustRunAs 14 | supplementalGroups: 15 | type: MustRunAs 16 | allowHostNetwork: false 17 | allowHostPID: false 18 | allowHostPorts: true 19 | allowHostDirVolumePlugin: false 20 | allowHostIPC: false 21 | readOnlyRootFilesystem: false 22 | seccompProfiles: 23 | - runtime/default 24 | volumes: 25 | - secret 26 | requiredDropCapabilities: 27 | - ALL 28 | users: 29 | - 'system:serviceaccount:*:nginx-ingress' 30 | allowedCapabilities: 31 | - NET_BIND_SERVICE 32 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Proposed changes 2 | Describe the use case and detail of the change. If this PR addresses an issue on GitHub, make sure to include a link to that issue here in this description (not in the title of the PR). 3 | 4 | ### Checklist 5 | Before creating a PR, run through this checklist and mark each as complete. 6 | 7 | - [ ] I have read the [CONTRIBUTING](https://github.com/nginx/nginx-ingress-operator/blob/main/CONTRIBUTING.md) doc 8 | - [ ] I have added tests that prove my fix is effective or that my feature works 9 | - [ ] I have checked that all unit tests pass after adding my changes 10 | - [ ] I have updated necessary documentation 11 | - [ ] I have rebased my branch onto main 12 | - [ ] I will ensure my PR is targeting the main branch and pulling from my branch from my own fork -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-prometheus-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.prometheus.create .Values.prometheus.service.create}} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ include "nginx-ingress.prometheus.serviceName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.prometheus.service.labels -}} 10 | {{- toYaml .Values.prometheus.service.labels | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | clusterIP: None 14 | ports: 15 | - name: prometheus 16 | protocol: TCP 17 | port: {{ .Values.prometheus.port }} 18 | targetPort: {{ .Values.prometheus.port }} 19 | selector: 20 | {{- include "nginx-ingress.selectorLabels" . | nindent 4 }} 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: role 7 | app.kubernetes.io/instance: leader-election-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: nginx-ingress-operator 10 | app.kubernetes.io/part-of: nginx-ingress-operator 11 | app.kubernets.io/managed-by: kustomize 12 | name: leader-election-role 13 | rules: 14 | - apiGroups: 15 | - coordination.k8s.io 16 | resources: 17 | - leases 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - create 23 | - update 24 | - patch 25 | - delete 26 | - apiGroups: 27 | - "" 28 | resources: 29 | - events 30 | verbs: 31 | - create 32 | - patch 33 | -------------------------------------------------------------------------------- /bundle/manifests/nginx-ingress-operator-controller-manager-metrics-service_v1_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/component: kube-rbac-proxy 7 | app.kubernetes.io/created-by: nginx-ingress-operator 8 | app.kubernetes.io/instance: controller-manager-metrics-service 9 | app.kubernetes.io/managed-by: kustomize 10 | app.kubernetes.io/name: service 11 | app.kubernetes.io/part-of: nginx-ingress-operator 12 | control-plane: controller-manager 13 | name: nginx-ingress-operator-controller-manager-metrics-service 14 | spec: 15 | ports: 16 | - name: https 17 | port: 8443 18 | protocol: TCP 19 | targetPort: https 20 | selector: 21 | control-plane: controller-manager 22 | status: 23 | loadBalancer: {} 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Deploy x to '...' using some.yaml 13 | 2. View logs on '....' 14 | 3. See error 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Your environment** 20 | * Version of the NGINX Ingress Operator - release version or a specific commit 21 | * Version of the Ingress Controller - release version or a specific commit 22 | * Version of Kubernetes 23 | * Kubernetes platform (e.g. Mini-kube or GCP) 24 | * Using NGINX or NGINX Plus 25 | 26 | **Additional context** 27 | Add any other context about the problem here. Any log files you want to share. -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | NGINX Ingress Controller {{ .Chart.AppVersion }} has been installed. 2 | 3 | For release notes, see: https://docs.nginx.com/nginx-ingress-controller/changelog/ 4 | 5 | For Helm installation instructions, see: https://docs.nginx.com/nginx-ingress-controller/install/helm/ 6 | 7 | {{ if .Release.IsUpgrade -}} 8 | If you are upgrading from a version of the chart that uses older Custom Resource Definitions (CRD) it is necessary to manually upgrade the CRDs as this is not managed by Helm. 9 | To update to the latest version of the CRDs: 10 | $ kubectl apply -f https://raw.githubusercontent.com/nginx/kubernetes-ingress/v{{ .Chart.AppVersion }}/deploy/crds.yaml 11 | 12 | For more details on upgrading the CRDs, see: https://docs.nginx.com/nginx-ingress-controller/install/upgrade/#upgrade-nginx-ingress-controller-crds 13 | {{- end -}} 14 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | name: "Dependency Review" 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | 7 | concurrency: 8 | group: ${{ github.ref_name }}-deps-review 9 | cancel-in-progress: true 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | dependency-review: 16 | runs-on: ubuntu-24.04 17 | permissions: 18 | contents: read # for actions/checkout 19 | pull-requests: write # for actions/dependency-review-action to post comments 20 | steps: 21 | - name: "Checkout Repository" 22 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 23 | 24 | - name: "Dependency Review" 25 | uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 26 | with: 27 | config-file: "nginx/k8s-common/dependency-review-config.yml@main" 28 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | app.kubernets.io/name: servicemonitor 9 | app.kubernetes.io/instance: controller-manager-metrics-monitor 10 | app.kubernetes.io/component: metrics 11 | app.kubernetes.io/created-by: nginx-ingress-operator 12 | app.kubernetes.io/part-of: nginx-ingress-operator 13 | app.kubernetes.io/managed-by: kustomize 14 | name: controller-manager-metrics-monitor 15 | namespace: system 16 | spec: 17 | endpoints: 18 | - path: /metrics 19 | port: https 20 | scheme: https 21 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 22 | tlsConfig: 23 | insecureSkipVerify: true 24 | selector: 25 | matchLabels: 26 | control-plane: controller-manager 27 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.prometheus.serviceMonitor.create }} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: {{ include "nginx-ingress.controller.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.prometheus.serviceMonitor.labels -}} 10 | {{- toYaml .Values.prometheus.serviceMonitor.labels | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | selector: 14 | matchLabels: 15 | {{- if .Values.prometheus.serviceMonitor.selectorMatchLabels -}} 16 | {{- toYaml .Values.prometheus.serviceMonitor.selectorMatchLabels | nindent 6 }} 17 | {{- end }} 18 | {{- include "nginx-ingress.selectorLabels" . | nindent 6 }} 19 | endpoints: 20 | {{- toYaml .Values.prometheus.serviceMonitor.endpoints | nindent 4 }} 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | # Core bundle annotations. 3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 6 | operators.operatorframework.io.bundle.package.v1: nginx-ingress-operator 7 | operators.operatorframework.io.bundle.channels.v1: alpha 8 | operators.operatorframework.io.bundle.channel.default.v1: alpha 9 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.40.0 10 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 11 | operators.operatorframework.io.metrics.project_layout: helm.sdk.operatorframework.io/v1 12 | 13 | # Annotations for testing. 14 | operators.operatorframework.io.test.mediatype.v1: scorecard+v1 15 | operators.operatorframework.io.test.config.v1: tests/scorecard/ 16 | 17 | # OpenShift annotations. 18 | com.redhat.openshift.versions: v4.12 19 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-pdb.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.controller.podDisruptionBudget.enabled -}} 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: {{ include "nginx-ingress.controller.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.controller.podDisruptionBudget.annotations }} 10 | annotations: 11 | {{ toYaml .Values.controller.podDisruptionBudget.annotations | indent 4 }} 12 | {{- end }} 13 | spec: 14 | selector: 15 | matchLabels: 16 | {{- include "nginx-ingress.selectorLabels" . | nindent 6 }} 17 | {{- if .Values.controller.podDisruptionBudget.minAvailable }} 18 | minAvailable: {{ .Values.controller.podDisruptionBudget.minAvailable }} 19 | {{- end }} 20 | {{- if .Values.controller.podDisruptionBudget.maxUnavailable }} 21 | maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} 22 | {{- end }} 23 | {{- end }} 24 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.create }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "nginx-ingress.serviceAccountName" . }} 6 | {{- if .Values.controller.serviceAccount.annotations }} 7 | annotations: {{- toYaml .Values.controller.serviceAccount.annotations | nindent 4 }} 8 | {{- end }} 9 | namespace: {{ .Release.Namespace }} 10 | labels: 11 | {{- include "nginx-ingress.labels" . | nindent 4 }} 12 | {{- if or .Values.controller.serviceAccount.imagePullSecretName .Values.controller.serviceAccount.imagePullSecretsNames }} 13 | imagePullSecrets: 14 | {{- end }} 15 | 16 | {{- if .Values.controller.serviceAccount.imagePullSecretName }} 17 | - name: {{ .Values.controller.serviceAccount.imagePullSecretName}} 18 | {{- end }} 19 | 20 | {{- if .Values.controller.serviceAccount.imagePullSecretsNames }} 21 | {{- range .Values.controller.serviceAccount.imagePullSecretsNames }} 22 | - name: {{ . }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /.github/workflows/dockerhub-description.yml: -------------------------------------------------------------------------------- 1 | name: Update Docker Hub Description 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - README.md 8 | - .github/workflows/dockerhub-description.yml 9 | 10 | concurrency: 11 | group: ${{ github.ref_name }}-dockerhub-description 12 | cancel-in-progress: true 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | dockerHubDescription: 19 | runs-on: ubuntu-24.04 20 | steps: 21 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 22 | 23 | - name: Modify readme for DockerHub 24 | run: | 25 | sed -i '1,2d' README.md 26 | - name: Docker Hub Description 27 | uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0 28 | with: 29 | username: ${{ secrets.DOCKER_USERNAME }} 30 | password: ${{ secrets.DOCKER_PASSWORD }} 31 | repository: nginx/nginx-ingress-operator 32 | short-description: ${{ github.event.repository.description }} 33 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | We advise users to run the most recent release of the NGINX Ingress Operator, and we issue software updates to the most recent release. We provide technical support for F5 customers who are using the most recent version of the NGINX Ingress Operator, and any version released within two years of the current release. 6 | 7 | For more information visit https://docs.nginx.com/nginx-ingress-controller/technical-specifications/ 8 | 9 | ## Reporting a Vulnerability 10 | 11 | The F5 Security Incident Response Team (F5 SIRT) has an email alias that makes it easy to report potential security vulnerabilities. 12 | 13 | - If you’re an F5 customer with an active support contract, please contact [F5 Technical Support](https://www.f5.com/services/support). 14 | - If you aren’t an F5 customer, please report any potential or current instances of security vulnerabilities with any F5 product to the F5 Security Incident Response Team at F5SIRT@f5.com 15 | 16 | For more information visit https://www.f5.com/services/support/report-a-vulnerability 17 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: nginx-ingress-operator-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: nginx-ingress-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #labels: 13 | #- includeSelectors: true 14 | # pairs: 15 | # someName: someValue 16 | 17 | resources: 18 | - ../crd 19 | - ../rbac 20 | - ../manager 21 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 22 | #- ../prometheus 23 | 24 | # Protect the /metrics endpoint by putting it behind auth. 25 | # If you want your controller-manager to expose the /metrics 26 | # endpoint w/o any authn/z, please comment the following lines. 27 | apiVersion: kustomize.config.k8s.io/v1beta1 28 | kind: Kustomization 29 | patches: 30 | - path: manager_auth_proxy_patch.yaml 31 | images: 32 | - name: kube-rbac-proxy 33 | newName: quay.io/brancz/kube-rbac-proxy 34 | newTag: v0.20.1 35 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | We currently support 2 methods of installation: 4 | 1. [Installation in an OpenShift cluster](./openshift-installation.md) using the [OLM](https://github.com/operator-framework/operator-lifecycle-manager). 5 | 1. [Manual installation](./manual-installation.md) in a Kubernetes or OpenShift cluster by manually deploying the operator manifests. 6 | 7 | After installation, use the [sample CR definition](../config/samples/charts_v1alpha1_nginxingress.yaml) to deploy the NGINX Ingress Controller using the operator. 8 | 9 | **Note:** Some users reported an `OOMkilled` error when they deployed the NGINX Ingress Operator in a large cluster with multiple namespaces and Kubernetes objects. This is due to the helm operator caching every Kubernetes object in the cluster, and thus consuming too much system memory. If you encounter this issue, consider setting the operator to only watch one namespace. If watching multiple namespaces is required in your use case, try manually increasing the memory limit for the operator. Note that the value might be overwritten after a release update. We are working with the OpenShift team to resolve this issue. 10 | -------------------------------------------------------------------------------- /bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # Core bundle labels. 4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 7 | LABEL operators.operatorframework.io.bundle.package.v1=nginx-ingress-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=alpha 9 | LABEL operators.operatorframework.io.bundle.channel.default.v1=alpha 10 | # renovate: datasource=github-releases depName=operator-sdk packageName=operator-framework/operator-sdk 11 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.41.1 12 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 13 | LABEL operators.operatorframework.io.metrics.project_layout=helm.sdk.operatorframework.io/v1 14 | 15 | # Labels for testing. 16 | LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 17 | LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ 18 | 19 | # Copy files to locations specified by labels. 20 | COPY bundle/manifests /manifests/ 21 | COPY bundle/metadata /metadata/ 22 | COPY bundle/tests/scorecard /tests/scorecard/ 23 | 24 | LABEL com.redhat.openshift.versions="v4.12" 25 | LABEL com.redhat.delivery.operator.bundle=true 26 | LABEL com.redhat.delivery.backport=true 27 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues and PRs" 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" # run at 1:30am every day 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | stale: 11 | permissions: 12 | issues: write # for actions/stale to close stale issues 13 | pull-requests: write # for actions/stale to close stale PRs 14 | runs-on: ubuntu-24.04 15 | steps: 16 | - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | stale-issue-message: "This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 10 days." 20 | stale-pr-message: "This PR is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 10 days." 21 | close-issue-message: "This issue was closed because it has been stalled for 10 days with no activity." 22 | close-pr-message: "This PR was closed because it has been stalled for 10 days with no activity." 23 | stale-issue-label: "stale" 24 | stale-pr-label: "stale" 25 | exempt-all-assignees: true 26 | exempt-issue-labels: "proposal" 27 | operations-per-run: 100 28 | days-before-stale: 90 29 | days-before-close: 10 30 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.40.0 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.40.0 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.40.0 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.40.0 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.40.0 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /.github/workflows/update-openshift-versions.yml: -------------------------------------------------------------------------------- 1 | name: Update OpenShift Versions 2 | 3 | on: 4 | schedule: 5 | # Run weekly on Mondays at 09:00 UTC 6 | - cron: '0 9 * * 1' 7 | workflow_dispatch: # Allow manual trigger 8 | 9 | jobs: 10 | update-openshift-versions: 11 | runs-on: ubuntu-24.04 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 15 | 16 | - name: Update OpenShift versions 17 | run: ./scripts/update-openshift-versions.sh 18 | 19 | - name: Check for changes 20 | id: changes 21 | run: | 22 | if git diff --quiet; then 23 | echo "changed=false" >> $GITHUB_OUTPUT 24 | else 25 | echo "changed=true" >> $GITHUB_OUTPUT 26 | fi 27 | 28 | - name: Create Pull Request 29 | if: steps.changes.outputs.changed == 'true' 30 | uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0 31 | with: 32 | token: ${{ secrets.GITHUB_TOKEN }} 33 | commit-message: 'update OpenShift supported versions' 34 | title: 'Update OpenShift supported versions' 35 | body: | 36 | This PR updates the minimum supported OpenShift version based on the latest Red Hat lifecycle data. 37 | 38 | Updated files: 39 | - `bundle.Dockerfile` 40 | - `bundle/metadata/annotations.yaml` 41 | - `Makefile` 42 | 43 | branch: chore/update-openshift-versions 44 | delete-branch: true -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.controller.autoscaling.enabled .Values.controller.autoscaling.create (eq .Values.controller.kind "deployment") (.Capabilities.APIVersions.Has "autoscaling/v2") -}} 2 | apiVersion: autoscaling/v2 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "nginx-ingress.controller.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.controller.autoscaling.annotations }} 10 | annotations: 11 | {{ toYaml .Values.controller.autoscaling.annotations | indent 4 }} 12 | {{- end }} 13 | spec: 14 | scaleTargetRef: 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | name: {{ include "nginx-ingress.controller.fullname" . }} 18 | minReplicas: {{ .Values.controller.autoscaling.minReplicas }} 19 | maxReplicas: {{ .Values.controller.autoscaling.maxReplicas }} 20 | {{- if .Values.controller.autoscaling.behavior }} 21 | behavior: 22 | {{ toYaml .Values.controller.autoscaling.behavior | indent 4 }} 23 | {{- end }} 24 | metrics: 25 | {{- if .Values.controller.autoscaling.targetMemoryUtilizationPercentage }} 26 | - type: Resource 27 | resource: 28 | name: memory 29 | target: 30 | type: Utilization 31 | averageUtilization: {{ .Values.controller.autoscaling.targetMemoryUtilizationPercentage }} 32 | {{- end }} 33 | {{- if .Values.controller.autoscaling.targetCPUUtilizationPercentage }} 34 | - type: Resource 35 | resource: 36 | name: cpu 37 | target: 38 | type: Utilization 39 | averageUtilization: {{ .Values.controller.autoscaling.targetCPUUtilizationPercentage }} 40 | {{- end }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | affinity: 12 | nodeAffinity: 13 | requiredDuringSchedulingIgnoredDuringExecution: 14 | nodeSelectorTerms: 15 | - matchExpressions: 16 | - key: kubernetes.io/arch 17 | operator: In 18 | values: 19 | - amd64 20 | - arm64 21 | - key: kubernetes.io/os 22 | operator: In 23 | values: 24 | - linux 25 | containers: 26 | - name: kube-rbac-proxy 27 | securityContext: 28 | allowPrivilegeEscalation: false 29 | capabilities: 30 | drop: 31 | - "ALL" 32 | image: kube-rbac-proxy:latest 33 | args: 34 | - "--secure-listen-address=0.0.0.0:8443" 35 | - "--upstream=http://127.0.0.1:8080/" 36 | - "--logtostderr=true" 37 | - "--v=0" 38 | ports: 39 | - containerPort: 8443 40 | protocol: TCP 41 | name: https 42 | resources: 43 | limits: 44 | cpu: 500m 45 | memory: 128Mi 46 | requests: 47 | cpu: 5m 48 | memory: 64Mi 49 | - name: manager 50 | args: 51 | - "--health-probe-bind-address=:8081" 52 | - "--metrics-bind-address=127.0.0.1:8080" 53 | - "--leader-elect" 54 | - "--leader-election-id=nginx-ingress-operator" 55 | -------------------------------------------------------------------------------- /config/crd/bases/charts.nginx.org_nginxingresses.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: nginxingresses.charts.nginx.org 6 | spec: 7 | group: charts.nginx.org 8 | names: 9 | kind: NginxIngress 10 | listKind: NginxIngressList 11 | plural: nginxingresses 12 | singular: nginxingress 13 | scope: Namespaced 14 | versions: 15 | - name: v1alpha1 16 | schema: 17 | openAPIV3Schema: 18 | description: NginxIngress is the Schema for the nginxingresses API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of NginxIngress 34 | type: object 35 | x-kubernetes-preserve-unknown-fields: true 36 | status: 37 | description: Status defines the observed state of NginxIngress 38 | type: object 39 | x-kubernetes-preserve-unknown-fields: true 40 | type: object 41 | served: true 42 | storage: true 43 | subresources: 44 | status: {} 45 | -------------------------------------------------------------------------------- /bundle/tests/scorecard/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: 8 | - entrypoint: 9 | - scorecard-test 10 | - basic-check-spec 11 | image: quay.io/operator-framework/scorecard-test:v1.42.0 12 | labels: 13 | suite: basic 14 | test: basic-check-spec-test 15 | storage: 16 | spec: 17 | mountPath: {} 18 | - entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:v1.42.0 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | storage: 26 | spec: 27 | mountPath: {} 28 | - entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:v1.42.0 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | storage: 36 | spec: 37 | mountPath: {} 38 | - entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:v1.42.0 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | storage: 46 | spec: 47 | mountPath: {} 48 | - entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:v1.42.0 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | storage: 56 | spec: 57 | mountPath: {} 58 | - entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:v1.42.0 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | storage: 66 | spec: 67 | mountPath: {} 68 | storage: 69 | spec: 70 | mountPath: {} 71 | -------------------------------------------------------------------------------- /bundle/manifests/charts.nginx.org_nginxingresses.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | creationTimestamp: null 5 | name: nginxingresses.charts.nginx.org 6 | spec: 7 | group: charts.nginx.org 8 | names: 9 | kind: NginxIngress 10 | listKind: NginxIngressList 11 | plural: nginxingresses 12 | singular: nginxingress 13 | scope: Namespaced 14 | versions: 15 | - name: v1alpha1 16 | schema: 17 | openAPIV3Schema: 18 | description: NginxIngress is the Schema for the nginxingresses API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of NginxIngress 34 | type: object 35 | x-kubernetes-preserve-unknown-fields: true 36 | status: 37 | description: Status defines the observed state of NginxIngress 38 | type: object 39 | x-kubernetes-preserve-unknown-fields: true 40 | type: object 41 | served: true 42 | storage: true 43 | subresources: 44 | status: {} 45 | status: 46 | acceptedNames: 47 | kind: "" 48 | plural: "" 49 | conditions: null 50 | storedVersions: null 51 | -------------------------------------------------------------------------------- /docs/openshift-installation.md: -------------------------------------------------------------------------------- 1 | # Installation in an OpenShift cluster using the OLM 2 | 3 | This installation method is the recommended way for OpenShift users. **Note**: OpenShift version must be 4.2 or higher. 4 | 5 | **Note: The `nginx-ingress-operator` supports `Basic Install` only - we do not support auto-updates. When you are installing the Operator using the OLM, the auto-update feature should be disabled to avoid breaking changes being auto-applied. In OpenShift, this can be done by setting the `Approval Strategy` to `Manual`. Please see the [Operator SDK docs](https://sdk.operatorframework.io/docs/overview/operator-capabilities/) for more details on the Operator Capability Levels.** 6 | 7 | The NGINX Ingress Operator is a [RedHat certified Operator](https://connect.redhat.com/en/partner-with-us/red-hat-openshift-operator-certification). 8 | 9 | 1. In the OpenShift dashboard, click `Operators` > `Operator Hub` in the left menu and use the search box to type `nginx ingress`: 10 | ![alt text](./images/openshift1.png "Operators") 11 | 2. Click the `NGINX Ingress Operator` and click `Install`: 12 | ![alt text](./images/openshift2.png "NGINX Ingress Operator") 13 | 3. Click `Subscribe`: 14 | ![alt text](./images/openshift3.png "NGINX Ingress Operator Install") 15 | 16 | OpenShift will install the NGINX Ingress Operator: 17 | 18 | ![alt text](./images/openshift4.png "NGINX Ingress Operator Subscribe") 19 | 20 | **Note: If you're upgrading your operator installation to a later release, navigate [here](../helm-charts/nginx-ingress/) and run `kubectl apply -f crds/` or `oc apply -f crds/` as a prerequisite** 21 | 22 | Additional steps: 23 | 24 | In order to deploy NGINX Ingress Controller instances into OpenShift environments, a new SCC is required to be created on the cluster which will be used to bind the specific required capabilities to the NGINX Ingress service account(s). To do so for NIC deployments, please run the following command (assuming you are logged in with administrator access to the cluster): 25 | 26 | `kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/v3.4.1/resources/scc.yaml` 27 | 28 | Alternatively, to create an SCC for NIC daemonsets, please run this command: 29 | 30 | `kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/v3.4.1/resources/scc-daemonset.yaml` 31 | 32 | You can now deploy the NGINX Ingress Controller instances. 33 | -------------------------------------------------------------------------------- /.github/workflows/f5-cla.yml: -------------------------------------------------------------------------------- 1 | name: F5 CLA 2 | 3 | on: 4 | issue_comment: 5 | types: 6 | - created 7 | pull_request_target: 8 | types: 9 | - opened 10 | - synchronize 11 | - reopened 12 | 13 | concurrency: 14 | group: ${{ github.ref_name }}-cla 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | f5-cla: 21 | name: F5 CLA 22 | runs-on: ubuntu-24.04 23 | permissions: 24 | actions: write 25 | contents: read 26 | pull-requests: write 27 | statuses: write 28 | steps: 29 | - name: Run F5 Contributor License Agreement (CLA) assistant 30 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have hereby read the F5 CLA and agree to its terms') || github.event_name == 'pull_request_target' 31 | uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 32 | with: 33 | # Any pull request targeting the following branch will trigger a CLA check. 34 | branch: "main" 35 | # Path to the CLA document. 36 | path-to-document: "https://github.com/f5/.github/blob/main/CLA/cla-markdown.md" 37 | # Custom CLA messages. 38 | custom-notsigned-prcomment: "🎉 Thank you for your contribution! It appears you have not yet signed the F5 Contributor License Agreement (CLA), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the [F5 CLA](https://github.com/f5/.github/blob/main/CLA/cla-markdown.md) and reply on a new comment with the following text to agree:" 39 | custom-pr-sign-comment: "I have hereby read the F5 CLA and agree to its terms" 40 | custom-allsigned-prcomment: "✅ All required contributors have signed the F5 CLA for this PR. Thank you!" 41 | # Remote repository storing CLA signatures. 42 | remote-organization-name: "f5" 43 | remote-repository-name: "f5-cla-data" 44 | path-to-signatures: "signatures/beta/signatures.json" 45 | # Comma separated list of usernames for maintainers or any other individuals who should not be prompted for a CLA. 46 | allowlist: bot*,renovate,mend 47 | # Do not lock PRs after a merge. 48 | lock-pullrequest-aftermerge: false 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | PERSONAL_ACCESS_TOKEN: ${{ secrets.F5_CLA_TOKEN }} 52 | -------------------------------------------------------------------------------- /.github/workflows/create-operator-pr.yml: -------------------------------------------------------------------------------- 1 | name: Create Bundle Approval PR 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | type: string 8 | description: The version of the operator to submit for approval 9 | required: true 10 | workflow_call: 11 | inputs: 12 | version: 13 | type: string 14 | description: The version of the operator to submit for approval 15 | required: true 16 | 17 | concurrency: 18 | group: ${{ github.ref_name }}-create-operator-pr 19 | cancel-in-progress: true 20 | 21 | permissions: 22 | contents: read 23 | 24 | jobs: 25 | create-pr: 26 | name: Certify for Red Hat OpenShift 27 | runs-on: ubuntu-24.04 28 | steps: 29 | - name: Checkout Repository 30 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 31 | 32 | - name: Make 33 | run: | 34 | make bundle USE_IMAGE_DIGESTS=true 35 | 36 | - name: Checkout certified-operators repo 37 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 38 | with: 39 | token: ${{ secrets.NGINX_PAT }} 40 | repository: nginx-bot/certified-operators 41 | path: certified-operators 42 | 43 | - name: Update certified-operators repo 44 | working-directory: certified-operators/operators/nginx-ingress-operator 45 | run: | 46 | mkdir v${{ inputs.version }} 47 | cp -R ../../../bundle/manifests v${{ inputs.version }}/ 48 | cp -R ../../../bundle/metadata v${{ inputs.version }}/ 49 | 50 | - name: Commit changes 51 | uses: stefanzweifel/git-auto-commit-action@28e16e81777b558cc906c8750092100bbb34c5e3 # v7.0.0 52 | with: 53 | commit_message: operator nginx-ingress-operator (v${{ inputs.version }}) 54 | commit_author: nginx-bot 55 | commit_user_name: nginx-bot 56 | commit_user_email: integrations@nginx.com 57 | create_branch: true 58 | branch: update-nginx-ingress-operator-to-v${{ inputs.version }} 59 | repository: certified-operators 60 | 61 | - name: Create PR 62 | working-directory: certified-operators 63 | run: | 64 | gh pr create --title "operator nginx-ingress-operator (v${{ inputs.version }})" --body "Update nginx-ingress-operator to v${{ inputs.version }}" --head nginx-bot:update-nginx-ingress-operator-to-v${{ inputs.version }} --base main --repo redhat-openshift-ecosystem/certified-operators 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.NGINX_PAT }} 67 | -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | name: OpenSSF Scorecards 2 | on: 3 | # For Branch-Protection check. Only the default branch is supported. See 4 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 5 | branch_protection_rule: 6 | # To guarantee Maintained check is occasionally updated. See 7 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 8 | schedule: 9 | - cron: "33 10 * * 1" # run every Monday at 10:33 UTC 10 | push: 11 | branches: 12 | - main 13 | 14 | # Declare default permissions as read only. 15 | permissions: read-all 16 | 17 | jobs: 18 | analysis: 19 | name: Scorecard analysis 20 | runs-on: ubuntu-24.04 21 | permissions: 22 | # Needed to upload the results to code-scanning dashboard. 23 | security-events: write 24 | # Needed to publish results and get a badge (see publish_results below). 25 | id-token: write 26 | # Uncomment the permissions below if installing in a private repository. 27 | # contents: read 28 | # actions: read 29 | 30 | steps: 31 | - name: "Checkout code" 32 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 33 | with: 34 | persist-credentials: false 35 | 36 | - name: "Run analysis" 37 | uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 38 | with: 39 | results_file: results.sarif 40 | results_format: sarif 41 | 42 | # Public repositories: 43 | # - Publish results to OpenSSF REST API for easy access by consumers 44 | # - Allows the repository to include the Scorecard badge. 45 | # - See https://github.com/ossf/scorecard-action#publishing-results. 46 | # For private repositories: 47 | # - `publish_results` will always be set to `false`, regardless 48 | # of the value entered here. 49 | publish_results: true 50 | 51 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 52 | # format to the repository Actions tab. 53 | - name: "Upload artifact" 54 | uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 55 | with: 56 | name: SARIF file 57 | path: results.sarif 58 | retention-days: 5 59 | 60 | # Upload the results to GitHub's code scanning dashboard. 61 | - name: "Upload to code-scanning" 62 | uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 63 | with: 64 | sarif_file: results.sarif 65 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/crds/appprotectdos.f5.com_apdospolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.9.2 6 | creationTimestamp: null 7 | name: apdospolicies.appprotectdos.f5.com 8 | spec: 9 | group: appprotectdos.f5.com 10 | names: 11 | kind: APDosPolicy 12 | listKind: APDosPoliciesList 13 | plural: apdospolicies 14 | singular: apdospolicy 15 | preserveUnknownFields: false 16 | scope: Namespaced 17 | versions: 18 | - name: v1beta1 19 | schema: 20 | openAPIV3Schema: 21 | type: object 22 | description: APDosPolicy is the Schema for the APDosPolicy API 23 | properties: 24 | apiVersion: 25 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | type: object 34 | description: APDosPolicySpec defines the desired state of APDosPolicy 35 | properties: 36 | mitigation_mode: 37 | enum: 38 | - "standard" 39 | - "conservative" 40 | - "none" 41 | default: "standard" 42 | type: string 43 | signatures: 44 | enum: 45 | - "on" 46 | - "off" 47 | default: "on" 48 | type: string 49 | bad_actors: 50 | enum: 51 | - "on" 52 | - "off" 53 | default: "on" 54 | type: string 55 | automation_tools_detection: 56 | enum: 57 | - "on" 58 | - "off" 59 | default: "on" 60 | type: string 61 | tls_fingerprint: 62 | enum: 63 | - "on" 64 | - "off" 65 | default: "on" 66 | type: string 67 | served: true 68 | storage: true 69 | -------------------------------------------------------------------------------- /docs/manual-installation.md: -------------------------------------------------------------------------------- 1 | # Manual installation 2 | 3 | This will deploy the operator in the `nginx-ingress-operator-system` namespace. 4 | 5 | 1. Deploy the Operator and associated resources: 6 | 7 | 1. Clone the `nginx-ingress-operator` repo: 8 | 9 | ```shell 10 | git clone https://github.com/nginx/nginx-ingress-helm-operator/ --branch v3.4.1 11 | cd nginx-ingress-helm-operator/ 12 | ``` 13 | 14 | 2. To deploy the Operator and associated resources to all environments, run: 15 | 16 | ```shell 17 | make deploy IMG=nginx/nginx-ingress-operator:3.4.1 18 | ``` 19 | 20 | 2. Check that the Operator is running: 21 | 22 | ```shell 23 | kubectl get deployments -n nginx-ingress-operator-system 24 | 25 | NAME READY UP-TO-DATE AVAILABLE AGE 26 | nginx-ingress-operator-controller-manager 1/1 1 1 15s 27 | ``` 28 | 29 | 3. `OpenShift` Additional steps: 30 | 31 | In order to deploy NGINX Ingress Controller instances into OpenShift environments, a new SCC is required to be created on the cluster which will be used to bind the specific required capabilities to the NGINX Ingress service account(s). To do so for NIC deployments, please run the following command (assuming you are logged in with administrator access to the cluster): 32 | 33 | `kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/v3.4.1/resources/scc.yaml` 34 | 35 | Alternatively, to create an SCC for NIC daemonsets, please run this command: 36 | 37 | `kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/v3.4.1/resources/scc-daemonset.yaml` 38 | 39 | You can now deploy the NGINX Ingress Controller instances. 40 | 41 | **Note: If you're upgrading your operator installation to a later release, navigate [here](../helm-charts/nginx-ingress/) and run `kubectl apply -f crds/` or `oc apply -f crds/` as a prerequisite** 42 | 43 | ## Private Registry 44 | You can use the operator (including the kube-rbac-proxy) images from your own private registry. 45 | 1. Tag the images for your private registry 46 | ```shell 47 | docker tag quay.io/nginx/nginx-ingress-operator:3.4.1 /nginx-ingress-operator:3.4.1 48 | docker tag quay.io/brancz/kube-rbac-proxy:v0.18.0 /kube-rbac-proxy:v0.18.0 49 | ``` 50 | 51 | 2. Push the image to your private registry 52 | ```shell 53 | docker push /nginx-ingress-operator:3.4.1 54 | docker push /kube-rbac-proxy:v0.18.0 55 | ``` 56 | 57 | 3. Follow step 1 above but in step 1.2 you can run 58 | ```shell 59 | make deploy IMG=/nginx-ingress-operator:3.4.1 KRP_IMAGE_BASE=/kube-rbac-proxy 60 | ``` 61 | **Note: If you need to use a different `kube-rbac-proxy` version than the default, use the `KRP_IMAGE_TAG` variable** 62 | 63 | **Note: To use an image pull secret, you can specify the `IMAGE_PULL_SECRET_NAME` variable, eg `IMAGE_PULL_SECRET_NAME=regcred`** 64 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/crds/appprotectdos.f5.com_apdoslogconfs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.9.2 6 | creationTimestamp: null 7 | name: apdoslogconfs.appprotectdos.f5.com 8 | spec: 9 | group: appprotectdos.f5.com 10 | names: 11 | kind: APDosLogConf 12 | listKind: APDosLogConfList 13 | plural: apdoslogconfs 14 | singular: apdoslogconf 15 | preserveUnknownFields: false 16 | scope: Namespaced 17 | versions: 18 | - name: v1beta1 19 | schema: 20 | openAPIV3Schema: 21 | description: APDosLogConf is the Schema for the APDosLogConfs API 22 | properties: 23 | apiVersion: 24 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 25 | type: string 26 | kind: 27 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 28 | type: string 29 | metadata: 30 | type: object 31 | spec: 32 | description: APDosLogConfSpec defines the desired state of APDosLogConf 33 | properties: 34 | content: 35 | properties: 36 | format: 37 | enum: 38 | - splunk 39 | - arcsight 40 | - user-defined 41 | type: string 42 | format_string: 43 | type: string 44 | max_message_size: 45 | pattern: ^([1-9]|[1-5][0-9]|6[0-4])k$ 46 | type: string 47 | type: object 48 | filter: 49 | properties: 50 | traffic-mitigation-stats: 51 | enum: 52 | - none 53 | - all 54 | default: all 55 | type: string 56 | bad-actors: 57 | pattern: ^(none|all|top ([1-9]|[1-9][0-9]|[1-9][0-9]{2,4}|100000))$ 58 | default: top 10 59 | type: string 60 | attack-signatures: 61 | pattern: ^(none|all|top ([1-9]|[1-9][0-9]|[1-9][0-9]{2,4}|100000))$ 62 | default: top 10 63 | type: string 64 | type: object 65 | type: object 66 | type: object 67 | served: true 68 | storage: true 69 | -------------------------------------------------------------------------------- /.github/workflows/notifications.yml: -------------------------------------------------------------------------------- 1 | name: Notification 2 | 3 | on: 4 | workflow_run: 5 | branches: main 6 | workflows: 7 | - "CI" 8 | - "OpenSSF Scorecards" 9 | types: 10 | - completed 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | on-failure: 17 | runs-on: ubuntu-24.04 18 | if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.repository.fork == false }} 19 | permissions: 20 | contents: read 21 | actions: read # for 8398a7/action-slack 22 | steps: 23 | - name: Data 24 | uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 25 | continue-on-error: true 26 | id: data 27 | with: 28 | script: | 29 | const message = context.payload.workflow_run.head_commit.message 30 | message_sanitized = message.split('\n')[0] 31 | 32 | const check_data = (await github.rest.checks.listForRef({ 33 | owner: context.payload.repository.owner.login, 34 | repo: context.payload.repository.name, 35 | ref: context.payload.workflow_run.head_commit.id, 36 | })).data.check_runs.filter(check_run => check_run.conclusion === 'failure')[0] 37 | 38 | return { 39 | job_name: check_data.name, 40 | job_url: check_data.html_url, 41 | commit_message: message_sanitized, 42 | } 43 | 44 | - name: Send Notification 45 | uses: 8398a7/action-slack@77eaa4f1c608a7d68b38af4e3f739dcd8cba273e # v3.19.0 46 | with: 47 | status: custom 48 | custom_payload: | 49 | { 50 | username: 'Github', 51 | icon_emoji: ':github:', 52 | mention: 'channel', 53 | attachments: [{ 54 | title: '[${{ github.event.repository.full_name }}] ${{ github.event.workflow.name }} pipeline has failed (${{ github.event.workflow_run.event }})', 55 | color: 'danger', 56 | fields: [{ 57 | title: 'Commit', 58 | value: ``, 59 | short: true 60 | }, 61 | { 62 | title: 'Failed Job', 63 | value: `<${{ fromJSON(steps.data.outputs.result).job_url }}|${{ fromJSON(steps.data.outputs.result).job_name }}>`, 64 | short: true 65 | }, 66 | { 67 | title: 'Author', 68 | value: `${{ github.event.workflow_run.head_commit.author.name }}`, 69 | short: true 70 | }, 71 | { 72 | title: 'Pipeline URL', 73 | value: ``, 74 | short: true 75 | }] 76 | }] 77 | } 78 | env: 79 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} 80 | -------------------------------------------------------------------------------- /ISSUE_LIFECYCLE.md: -------------------------------------------------------------------------------- 1 | # Issue Lifecycle 2 | 3 | To ensure a balance between work carried out by the NGINX engineering team while encouraging community involvement on this project, we use the following issue lifecycle. (Note: The issue *creator* refers to the community member that created the issue. The issue *owner* refers to the NGINX team member that is responsible for managing the issue lifecycle.) 4 | 5 | 1. New issue created by community member. 6 | 7 | 8 | 2. Assign issue owner: All new issues are assigned an owner on the NGINX engineering team. This owner shepherds the issue through the subsequent stages in the issue lifecycle. 9 | 10 | 11 | 3. Determine issue type: This is done with automation where possible, and manually by the owner where necessary. The associated label is applied to the issue. 12 | #### Possible Issue Types 13 | `needs more info`: The owner should use the issue to request information from the creator. If we don't receive the needed information within 7 days, automation closes the issue. 14 | 15 | `bug`: The implementation of a feature is not correct. 16 | 17 | `proposal`: Request for a change. This can be a new feature, tackling technical debt, documentation changes, or improving existing features. 18 | 19 | `question`: The owner converts the issue to a github discussion and engages the creator. 20 | 21 | 22 | 4. Determine milestone: The owner, in collaboration with the wider team (PM & engineering), determines what milestone to attach to an issue. Generally, milestones correspond to product releases - however there are two 'magic' milestones with special meanings (not tied to a specific release): 23 | 24 | - Issues assigned to backlog: Our team is in favour of implementing the feature request/fixing the issue, however the implementation is not yet assigned to a concrete release. If and when a `backlog` issue aligns well with our roadmap, it will be scheduled for a concrete iteration. We review and update our roadmap at least once every quarter. The `backlog` list helps us shape our roadmap, but it is not the only source of input. Therefore, some `backlog` items may eventually be closed as `out of scope`, or relabelled as `backlog candidate` once it becomes clear that they do not align with our evolving roadmap. 25 | 26 | - Issues assigned to `backlog candidate`: Our team does not intend to implement the feature/fix request described in the issue and wants the community to weigh in before we make our final decision. 27 | 28 | `backlog` issues can be labeled by the owner as `help wanted` and/or `good first issue` as appropriate. 29 | 30 | 31 | 5. Promotion of `backlog candidate` issue to `backlog` issue: If an issue labelled `backlog candidate` receives more than 30 upvotes within 60 days, we promote the issue by applying the `backlog` label. While issues promoted in this manner have not been committed to a particular release, we welcome PRs from the community on them. 32 | 33 | If an issue does not make our roadmap and has not been moved to a discussion, it is closed with the label `out of scope`. The goal is to get every issue in the issues list to one of the following end states: 34 | 35 | - An assigned release. 36 | - The `backlog` label. 37 | - Closed as `out of scope`. 38 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/crds/k8s.nginx.org_globalconfigurations.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.19.0 7 | name: globalconfigurations.k8s.nginx.org 8 | spec: 9 | group: k8s.nginx.org 10 | names: 11 | kind: GlobalConfiguration 12 | listKind: GlobalConfigurationList 13 | plural: globalconfigurations 14 | shortNames: 15 | - gc 16 | singular: globalconfiguration 17 | scope: Namespaced 18 | versions: 19 | - name: v1 20 | schema: 21 | openAPIV3Schema: 22 | description: GlobalConfiguration defines the GlobalConfiguration resource. 23 | properties: 24 | apiVersion: 25 | description: |- 26 | APIVersion defines the versioned schema of this representation of an object. 27 | Servers should convert recognized schemas to the latest internal value, and 28 | may reject unrecognized values. 29 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 30 | type: string 31 | kind: 32 | description: |- 33 | Kind is a string value representing the REST resource this object represents. 34 | Servers may infer this from the endpoint the client submits requests to. 35 | Cannot be updated. 36 | In CamelCase. 37 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 38 | type: string 39 | metadata: 40 | type: object 41 | spec: 42 | description: GlobalConfigurationSpec resource defines the global configuration 43 | parameters of the Ingress Controller. 44 | properties: 45 | listeners: 46 | description: Listeners field of the GlobalConfigurationSpec resource 47 | items: 48 | description: Listener defines a listener. 49 | properties: 50 | ipv4: 51 | description: Specifies the IPv4 address to listen on. 52 | type: string 53 | ipv6: 54 | description: ipv6 addresse that NGINX will listen on. 55 | type: string 56 | name: 57 | description: The name of the listener. The name must be unique 58 | across all listeners. 59 | type: string 60 | port: 61 | description: The port on which the listener will accept connections. 62 | type: integer 63 | protocol: 64 | description: The protocol of the listener. For example, HTTP. 65 | type: string 66 | ssl: 67 | description: Whether the listener will be listening for SSL 68 | connections 69 | type: boolean 70 | type: object 71 | type: array 72 | type: object 73 | type: object 74 | served: true 75 | storage: true 76 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/crds/appprotect.f5.com_aplogconfs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.13.0 6 | name: aplogconfs.appprotect.f5.com 7 | spec: 8 | group: appprotect.f5.com 9 | names: 10 | kind: APLogConf 11 | listKind: APLogConfList 12 | plural: aplogconfs 13 | singular: aplogconf 14 | preserveUnknownFields: false 15 | scope: Namespaced 16 | versions: 17 | - name: v1beta1 18 | schema: 19 | openAPIV3Schema: 20 | description: APLogConf is the Schema for the APLogConfs API 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: APLogConfSpec defines the desired state of APLogConf 36 | properties: 37 | content: 38 | properties: 39 | escaping_characters: 40 | items: 41 | properties: 42 | from: 43 | type: string 44 | to: 45 | type: string 46 | type: object 47 | type: array 48 | format: 49 | enum: 50 | - splunk 51 | - arcsight 52 | - default 53 | - user-defined 54 | - grpc 55 | type: string 56 | format_string: 57 | type: string 58 | list_delimiter: 59 | type: string 60 | list_prefix: 61 | type: string 62 | list_suffix: 63 | type: string 64 | max_message_size: 65 | pattern: ^([1-9]|[1-5][0-9]|6[0-4])k$ 66 | type: string 67 | max_request_size: 68 | pattern: ^([1-9]|[1-9][0-9]|[1-9][0-9]{2}|[1-9][0-9]{3}|10[0-2][0-9][0-9]|[1-9]k|10k|any)$ 69 | type: string 70 | type: object 71 | filter: 72 | properties: 73 | request_type: 74 | enum: 75 | - all 76 | - illegal 77 | - blocked 78 | type: string 79 | type: object 80 | type: object 81 | type: object 82 | served: true 83 | storage: true 84 | -------------------------------------------------------------------------------- /examples/deployment-oss-min/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | In this example we deploy the NGINX Ingress Controller (edge) as a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) using the NGINX Ingress Operator for NGINX Open Source. 4 | 5 | ## Prerequisites 6 | 7 | Have the NGINX Ingress Operator deployed in your cluster. Follow [installation](../../README.md#installation) steps. 8 | If you would like to use TransportServers, refer to [this section](README.md#TransportServers) for additional pre-requisites. 9 | 10 | ## Running the example 11 | 12 | 1. Create a new namespace for our Ingress Controller instance: 13 | ``` 14 | kubectl create -f ns.yaml 15 | ``` 16 | 17 | 2. Create a new NginxIngressController resource that defines our NGINX Ingress Controller instance (**Note**: If using OpenShift, change the `image.tag` to `edge-ubi`): 18 | ``` 19 | kubectl create -f nginx-ingress-controller.yaml 20 | ``` 21 | 22 | 23 | 24 | This will deploy an NGINX Ingress Controller instance using a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) in the `my-nginx-controller` namespace. 25 | 26 | 3. Check if all resources were deployed: 27 | 28 | ``` 29 | kubectl -n my-nginx-ingress get all 30 | 31 | NAME READY STATUS RESTARTS AGE 32 | pod/my-nginx-ingress-controller-666854fb5f-f67fs 1/1 Running 0 3s 33 | 34 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 35 | service/my-nginx-ingress-controller NodePort 10.103.105.52 80:30298/TCP,443:32576/TCP 3s 36 | 37 | NAME READY UP-TO-DATE AVAILABLE AGE 38 | deployment.apps/my-nginx-ingress-controller 1/1 1 1 4s 39 | 40 | NAME DESIRED CURRENT READY AGE 41 | replicaset.apps/my-nginx-ingress-controller-666854fb5f 1 1 1 4s 42 | ``` 43 | 44 | For more information about how to configure the NGINX Ingress Controller, check the official [documentation](https://docs.nginx.com/nginx-ingress-controller/overview/). 45 | 46 | ## Remove 47 | 48 | 1. Delete the NginxIngressController: 49 | ``` 50 | kubectl delete -f nginx-ingress-controller.yaml 51 | ``` 52 | 53 | 1. Delete the namespace: 54 | ``` 55 | kubectl delete namespace my-nginx-ingress 56 | ``` 57 | 58 | ## TransportServers 59 | 60 | TransportServers provide support for TCP/UDP but are in active development and provided as a preview feature. 61 | A GlobalConfiguration resource is used to specify the TCP/UDP listeners and is required by TransportServers. 62 | To use TransportServers, you must create a GlobalConfiguration resource *after* creating the namespace and *before* starting the Operator. 63 | 64 | 65 | ``` 66 | Step 1. namespace 67 | Step 2. global configuration <--- in this order 68 | Step 3. ingress controller 69 | ... 70 | ``` 71 | 72 | ``` 73 | kubectl apply -f global-configuration.yaml 74 | ``` 75 | 76 | Then update the NginxIngressController to use the GlobalConfiguration by adding the following config to `nginx-ingress-controller.yaml` 77 | ``` 78 | globalConfiguration: my-nginx-ingress/nginx-configuration 79 | ``` 80 | 81 | For more information, check the official [documentation](https://docs.nginx.com/nginx-ingress-controller/configuration/transportserver-resource/). 82 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | labels: 6 | control-plane: controller-manager 7 | app.kubernetes.io/name: namespace 8 | app.kubernetes.io/instance: system 9 | app.kubernetes.io/component: manager 10 | app.kubernetes.io/created-by: nginx-ingress-operator 11 | app.kubernetes.io/part-of: nginx-ingress-operator 12 | app.kubernetes.io/managed-by: kustomize 13 | name: system 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: controller-manager 19 | namespace: system 20 | labels: 21 | control-plane: controller-manager 22 | app.kubernetes.io/name: deployment 23 | app.kubernetes.io/instance: controller-manager 24 | app.kubernetes.io/component: manager 25 | app.kubernetes.io/created-by: nginx-ingress-operator 26 | app.kubernetes.io/part-of: nginx-ingress-operator 27 | app.kubernetes.io/managed-by: kustomize 28 | spec: 29 | selector: 30 | matchLabels: 31 | control-plane: controller-manager 32 | replicas: 1 33 | template: 34 | metadata: 35 | annotations: 36 | kubectl.kubernetes.io/default-container: manager 37 | labels: 38 | control-plane: controller-manager 39 | spec: 40 | affinity: 41 | nodeAffinity: 42 | requiredDuringSchedulingIgnoredDuringExecution: 43 | nodeSelectorTerms: 44 | - matchExpressions: 45 | - key: kubernetes.io/arch 46 | operator: In 47 | values: 48 | - amd64 49 | - arm64 50 | - key: kubernetes.io/os 51 | operator: In 52 | values: 53 | - linux 54 | securityContext: 55 | runAsNonRoot: true 56 | # TODO(user): For common cases that do not require escalating privileges 57 | # it is recommended to ensure that all your Pods/Containers are restrictive. 58 | # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted 59 | # Please uncomment the following code if your project does NOT have to work on old Kubernetes 60 | # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). 61 | # seccompProfile: 62 | # type: RuntimeDefault 63 | containers: 64 | - args: 65 | - --leader-elect 66 | - --leader-election-id=nginx-ingress-operator 67 | image: controller:latest 68 | name: manager 69 | securityContext: 70 | allowPrivilegeEscalation: false 71 | capabilities: 72 | drop: 73 | - "ALL" 74 | livenessProbe: 75 | httpGet: 76 | path: /healthz 77 | port: 8081 78 | initialDelaySeconds: 15 79 | periodSeconds: 20 80 | readinessProbe: 81 | httpGet: 82 | path: /readyz 83 | port: 8081 84 | initialDelaySeconds: 5 85 | periodSeconds: 10 86 | # TODO(user): Configure the resources accordingly based on the project requirements. 87 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 88 | resources: 89 | limits: 90 | cpu: 500m 91 | memory: 1Gi 92 | requests: 93 | cpu: 250m 94 | memory: 128Mi 95 | serviceAccountName: controller-manager 96 | terminationGracePeriodSeconds: 10 97 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project and everyone participating in it is governed by this code. 4 | 5 | ## Our Pledge 6 | 7 | In the interest of fostering an open and welcoming environment, we as 8 | contributors and maintainers pledge to making participation in our project and 9 | our community a harassment-free experience for everyone, regardless of age, body 10 | size, disability, ethnicity, sex characteristics, gender identity and expression, 11 | level of experience, education, socio-economic status, nationality, personal 12 | appearance, race, religion, or sexual identity and orientation. 13 | 14 | ## Our Standards 15 | 16 | Examples of behavior that contributes to creating a positive environment 17 | include: 18 | 19 | * Using welcoming and inclusive language 20 | * Being respectful of differing viewpoints and experiences 21 | * Gracefully accepting constructive criticism 22 | * Focusing on what is best for the community 23 | * Showing empathy towards other community members 24 | 25 | Examples of unacceptable behavior by participants include: 26 | 27 | * The use of sexualized language or imagery and unwelcome sexual attention or 28 | advances 29 | * Trolling, insulting/derogatory comments, and personal or political attacks 30 | * Public or private harassment 31 | * Publishing others' private information, such as a physical or electronic 32 | address, without explicit permission 33 | * Other conduct which could reasonably be considered inappropriate in a 34 | professional setting 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying the standards of acceptable 39 | behavior and are expected to take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or 43 | reject comments, commits, code, wiki edits, issues, and other contributions 44 | that are not aligned to this Code of Conduct, or to ban temporarily or 45 | permanently any contributor for other behaviors that they deem inappropriate, 46 | threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies both within project spaces and in public spaces 51 | when an individual is representing the project or its community. Examples of 52 | representing a project or community include using an official project e-mail 53 | address, posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. Representation of a project may be 55 | further defined and clarified by project maintainers. 56 | 57 | ## Enforcement 58 | 59 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 60 | reported by contacting the project team at [mailto:nginx@nginx.org]. All 61 | complaints will be reviewed and investigated and will result in a response that 62 | is deemed necessary and appropriate to the circumstances. The project team is 63 | obligated to maintain confidentiality with regard to the reporter of an incident. 64 | Further details of specific enforcement policies may be posted separately. 65 | 66 | Project maintainers who do not follow or enforce the Code of Conduct in good 67 | faith may face temporary or permanent repercussions as determined by other 68 | members of the project's leadership. 69 | 70 | ## Attribution 71 | 72 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 73 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 74 | 75 | [homepage]: https://www.contributor-covenant.org -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- if not .Values.controller.customConfigMap -}} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ include "nginx-ingress.configName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.controller.config.annotations }} 10 | annotations: 11 | {{ toYaml .Values.controller.config.annotations | indent 4 }} 12 | {{- end }} 13 | data: 14 | {{ toYaml (default dict .Values.controller.config.entries) | indent 2 }} 15 | {{- end }} 16 | --- 17 | {{- if and .Values.nginxAgent.enable (eq (.Values.nginxAgent.customConfigMap | default "") "") }} 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: {{ include "nginx-ingress.agentConfigName" . }} 22 | namespace: {{ .Release.Namespace }} 23 | labels: 24 | {{- include "nginx-ingress.labels" . | nindent 4 }} 25 | {{- if .Values.controller.config.annotations }} 26 | annotations: 27 | {{ toYaml .Values.controller.config.annotations | indent 4 }} 28 | {{- end }} 29 | data: 30 | nginx-agent.conf: |- 31 | {{ include "nginx-ingress.agentConfiguration" . | indent 4 }} 32 | {{- end }} 33 | --- 34 | {{- if .Values.controller.nginxplus }} 35 | apiVersion: v1 36 | kind: ConfigMap 37 | metadata: 38 | name: {{ include "nginx-ingress.mgmtConfigName" . }} 39 | namespace: {{ .Release.Namespace }} 40 | labels: 41 | {{- include "nginx-ingress.labels" . | nindent 4 }} 42 | {{- if .Values.controller.config.annotations }} 43 | annotations: 44 | {{ toYaml .Values.controller.config.annotations | indent 4 }} 45 | {{- end }} 46 | data: 47 | license-token-secret-name: {{ required "When using Nginx Plus, 'controller.mgmt.licenseTokenSecretName' cannot be empty " (include "nginx-ingress.licenseTokenSecretName" . ) }} 48 | {{- if hasKey .Values.controller.mgmt "sslVerify" }} 49 | ssl-verify: {{ quote .Values.controller.mgmt.sslVerify }} 50 | {{- end }} 51 | {{- if hasKey .Values.controller.mgmt "enforceInitialReport" }} 52 | enforce-initial-report: {{ quote .Values.controller.mgmt.enforceInitialReport }} 53 | {{- end }} 54 | {{- if hasKey .Values.controller.mgmt "usageReport" }} 55 | {{- if hasKey .Values.controller.mgmt.usageReport "endpoint" }} 56 | usage-report-endpoint: {{ quote .Values.controller.mgmt.usageReport.endpoint }} 57 | {{- end }} 58 | {{- if hasKey .Values.controller.mgmt.usageReport "interval" }} 59 | usage-report-interval: {{ quote .Values.controller.mgmt.usageReport.interval }} 60 | {{- end }} 61 | {{- if hasKey .Values.controller.mgmt.usageReport "proxyHost" }} 62 | usage-report-proxy-host: {{ quote .Values.controller.mgmt.usageReport.proxyHost }} 63 | {{- end }} 64 | {{- end }} 65 | {{- if hasKey .Values.controller.mgmt "sslTrustedCertificateSecretName" }} 66 | ssl-trusted-certificate-secret-name: {{ quote .Values.controller.mgmt.sslTrustedCertificateSecretName }} 67 | {{- end }} 68 | {{- if hasKey .Values.controller.mgmt "sslCertificateSecretName" }} 69 | ssl-certificate-secret-name: {{ quote .Values.controller.mgmt.sslCertificateSecretName}} 70 | {{- end }} 71 | {{- if hasKey .Values.controller.mgmt "resolver" }} 72 | {{- if hasKey .Values.controller.mgmt.resolver "addresses" }} 73 | resolver-addresses: {{ join "," .Values.controller.mgmt.resolver.addresses | quote }} 74 | {{- end }} 75 | {{- if hasKey .Values.controller.mgmt.resolver "ipv6" }} 76 | resolver-ipv6: {{ quote .Values.controller.mgmt.resolver.ipv6 }} 77 | {{- end }} 78 | {{- if hasKey .Values.controller.mgmt.resolver "valid" }} 79 | resolver-valid: {{ quote .Values.controller.mgmt.resolver.valid }} 80 | {{- end }} 81 | {{- end }} 82 | {{- end }} 83 | -------------------------------------------------------------------------------- /examples/default-server-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: default-server-secret 5 | namespace: nginx-ingress 6 | type: kubernetes.io/tls 7 | data: 8 | tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN2akNDQWFZQ0NRREFPRjl0THNhWFhEQU5CZ2txaGtpRzl3MEJBUXNGQURBaE1SOHdIUVlEVlFRRERCWk8KUjBsT1dFbHVaM0psYzNORGIyNTBjbTlzYkdWeU1CNFhEVEU0TURreE1qRTRNRE16TlZvWERUSXpNRGt4TVRFNApNRE16TlZvd0lURWZNQjBHQTFVRUF3d1dUa2RKVGxoSmJtZHlaWE56UTI5dWRISnZiR3hsY2pDQ0FTSXdEUVlKCktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUwvN2hIUEtFWGRMdjNyaUM3QlBrMTNpWkt5eTlyQ08KR2xZUXYyK2EzUDF0azIrS3YwVGF5aGRCbDRrcnNUcTZzZm8vWUk1Y2Vhbkw4WGM3U1pyQkVRYm9EN2REbWs1Qgo4eDZLS2xHWU5IWlg0Rm5UZ0VPaStlM2ptTFFxRlBSY1kzVnNPazFFeUZBL0JnWlJVbkNHZUtGeERSN0tQdGhyCmtqSXVuektURXUyaDU4Tlp0S21ScUJHdDEwcTNRYzhZT3ExM2FnbmovUWRjc0ZYYTJnMjB1K1lYZDdoZ3krZksKWk4vVUkxQUQ0YzZyM1lma1ZWUmVHd1lxQVp1WXN2V0RKbW1GNWRwdEMzN011cDBPRUxVTExSakZJOTZXNXIwSAo1TmdPc25NWFJNV1hYVlpiNWRxT3R0SmRtS3FhZ25TZ1JQQVpQN2MwQjFQU2FqYzZjNGZRVXpNQ0F3RUFBVEFOCkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQWpLb2tRdGRPcEsrTzhibWVPc3lySmdJSXJycVFVY2ZOUitjb0hZVUoKdGhrYnhITFMzR3VBTWI5dm15VExPY2xxeC9aYzJPblEwMEJCLzlTb0swcitFZ1U2UlVrRWtWcitTTFA3NTdUWgozZWI4dmdPdEduMS9ienM3bzNBaS9kclkrcUI5Q2k1S3lPc3FHTG1US2xFaUtOYkcyR1ZyTWxjS0ZYQU80YTY3Cklnc1hzYktNbTQwV1U3cG9mcGltU1ZmaXFSdkV5YmN3N0NYODF6cFErUyt1eHRYK2VBZ3V0NHh3VlI5d2IyVXYKelhuZk9HbWhWNThDd1dIQnNKa0kxNXhaa2VUWXdSN0diaEFMSkZUUkk3dkhvQXprTWIzbjAxQjQyWjNrN3RXNQpJUDFmTlpIOFUvOWxiUHNoT21FRFZkdjF5ZytVRVJxbStGSis2R0oxeFJGcGZnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 9 | tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdi91RWM4b1JkMHUvZXVJTHNFK1RYZUprckxMMnNJNGFWaEMvYjVyYy9XMlRiNHEvClJOcktGMEdYaVN1eE9ycXgrajlnamx4NXFjdnhkenRKbXNFUkJ1Z1B0ME9hVGtIekhvb3FVWmcwZGxmZ1dkT0EKUTZMNTdlT1l0Q29VOUZ4amRXdzZUVVRJVUQ4R0JsRlNjSVo0b1hFTkhzbysyR3VTTWk2Zk1wTVM3YUhudzFtMApxWkdvRWEzWFNyZEJ6eGc2clhkcUNlUDlCMXl3VmRyYURiUzc1aGQzdUdETDU4cGszOVFqVUFQaHpxdmRoK1JWClZGNGJCaW9CbTVpeTlZTW1hWVhsMm0wTGZzeTZuUTRRdFFzdEdNVWozcGJtdlFmazJBNnljeGRFeFpkZFZsdmwKMm82MjBsMllxcHFDZEtCRThCay90elFIVTlKcU56cHpoOUJUTXdJREFRQUJBb0lCQVFDZklHbXowOHhRVmorNwpLZnZJUXQwQ0YzR2MxNld6eDhVNml4MHg4Mm15d1kxUUNlL3BzWE9LZlRxT1h1SENyUlp5TnUvZ2IvUUQ4bUFOCmxOMjRZTWl0TWRJODg5TEZoTkp3QU5OODJDeTczckM5bzVvUDlkazAvYzRIbjAzSkVYNzZ5QjgzQm9rR1FvYksKMjhMNk0rdHUzUmFqNjd6Vmc2d2szaEhrU0pXSzBwV1YrSjdrUkRWYmhDYUZhNk5nMUZNRWxhTlozVDhhUUtyQgpDUDNDeEFTdjYxWTk5TEI4KzNXWVFIK3NYaTVGM01pYVNBZ1BkQUk3WEh1dXFET1lvMU5PL0JoSGt1aVg2QnRtCnorNTZud2pZMy8yUytSRmNBc3JMTnIwMDJZZi9oY0IraVlDNzVWYmcydVd6WTY3TWdOTGQ5VW9RU3BDRkYrVm4KM0cyUnhybnhBb0dCQU40U3M0ZVlPU2huMVpQQjdhTUZsY0k2RHR2S2ErTGZTTXFyY2pOZjJlSEpZNnhubmxKdgpGenpGL2RiVWVTbWxSekR0WkdlcXZXaHFISy9iTjIyeWJhOU1WMDlRQ0JFTk5jNmtWajJTVHpUWkJVbEx4QzYrCk93Z0wyZHhKendWelU0VC84ajdHalRUN05BZVpFS2FvRHFyRG5BYWkyaW5oZU1JVWZHRXFGKzJyQW9HQkFOMVAKK0tZL0lsS3RWRzRKSklQNzBjUis3RmpyeXJpY05iWCtQVzUvOXFHaWxnY2grZ3l4b25BWlBpd2NpeDN3QVpGdwpaZC96ZFB2aTBkWEppc1BSZjRMazg5b2pCUmpiRmRmc2l5UmJYbyt3TFU4NUhRU2NGMnN5aUFPaTVBRHdVU0FkCm45YWFweUNweEFkREtERHdObit3ZFhtaTZ0OHRpSFRkK3RoVDhkaVpBb0dCQUt6Wis1bG9OOTBtYlF4VVh5YUwKMjFSUm9tMGJjcndsTmVCaWNFSmlzaEhYa2xpSVVxZ3hSZklNM2hhUVRUcklKZENFaHFsV01aV0xPb2I2NTNyZgo3aFlMSXM1ZUtka3o0aFRVdnpldm9TMHVXcm9CV2xOVHlGanIrSWhKZnZUc0hpOGdsU3FkbXgySkJhZUFVWUNXCndNdlQ4NmNLclNyNkQrZG8wS05FZzFsL0FvR0FlMkFVdHVFbFNqLzBmRzgrV3hHc1RFV1JqclRNUzRSUjhRWXQKeXdjdFA4aDZxTGxKUTRCWGxQU05rMXZLTmtOUkxIb2pZT2pCQTViYjhibXNVU1BlV09NNENoaFJ4QnlHbmR2eAphYkJDRkFwY0IvbEg4d1R0alVZYlN5T294ZGt5OEp0ek90ajJhS0FiZHd6NlArWDZDODhjZmxYVFo5MWpYL3RMCjF3TmRKS2tDZ1lCbyt0UzB5TzJ2SWFmK2UwSkN5TGhzVDQ5cTN3Zis2QWVqWGx2WDJ1VnRYejN5QTZnbXo5aCsKcDNlK2JMRUxwb3B0WFhNdUFRR0xhUkcrYlNNcjR5dERYbE5ZSndUeThXczNKY3dlSTdqZVp2b0ZpbmNvVlVIMwphdmxoTUVCRGYxSjltSDB5cDBwWUNaS2ROdHNvZEZtQktzVEtQMjJhTmtsVVhCS3gyZzR6cFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= 10 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.controller.service.create }} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ include "nginx-ingress.controller.service.name" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.controller.service.extraLabels }} 10 | {{ toYaml .Values.controller.service.extraLabels | indent 4 }} 11 | {{- end }} 12 | {{- if .Values.controller.service.annotations }} 13 | annotations: 14 | {{ toYaml .Values.controller.service.annotations | indent 4 }} 15 | {{- end }} 16 | spec: 17 | {{- if .Values.controller.service.clusterIP }} 18 | clusterIP: {{ .Values.controller.service.clusterIP }} 19 | {{- end }} 20 | {{- if or (eq .Values.controller.service.type "LoadBalancer") (eq .Values.controller.service.type "NodePort") }} 21 | {{- if .Values.controller.service.externalTrafficPolicy }} 22 | externalTrafficPolicy: {{ .Values.controller.service.externalTrafficPolicy }} 23 | {{- end }} 24 | {{- end }} 25 | {{- if eq .Values.controller.service.type "LoadBalancer" }} 26 | {{- if hasKey .Values.controller.service "allocateLoadBalancerNodePorts" }} 27 | allocateLoadBalancerNodePorts: {{ .Values.controller.service.allocateLoadBalancerNodePorts }} 28 | {{- end }} 29 | {{- if .Values.controller.service.loadBalancerIP }} 30 | loadBalancerIP: {{ .Values.controller.service.loadBalancerIP }} 31 | {{- end }} 32 | {{- if .Values.controller.service.loadBalancerSourceRanges }} 33 | loadBalancerSourceRanges: 34 | {{ toYaml .Values.controller.service.loadBalancerSourceRanges | indent 4 }} 35 | {{- end }} 36 | {{- end }} 37 | type: {{ .Values.controller.service.type }} 38 | {{- if .Values.controller.service.ipFamilyPolicy }} 39 | ipFamilyPolicy: {{ .Values.controller.service.ipFamilyPolicy }} 40 | {{- end }} 41 | {{- if .Values.controller.service.ipFamilies }} 42 | ipFamilies: 43 | {{ toYaml .Values.controller.service.ipFamilies | indent 4 }} 44 | {{- end }} 45 | ports: 46 | {{- if .Values.controller.service.customPorts }} 47 | {{ toYaml .Values.controller.service.customPorts | indent 2 }} 48 | {{ end }} 49 | {{- if .Values.controller.service.httpPort.enable }} 50 | - port: {{ .Values.controller.service.httpPort.port }} 51 | targetPort: {{ .Values.controller.service.httpPort.targetPort }} 52 | protocol: TCP 53 | name: {{ .Values.controller.service.httpPort.name }} 54 | {{- if or (eq .Values.controller.service.type "LoadBalancer") (eq .Values.controller.service.type "NodePort") }} 55 | nodePort: {{ .Values.controller.service.httpPort.nodePort }} 56 | {{- end }} 57 | {{- end }} 58 | {{- if .Values.controller.service.httpsPort.enable }} 59 | - port: {{ .Values.controller.service.httpsPort.port }} 60 | targetPort: {{ .Values.controller.service.httpsPort.targetPort }} 61 | protocol: TCP 62 | name: {{ .Values.controller.service.httpsPort.name }} 63 | {{- if or (eq .Values.controller.service.type "LoadBalancer") (eq .Values.controller.service.type "NodePort") }} 64 | nodePort: {{ .Values.controller.service.httpsPort.nodePort }} 65 | {{- end }} 66 | {{- end }} 67 | selector: 68 | {{- include "nginx-ingress.selectorLabels" . | nindent 4 }} 69 | {{- if .Values.controller.service.sessionAffinity.enable }} 70 | sessionAffinity: {{ .Values.controller.service.sessionAffinity.type }} 71 | {{- if eq .Values.controller.service.sessionAffinity.type "ClientIP" }} 72 | sessionAffinityConfig: 73 | clientIP: 74 | timeoutSeconds: {{ .Values.controller.service.sessionAffinity.timeoutSeconds }} 75 | {{- end }} 76 | {{- end }} 77 | {{- if .Values.controller.service.externalIPs }} 78 | externalIPs: 79 | {{ toYaml .Values.controller.service.externalIPs | indent 4 }} 80 | {{- end }} 81 | {{- end }} 82 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/crds/appprotect.f5.com_apusersigs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.13.0 6 | name: apusersigs.appprotect.f5.com 7 | spec: 8 | group: appprotect.f5.com 9 | names: 10 | kind: APUserSig 11 | listKind: APUserSigList 12 | plural: apusersigs 13 | singular: apusersig 14 | preserveUnknownFields: false 15 | scope: Namespaced 16 | versions: 17 | - name: v1beta1 18 | schema: 19 | openAPIV3Schema: 20 | description: APUserSig is the Schema for the apusersigs API 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: APUserSigSpec defines the desired state of APUserSig 36 | properties: 37 | properties: 38 | type: string 39 | signatures: 40 | items: 41 | properties: 42 | accuracy: 43 | enum: 44 | - high 45 | - medium 46 | - low 47 | type: string 48 | attackType: 49 | properties: 50 | name: 51 | type: string 52 | type: object 53 | description: 54 | type: string 55 | name: 56 | type: string 57 | references: 58 | properties: 59 | type: 60 | enum: 61 | - bugtraq 62 | - cve 63 | - nessus 64 | - url 65 | type: string 66 | value: 67 | type: string 68 | type: object 69 | risk: 70 | enum: 71 | - high 72 | - medium 73 | - low 74 | type: string 75 | rule: 76 | type: string 77 | signatureType: 78 | enum: 79 | - request 80 | - response 81 | type: string 82 | systems: 83 | items: 84 | properties: 85 | name: 86 | type: string 87 | type: object 88 | type: array 89 | type: object 90 | type: array 91 | softwareVersion: 92 | type: string 93 | tag: 94 | type: string 95 | type: object 96 | type: object 97 | served: true 98 | storage: true 99 | -------------------------------------------------------------------------------- /bundle/manifests/nginx-ingress-operator-nginx-ingress-admin_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | name: nginx-ingress-operator-nginx-ingress-admin 6 | rules: 7 | - apiGroups: 8 | - appprotect.f5.com 9 | resources: 10 | - appolicies 11 | - aplogconfs 12 | - apusersigs 13 | verbs: 14 | - get 15 | - watch 16 | - list 17 | - apiGroups: 18 | - appprotectdos.f5.com 19 | resources: 20 | - apdospolicies 21 | - apdoslogconfs 22 | - dosprotectedresources 23 | verbs: 24 | - get 25 | - watch 26 | - list 27 | - apiGroups: 28 | - discovery.k8s.io 29 | resources: 30 | - endpointslices 31 | verbs: 32 | - get 33 | - list 34 | - watch 35 | - apiGroups: 36 | - "" 37 | resources: 38 | - services 39 | verbs: 40 | - get 41 | - list 42 | - watch 43 | - create 44 | - update 45 | - delete 46 | - patch 47 | - apiGroups: 48 | - "" 49 | resources: 50 | - secrets 51 | verbs: 52 | - get 53 | - list 54 | - watch 55 | - apiGroups: 56 | - "" 57 | resources: 58 | - configmaps 59 | verbs: 60 | - get 61 | - list 62 | - watch 63 | - update 64 | - create 65 | - apiGroups: 66 | - apps 67 | resources: 68 | - daemonsets 69 | - deployments 70 | - replicasets 71 | - statefulsets 72 | verbs: 73 | - get 74 | - apiGroups: 75 | - "" 76 | resources: 77 | - nodes 78 | verbs: 79 | - list 80 | - apiGroups: 81 | - "" 82 | resources: 83 | - pods 84 | verbs: 85 | - get 86 | - list 87 | - watch 88 | - update 89 | - apiGroups: 90 | - security.openshift.io 91 | resourceNames: 92 | - nginx-ingress-admin 93 | resources: 94 | - securitycontextconstraints 95 | verbs: 96 | - use 97 | - apiGroups: 98 | - "" 99 | resources: 100 | - namespaces 101 | verbs: 102 | - get 103 | - list 104 | - watch 105 | - apiGroups: 106 | - "" 107 | resources: 108 | - events 109 | verbs: 110 | - create 111 | - patch 112 | - list 113 | - apiGroups: 114 | - coordination.k8s.io 115 | resources: 116 | - leases 117 | verbs: 118 | - get 119 | - list 120 | - watch 121 | - update 122 | - create 123 | - apiGroups: 124 | - networking.k8s.io 125 | resources: 126 | - ingresses 127 | verbs: 128 | - get 129 | - list 130 | - watch 131 | - apiGroups: 132 | - networking.k8s.io 133 | resources: 134 | - ingressclasses 135 | verbs: 136 | - get 137 | - apiGroups: 138 | - networking.k8s.io 139 | resources: 140 | - ingresses/status 141 | verbs: 142 | - update 143 | - apiGroups: 144 | - k8s.nginx.org 145 | resources: 146 | - virtualservers 147 | - virtualserverroutes 148 | - globalconfigurations 149 | - transportservers 150 | - policies 151 | verbs: 152 | - list 153 | - watch 154 | - get 155 | - apiGroups: 156 | - k8s.nginx.org 157 | resources: 158 | - virtualservers/status 159 | - virtualserverroutes/status 160 | - policies/status 161 | - transportservers/status 162 | - virtualservers/finalizers 163 | verbs: 164 | - update 165 | - apiGroups: 166 | - cis.f5.com 167 | resources: 168 | - ingresslinks 169 | verbs: 170 | - list 171 | - watch 172 | - get 173 | - apiGroups: 174 | - cert-manager.io 175 | resources: 176 | - certificates 177 | verbs: 178 | - list 179 | - watch 180 | - get 181 | - update 182 | - create 183 | - delete 184 | - apiGroups: 185 | - externaldns.nginx.org 186 | resources: 187 | - dnsendpoints 188 | verbs: 189 | - list 190 | - watch 191 | - get 192 | - update 193 | - create 194 | - delete 195 | - apiGroups: 196 | - externaldns.nginx.org 197 | resources: 198 | - dnsendpoints/status 199 | verbs: 200 | - update 201 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | The following is a set of guidelines for contributing to the NGINX Ingress Operator. We really appreciate that you are considering contributing! 4 | 5 | #### Table Of Contents 6 | 7 | [Ask a Question](#ask-a-question) 8 | 9 | [Getting Started](#getting-started) 10 | 11 | [Contributing](#contributing) 12 | 13 | [Style Guides](#style-guides) 14 | * [Git Style Guide](#git-style-guide) 15 | 16 | [Code of Conduct](CODE_OF_CONDUCT.md) 17 | 18 | ## Ask a Question 19 | 20 | To ask a question please use [Github Discussions](https://github.com/nginx/kubernetes-ingress/discussions). 21 | 22 | You can also get help through the [NGINX Community Forum](https://community.nginx.org/). 23 | 24 | Please reserve GitHub issues for feature requests and bugs rather than general questions. 25 | 26 | ## Getting Started 27 | 28 | Follow our [Installation Guide](docs/installation.md) to get the NGINX Ingress Operator up and running. 29 | 30 | Read the [documentation](https://github.com/nginx/nginx-ingress-helm-operator/tree/main/docs) . 31 | 32 | ### Project Structure 33 | 34 | * This Operator was created the using the Helm operator-framework and supports both the open source NGINX Ingress Controller and NGINX Plus Ingress Controller. It supports the same NGINX Ingress Controller features as the NGINX Ingress Controller Helm chart. 35 | 36 | ## Contributing 37 | 38 | ### Report a Bug 39 | 40 | To report a bug, open an issue on GitHub with the label `bug` using the available bug report issue template. Please ensure the issue has not already been reported. 41 | 42 | ### Suggest an Enhancement 43 | 44 | To suggest an enhancement, please create an issue on GitHub with the label `enhancement` using the available feature issue template. 45 | 46 | ### Open a Pull Request 47 | 48 | * Fork the repo, create a branch, submit a PR when your changes are tested and ready for review 49 | * Fill in [our pull request template](.github/PULL_REQUEST_TEMPLATE.md) 50 | 51 | > **Note** 52 | > 53 | > If you’d like to implement a new feature, please consider creating a feature request issue first to start a discussion about the feature. 54 | 55 | ### Issue lifecycle 56 | 57 | * When an issue or PR is created, it will be triaged by the core development team and assigned a label to indicate the type of issue it is (bug, feature request, etc) and to determine the milestone. Please see the [Issue Lifecycle](ISSUE_LIFECYCLE.md) document for more information. 58 | 59 | ### F5 Contributor License Agreement (CLA) 60 | 61 | F5 requires all external contributors to agree to the terms of the F5 CLA (available [here](https://github.com/f5/.github/blob/main/CLA/cla-markdown.md)) 62 | before any of their changes can be incorporated into an F5 Open Source repository. 63 | 64 | If you have not yet agreed to the F5 CLA terms and submit a PR to this repository, a bot will prompt you to view and 65 | agree to the F5 CLA. You will have to agree to the F5 CLA terms through a comment in the PR before any of your changes 66 | can be merged. Your agreement signature will be safely stored by F5 and no longer be required in future PRs. 67 | 68 | ## Style Guides 69 | 70 | ### Git Style Guide 71 | 72 | * Keep a clean, concise and meaningful git commit history on your branch, rebasing locally and squashing before submitting a PR 73 | * Follow the guidelines of writing a good commit message as described here https://chris.beams.io/posts/git-commit/ and summarized in the next few points 74 | * In the subject line, use the present tense ("Add feature" not "Added feature") 75 | * In the subject line, use the imperative mood ("Move cursor to..." not "Moves cursor to...") 76 | * Limit the subject line to 72 characters or less 77 | * Reference issues and pull requests liberally after the subject line 78 | * Add more detailed description in the body of the git message (`git commit -a` to give you more space and time in your text editor to write a good message instead of `git commit -am`) 79 | -------------------------------------------------------------------------------- /examples/deployment-plus-min/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | In this example we deploy the NGINX Ingress Controller (edge) as a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) using the NGINX Ingress Operator for NGINX Plus. 4 | 5 | ## Prerequisites 6 | 7 | 1. Have the NGINX Ingress Operator deployed in your cluster. Follow [installation](../../README.md#installation) steps. 8 | 2. Build the NGINX Ingress Controller for Plus image and push it to a private repository following 9 | [these instructions](https://docs.nginx.com/nginx-ingress-controller/install/build/#building-the-image-and-pushing-it-to-the-private-registry) 10 | (**Note**: For the build process, if using OpenShift, use the `ubi-image-plus`, `ubi-image-nap-plus`, `ubi-image-dos-plus` or `ubi-image-nap-dos-plus` targets). 11 | 3. Setup your NGINX Plus [license](https://docs.nginx.com/nginx-ingress-controller/install/license-secret/) `Secret` 12 | 13 | If you would like to use TransportServers, refer to [this section](README.md#TransportServers) for additional pre-requisites. 14 | 15 | ## Running the example 16 | 17 | 1. Create a new namespace for our Ingress Controller instance: 18 | ``` 19 | kubectl create -f ns.yaml 20 | ``` 21 | 22 | 2. Create a new NginxIngressController resource that defines our NGINX Ingress Controller instance (**Note:** Update the `image.repository` field in the `nginx-ingress-controller.yaml` with your previously built image for NGINX Plus): 23 | ``` 24 | kubectl create -f nginx-ingress-controller.yaml 25 | ``` 26 | 27 | This will deploy an NGINX Ingress Controller instance using a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) in the `my-nginx-controller` namespace. 28 | 29 | 3. Check if all resources were deployed: 30 | 31 | ``` 32 | kubectl -n my-nginx-ingress get all 33 | 34 | NAME READY STATUS RESTARTS AGE 35 | pod/my-nginx-ingress-controller-666854fb5f-f67fs 1/1 Running 0 3s 36 | 37 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 38 | service/my-nginx-ingress-controller NodePort 10.103.105.52 80:30298/TCP,443:32576/TCP 3s 39 | 40 | NAME READY UP-TO-DATE AVAILABLE AGE 41 | deployment.apps/my-nginx-ingress-controller 1/1 1 1 4s 42 | 43 | NAME DESIRED CURRENT READY AGE 44 | replicaset.apps/my-nginx-ingress-controller-666854fb5f 1 1 1 4s 45 | ``` 46 | 47 | For more information about how to configure the NGINX Ingress Controller, check the official [documentation](https://docs.nginx.com/nginx-ingress-controller/overview/). 48 | 49 | ## Remove 50 | 51 | 1. Delete the NginxIngressController: 52 | ``` 53 | kubectl delete -f nginx-ingress-controller.yaml 54 | ``` 55 | 56 | 1. Delete the namespace: 57 | ``` 58 | kubectl delete namespace my-nginx-ingress 59 | ``` 60 | 61 | ## TransportServers 62 | 63 | TransportServers provide support for TCP/UDP but are in active development and provided as a preview feature. 64 | A GlobalConfiguration resource is used to specify the TCP/UDP listeners and is required by TransportServers. 65 | To use TransportServers, you must create a GlobalConfiguration resource *after* creating the namespace and *before* starting the Operator. 66 | 67 | 68 | ``` 69 | Step 1. namespace 70 | Step 2. global configuration <--- in this order 71 | Step 3. ingress controller 72 | ... 73 | ``` 74 | 75 | 76 | ``` 77 | kubectl apply -f global-configuration.yaml 78 | ``` 79 | 80 | Then update the NginxIngressController to use the GlobalConfiguration by adding the following config to `nginx-ingress-controller.yaml` 81 | ``` 82 | globalConfiguration: my-nginx-ingress/nginx-configuration 83 | ``` 84 | 85 | For more information, check the official [documentation](https://docs.nginx.com/nginx-ingress-controller/configuration/transportserver-resource/). 86 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/crds/externaldns.nginx.org_dnsendpoints.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.19.0 7 | name: dnsendpoints.externaldns.nginx.org 8 | spec: 9 | group: externaldns.nginx.org 10 | names: 11 | kind: DNSEndpoint 12 | listKind: DNSEndpointList 13 | plural: dnsendpoints 14 | singular: dnsendpoint 15 | scope: Namespaced 16 | versions: 17 | - name: v1 18 | schema: 19 | openAPIV3Schema: 20 | description: DNSEndpoint is the CRD wrapper for Endpoint 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: DNSEndpointSpec holds information about endpoints. 41 | properties: 42 | endpoints: 43 | items: 44 | description: Endpoint describes DNS Endpoint. 45 | properties: 46 | dnsName: 47 | description: The hostname for the DNS record 48 | type: string 49 | labels: 50 | additionalProperties: 51 | type: string 52 | description: Labels stores labels defined for the Endpoint 53 | type: object 54 | providerSpecific: 55 | description: ProviderSpecific stores provider specific config 56 | items: 57 | description: ProviderSpecificProperty represents provider 58 | specific config property. 59 | properties: 60 | name: 61 | description: Name of the property 62 | type: string 63 | value: 64 | description: Value of the property 65 | type: string 66 | type: object 67 | type: array 68 | recordTTL: 69 | description: TTL for the record 70 | format: int64 71 | type: integer 72 | recordType: 73 | description: RecordType type of record, e.g. CNAME, A, SRV, 74 | TXT, MX 75 | type: string 76 | targets: 77 | description: The targets the DNS service points to 78 | items: 79 | type: string 80 | type: array 81 | type: object 82 | type: array 83 | type: object 84 | status: 85 | description: DNSEndpointStatus represents generation observed by the external 86 | dns controller. 87 | properties: 88 | observedGeneration: 89 | description: The generation observed by by the external-dns controller. 90 | format: int64 91 | type: integer 92 | type: object 93 | type: object 94 | served: true 95 | storage: true 96 | subresources: 97 | status: {} 98 | -------------------------------------------------------------------------------- /docs/upgrades.md: -------------------------------------------------------------------------------- 1 | # Upgrade - 0.5.1 to 1.0.0 2 | 3 | Release 1.0.0 includes a backward incompatible change from version 0.5.1 as we have moved from a Go based operator to a Helm based operator. 4 | 5 | ## OLM upgrade - 0.5.1 to 1.0.0 6 | 7 | **Note: The `nginx-ingress-operator` supports `Basic Install` only - we do not support auto-updates. When you are installing the Operator using the OLM, the auto-update feature should be disabled to avoid breaking changes being auto-applied. In OpenShift, this can be done by setting the `Approval Strategy` to `Manual`. Please see the [Operator SDK docs](https://sdk.operatorframework.io/docs/advanced-topics/operator-capabilities/operator-capabilities/) for more details on the Operator Capability Levels.** 8 | 9 | 1. Upgrade CRDs 10 | 2. Uninstall Go operator -> this will also remove any instances of the NginxIngressController, but not any dependent objects (ingresses, VSs, etc) 11 | 3. Remove the nginx-ingress ingressClass `kubectl delete ingressclass/nginx` 12 | 4. Install new operator 13 | 5. Deploy common resources (scc, default server secret, ns, etc). 14 | **Note: Multiple NIC deployments: the RBAC resources should be deployed separately if deploying multiple ICs in same namespace. This is because only one of the ICs in a namespace will be assigned "ownership" of these resources. Similarly, the IngressClass resource needs to be created separately if deploying multiple NIC instances with a shared IngressClass. See the [README](../README.md) for more information** 15 | 6. Re-create ingress controllers (note: multi IC rules) using the new Operator. Be sure to use the same configuration as the previous deployments (ingress class name, namespaces etc). They will pick up all deployed dependent resources. 16 | 17 | ### 1. Upgrade the existing NIC crds 18 | 19 | Navigate [here](../helm-charts/nginx-ingress/) and run `kubectl apply -f crds/` 20 | 21 | ### 2. Uninstall the existing 0.5.1 operator, the nginx ingress controller CRD, and the ingressClass 22 | 23 | Uninstall the operator using the web console - see [the OCP documentation for details](https://access.redhat.com/documentation/en-us/openshift_container_platform/4.13/pdf/operators/OpenShift_Container_Platform-4.13-Operators-en-US.pdf). 24 | 25 | Next uninstall the NIC CRD `nginxingresscontrollers.k8s.nginx.org`. This will remove any instances of the NginxIngressController, but not any dependent objects (ingresses, VSs, etc). 26 | 27 | Finally, remove the nginx-ingress ingressClass `kubectl delete ingressclass/nginx`. 28 | 29 | ### 3. Install the latest version of the operator 30 | 31 | Install the latest version of the Operator following the steps outlined in [OpenShift installation doc](./openshift-installation.md). 32 | 33 | ### 4. Deploy new ingress controller deployments 34 | 35 | Use the new Nginx Ingress Operator installation to deploy Nginx Ingress Controller - see the release notes [here](https://docs.nginx.com/nginx-ingress-controller/releases/#nginx-ingress-controller-3-5-1) and a guide to the Helm configuration parameters [here](https://docs.nginx.com/nginx-ingress-controller/install/helm/#configuration) 36 | 37 | ## Manual upgrade - 0.5.1 to 1.0.0 38 | 39 | ### 1. Deploy the new operator 40 | 41 | Deploy the operator following the steps outlined in [manual installation doc](./manual-installation.md). 42 | 43 | ### 2. Cleanup the existing operator 44 | 45 | Uninstall the existing operator deployment: 46 | 47 | 1. Checkout the previous version of the nginx-ingress-operator [0.5.1](https://github.com/nginx /nginx-ingress-helm-operator/releases/tag/v0.5.1). 48 | 2. Uninstall the resources by running the following command: 49 | 50 | ```shell 51 | make undeploy 52 | ``` 53 | 54 | ### 3. Install the latest version of the operator 55 | 56 | Install the latest version of the Operator following the steps outlined in [manual installation doc](./manual-installation.md). 57 | 58 | ### 4. Deploy new ingress controller deployments 59 | 60 | Use the new Nginx Ingress Operator installation to deploy Nginx Ingress Controller - see the release notes [here](https://docs.nginx.com/nginx-ingress-controller/releases/#nginx-ingress-controller-3-5-1) and a guide to the Helm configuration parameters [here](https://docs.nginx.com/nginx-ingress-controller/install/helm/#configuration) 61 | -------------------------------------------------------------------------------- /scripts/update-openshift-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to automatically update OpenShift version annotations based on Red Hat API 4 | # This script finds the minimum OpenShift version with Extended Update Support Add-On available 5 | # and updates both bundle.Dockerfile and bundle/metadata/annotations.yaml 6 | 7 | set -e 8 | 9 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 10 | PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" 11 | 12 | # Red Hat API endpoint for OpenShift lifecycle 13 | API_URL="https://access.redhat.com/product-life-cycles/api/v1/products?name=Openshift%20Container%20Platform%204" 14 | 15 | echo "Fetching OpenShift lifecycle data from Red Hat API..." 16 | 17 | # Fetch the API data and extract supported versions 18 | api_data=$(curl -sSf "$API_URL") 19 | curl_exit=$? 20 | 21 | if [ $curl_exit -ne 0 ]; then 22 | echo "Error: Failed to fetch data from Red Hat API (exit code: $curl_exit)" 23 | exit 1 24 | fi 25 | 26 | # Validate API response structure 27 | if ! echo "$api_data" | jq -e '.data' > /dev/null 2>&1; then 28 | echo "Error: Invalid API response - missing 'data' field" 29 | exit 1 30 | fi 31 | 32 | if [ "$(echo "$api_data" | jq '.data | length')" -eq 0 ]; then 33 | echo "Error: API response contains empty data array" 34 | exit 1 35 | fi 36 | 37 | # Extract versions that have Extended Update Support Add-On available 38 | # (versions with "Extended update support" phase that is not "N/A") 39 | supported_versions=$(echo "$api_data" | jq -r ' 40 | .data[0].versions[] | 41 | select(.phases[] | select(.name == "Extended update support" and .date != "N/A")) | 42 | .name 43 | ' | sort -V) 44 | 45 | 46 | if [ -z "$supported_versions" ]; then 47 | echo "Error: No supported versions found" 48 | exit 1 49 | fi 50 | 51 | # Get the minimum supported version 52 | min_version=$(echo "$supported_versions" | head -n 1) 53 | 54 | echo "Currently supported OpenShift versions:" 55 | echo "$supported_versions" | sed 's/^/ - /' 56 | echo "" 57 | echo "Minimum supported version: $min_version" 58 | 59 | # Update bundle.Dockerfile 60 | dockerfile_path="$PROJECT_ROOT/bundle.Dockerfile" 61 | if [ -f "$dockerfile_path" ]; then 62 | echo "Updating $dockerfile_path..." 63 | 64 | # Use sed to replace the OpenShift version label 65 | if grep -q "com.redhat.openshift.versions=" "$dockerfile_path"; then 66 | temp_file=$(mktemp) 67 | sed "s/LABEL com.redhat.openshift.versions=\"v[0-9]\+\.[0-9]\+\"/LABEL com.redhat.openshift.versions=\"v$min_version\"/" "$dockerfile_path" > "$temp_file" 68 | mv "$temp_file" "$dockerfile_path" 69 | echo "Updated bundle.Dockerfile" 70 | else 71 | echo "OpenShift version label not found in bundle.Dockerfile" 72 | fi 73 | else 74 | echo "bundle.Dockerfile not found" 75 | fi 76 | 77 | # Update bundle/metadata/annotations.yaml 78 | annotations_path="$PROJECT_ROOT/bundle/metadata/annotations.yaml" 79 | if [ -f "$annotations_path" ]; then 80 | echo "Updating $annotations_path..." 81 | 82 | # Use sed to replace the OpenShift version annotation 83 | if grep -q "com.redhat.openshift.versions:" "$annotations_path"; then 84 | temp_file=$(mktemp) 85 | sed "s/com.redhat.openshift.versions: v[0-9]\+\.[0-9]\+/com.redhat.openshift.versions: v$min_version/" "$annotations_path" > "$temp_file" 86 | mv "$temp_file" "$annotations_path" 87 | echo "Updated annotations.yaml" 88 | else 89 | echo "OpenShift version annotation not found in annotations.yaml" 90 | fi 91 | else 92 | echo "bundle/metadata/annotations.yaml not found" 93 | fi 94 | 95 | # Update Makefile OPENSHIFT_VERSION variable 96 | makefile_path="$PROJECT_ROOT/Makefile" 97 | if [ -f "$makefile_path" ]; then 98 | echo "Updating $makefile_path..." 99 | 100 | # Use sed to replace the OPENSHIFT_VERSION variable 101 | if grep -q "OPENSHIFT_VERSION ?=" "$makefile_path"; then 102 | temp_file=$(mktemp) 103 | sed "s/OPENSHIFT_VERSION ?= v[0-9]\+\.[0-9]\+/OPENSHIFT_VERSION ?= v$min_version/" "$makefile_path" > "$temp_file" 104 | mv "$temp_file" "$makefile_path" 105 | echo "Updated Makefile" 106 | else 107 | echo "OPENSHIFT_VERSION variable not found in Makefile" 108 | fi 109 | else 110 | echo "Makefile not found" 111 | fi 112 | 113 | echo "" 114 | echo "OpenShift version updated to v$min_version" 115 | echo "" 116 | echo "Note: This version (v$min_version) will automatically support all newer OpenShift versions according to Red Hat's operator bundle specification." -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/crds/appprotectdos.f5.com_dosprotectedresources.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.19.0 7 | name: dosprotectedresources.appprotectdos.f5.com 8 | spec: 9 | group: appprotectdos.f5.com 10 | names: 11 | kind: DosProtectedResource 12 | listKind: DosProtectedResourceList 13 | plural: dosprotectedresources 14 | shortNames: 15 | - pr 16 | singular: dosprotectedresource 17 | scope: Namespaced 18 | versions: 19 | - name: v1beta1 20 | schema: 21 | openAPIV3Schema: 22 | description: DosProtectedResource defines a Dos protected resource. 23 | properties: 24 | apiVersion: 25 | description: |- 26 | APIVersion defines the versioned schema of this representation of an object. 27 | Servers should convert recognized schemas to the latest internal value, and 28 | may reject unrecognized values. 29 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 30 | type: string 31 | kind: 32 | description: |- 33 | Kind is a string value representing the REST resource this object represents. 34 | Servers may infer this from the endpoint the client submits requests to. 35 | Cannot be updated. 36 | In CamelCase. 37 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 38 | type: string 39 | metadata: 40 | type: object 41 | spec: 42 | description: DosProtectedResourceSpec defines the properties and values 43 | a DosProtectedResource can have. 44 | properties: 45 | allowList: 46 | description: AllowList is a list of allowed IPs and subnet masks 47 | items: 48 | description: AllowListEntry represents an IP address and a subnet 49 | mask. 50 | properties: 51 | ipWithMask: 52 | type: string 53 | type: object 54 | type: array 55 | apDosMonitor: 56 | description: 'ApDosMonitor is how NGINX App Protect DoS monitors the 57 | stress level of the protected object. The monitor requests are sent 58 | from localhost (127.0.0.1). Default value: URI - None, protocol 59 | - http1, timeout - NGINX App Protect DoS default.' 60 | properties: 61 | protocol: 62 | description: Protocol determines if the server listens on http1 63 | / http2 / grpc / websocket. The default is http1. 64 | enum: 65 | - http1 66 | - http2 67 | - grpc 68 | - websocket 69 | type: string 70 | timeout: 71 | description: Timeout determines how long (in seconds) should NGINX 72 | App Protect DoS wait for a response. Default is 10 seconds for 73 | http1/http2 and 5 seconds for grpc. 74 | format: int64 75 | type: integer 76 | uri: 77 | description: 'URI is the destination to the desired protected 78 | object in the nginx.conf:' 79 | type: string 80 | type: object 81 | apDosPolicy: 82 | description: ApDosPolicy is the namespace/name of a ApDosPolicy resource 83 | type: string 84 | dosAccessLogDest: 85 | description: DosAccessLogDest is the network address for the access 86 | logs 87 | type: string 88 | dosSecurityLog: 89 | description: DosSecurityLog defines the security log of the DosProtectedResource. 90 | properties: 91 | apDosLogConf: 92 | description: ApDosLogConf is the namespace/name of a APDosLogConf 93 | resource 94 | type: string 95 | dosLogDest: 96 | description: DosLogDest is the network address of a logging service, 97 | can be either IP or DNS name. 98 | type: string 99 | enable: 100 | description: Enable enables the security logging feature if set 101 | to true 102 | type: boolean 103 | type: object 104 | enable: 105 | description: Enable enables the DOS feature if set to true 106 | type: boolean 107 | name: 108 | description: Name is the name of protected object, max of 63 characters. 109 | type: string 110 | type: object 111 | type: object 112 | served: true 113 | storage: true 114 | -------------------------------------------------------------------------------- /config/samples/charts_v1alpha1_nginxingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: charts.nginx.org/v1alpha1 2 | kind: NginxIngress 3 | metadata: 4 | name: nginxingress-sample 5 | spec: 6 | # Default values copied from /deployments/helm-chart/values.yaml 7 | controller: 8 | name: controller 9 | kind: deployment 10 | selectorLabels: {} 11 | annotations: {} 12 | nginxplus: false 13 | mgmt: 14 | licenseTokenSecretName: license-token 15 | nginxReloadTimeout: 60000 16 | appprotect: 17 | enable: false 18 | # logLevel: fatal 19 | appprotectdos: 20 | enable: false 21 | debug: false 22 | maxWorkers: 0 23 | maxDaemons: 0 24 | memory: 0 25 | hostNetwork: false 26 | hostPort: 27 | enable: false 28 | http: 80 29 | https: 443 30 | containerPort: 31 | http: 80 32 | https: 443 33 | dnsPolicy: ClusterFirst 34 | nginxDebug: false 35 | shareProcessNamespace: false 36 | logLevel: info 37 | logFormat: glog 38 | customPorts: [] 39 | image: 40 | repository: nginx/nginx-ingress 41 | tag: "5.3.1-ubi" 42 | # digest: "sha256:CHANGEME" 43 | pullPolicy: IfNotPresent 44 | lifecycle: {} 45 | customConfigMap: "" 46 | config: 47 | # name: nginx-config 48 | annotations: {} 49 | entries: {} 50 | defaultTLS: 51 | cert: "" 52 | key: "" 53 | secret: "" 54 | wildcardTLS: 55 | cert: "" 56 | key: "" 57 | secret: "" 58 | # nodeSelector: {} 59 | terminationGracePeriodSeconds: 30 60 | autoscaling: 61 | enabled: false 62 | annotations: {} 63 | minReplicas: 1 64 | maxReplicas: 3 65 | targetCPUUtilizationPercentage: 50 66 | targetMemoryUtilizationPercentage: 50 67 | behavior: {} 68 | resources: 69 | requests: 70 | cpu: 100m 71 | memory: 128Mi 72 | # limits: 73 | # cpu: 1 74 | # memory: 1Gi 75 | initContainerResources: 76 | requests: 77 | cpu: 100m 78 | memory: 128Mi 79 | # limits: 80 | # cpu: 1 81 | # memory: 1Gi 82 | tolerations: [] 83 | affinity: {} 84 | # topologySpreadConstraints: {} 85 | env: [] 86 | # - name: MY_VAR 87 | # value: myvalue 88 | volumes: [] 89 | # - name: extra-conf 90 | # configMap: 91 | # name: extra-conf 92 | volumeMounts: [] 93 | # - name: extra-conf 94 | # mountPath: /etc/nginx/conf.d/extra.conf 95 | # subPath: extra.conf 96 | initContainers: [] 97 | # - name: init-container 98 | # image: busybox:1.34 99 | # command: ['sh', '-c', 'echo this is initial setup!'] 100 | minReadySeconds: 0 101 | podDisruptionBudget: 102 | enabled: false 103 | annotations: {} 104 | # minAvailable: 1 105 | # maxUnavailable: 1 106 | strategy: {} 107 | extraContainers: [] 108 | # - name: container 109 | # image: busybox:1.34 110 | # command: ['sh', '-c', 'echo this is a sidecar!'] 111 | replicaCount: 1 112 | ingressClass: 113 | name: nginx 114 | create: true 115 | setAsDefaultIngress: false 116 | watchNamespace: "" 117 | watchNamespaceLabel: "" 118 | watchSecretNamespace: "" 119 | enableCustomResources: true 120 | enableOIDC: false 121 | enableTLSPassthrough: false 122 | tlsPassthroughPort: 443 123 | enableCertManager: false 124 | enableExternalDNS: false 125 | globalConfiguration: 126 | create: false 127 | spec: {} 128 | # listeners: 129 | # - name: dns-udp 130 | # port: 5353 131 | # protocol: UDP 132 | # - name: dns-tcp 133 | # port: 5353 134 | # protocol: TCP 135 | enableSnippets: false 136 | healthStatus: false 137 | healthStatusURI: "/nginx-health" 138 | nginxStatus: 139 | enable: true 140 | port: 8080 141 | allowCidrs: "127.0.0.1" 142 | service: 143 | create: true 144 | type: LoadBalancer 145 | externalTrafficPolicy: Local 146 | annotations: {} 147 | extraLabels: {} 148 | loadBalancerIP: "" 149 | clusterIP: "" 150 | externalIPs: [] 151 | loadBalancerSourceRanges: [] 152 | # allocateLoadBalancerNodePorts: false 153 | # ipFamilyPolicy: SingleStack 154 | # ipFamilies: 155 | # - IPv6 156 | httpPort: 157 | enable: true 158 | port: 80 159 | # nodePort: 80 160 | targetPort: 80 161 | httpsPort: 162 | enable: true 163 | port: 443 164 | # nodePort: 443 165 | targetPort: 443 166 | customPorts: [] 167 | serviceAccount: 168 | annotations: {} 169 | # name: nginx-ingress 170 | imagePullSecretName: "" 171 | imagePullSecretsNames: [] 172 | reportIngressStatus: 173 | enable: true 174 | # externalService: nginx-ingress 175 | ingressLink: "" 176 | enableLeaderElection: true 177 | leaderElectionLockName: "nginx-ingress-leader" 178 | annotations: {} 179 | pod: 180 | annotations: {} 181 | extraLabels: {} 182 | # priorityClassName: "" 183 | readyStatus: 184 | enable: true 185 | port: 8081 186 | initialDelaySeconds: 0 187 | enableLatencyMetrics: false 188 | disableIPV6: false 189 | defaultHTTPListenerPort: 80 190 | defaultHTTPSListenerPort: 443 191 | readOnlyRootFilesystem: false 192 | enableSSLDynamicReload: true 193 | rbac: 194 | create: true 195 | prometheus: 196 | create: true 197 | port: 9113 198 | secret: "" 199 | scheme: http 200 | service: 201 | create: false 202 | labels: 203 | service: "nginx-ingress-prometheus-service" 204 | serviceMonitor: 205 | create: false 206 | labels: {} 207 | selectorMatchLabels: 208 | service: "nginx-ingress-prometheus-service" 209 | endpoints: 210 | - port: prometheus 211 | serviceInsight: 212 | create: false 213 | port: 9114 214 | secret: "" 215 | scheme: http 216 | nginxServiceMesh: 217 | enable: false 218 | enableEgress: false 219 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: manager-role 5 | rules: 6 | ## 7 | ## Base operator rules 8 | ## 9 | # We need to get namespaces so the operator can read namespaces to ensure they exist 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - configmaps 14 | - endpoints 15 | - events 16 | - namespaces 17 | - persistentvolumeclaims 18 | - pods 19 | - secrets 20 | - serviceaccounts 21 | - services 22 | - services/finalizers 23 | verbs: 24 | - create 25 | - delete 26 | - get 27 | - list 28 | - patch 29 | - update 30 | - watch 31 | # We need to manage Helm release secrets 32 | - apiGroups: 33 | - apiextensions.k8s.io 34 | resources: 35 | - customresourcedefinitions 36 | - secrets 37 | verbs: 38 | - "*" 39 | - apiGroups: 40 | - appprotect.f5.com 41 | - appprotectdos.f5.com 42 | - k8s.nginx.org 43 | resources: 44 | - '*' 45 | verbs: 46 | - create 47 | - delete 48 | - get 49 | - list 50 | - patch 51 | - update 52 | - watch 53 | - apiGroups: 54 | - apps 55 | resources: 56 | - daemonsets 57 | - deployments 58 | - replicasets 59 | - statefulsets 60 | verbs: 61 | - create 62 | - delete 63 | - get 64 | - list 65 | - patch 66 | - update 67 | - watch 68 | - apiGroups: 69 | - security.openshift.io 70 | verbs: 71 | - create 72 | - delete 73 | - get 74 | resources: 75 | - securitycontextconstraints 76 | ## 77 | ## Rules for charts.nginx.org/v1alpha1, Kind: NginxIngress 78 | ## 79 | - apiGroups: 80 | - charts.nginx.org 81 | resources: 82 | - nginxingresses 83 | - nginxingresses/status 84 | - nginxingresses/finalizers 85 | verbs: 86 | - "*" 87 | - verbs: 88 | - "*" 89 | apiGroups: 90 | - "rbac.authorization.k8s.io" 91 | resources: 92 | - "clusterrolebindings" 93 | - "clusterroles" 94 | - verbs: 95 | - "*" 96 | apiGroups: 97 | - "networking.k8s.io" 98 | resources: 99 | - "ingressclasses" 100 | - verbs: 101 | - "*" 102 | apiGroups: 103 | - "apps" 104 | resources: 105 | - "deployments" 106 | - verbs: 107 | - "*" 108 | apiGroups: 109 | - "" 110 | resources: 111 | - "configmaps" 112 | - "secrets" 113 | - "serviceaccounts" 114 | - "services" 115 | 116 | #+kubebuilder:scaffold:rules 117 | --- 118 | # Create cluster role for IC resources 119 | kind: ClusterRole 120 | apiVersion: rbac.authorization.k8s.io/v1 121 | metadata: 122 | name: nginx-ingress-admin 123 | rules: 124 | - apiGroups: 125 | - appprotect.f5.com 126 | resources: 127 | - appolicies 128 | - aplogconfs 129 | - apusersigs 130 | verbs: 131 | - get 132 | - watch 133 | - list 134 | - apiGroups: 135 | - appprotectdos.f5.com 136 | resources: 137 | - apdospolicies 138 | - apdoslogconfs 139 | - dosprotectedresources 140 | verbs: 141 | - get 142 | - watch 143 | - list 144 | - apiGroups: 145 | - discovery.k8s.io 146 | resources: 147 | - endpointslices 148 | verbs: 149 | - get 150 | - list 151 | - watch 152 | - apiGroups: 153 | - "" 154 | resources: 155 | - services 156 | verbs: 157 | - get 158 | - list 159 | - watch 160 | - create 161 | - update 162 | - delete 163 | - patch 164 | - apiGroups: 165 | - "" 166 | resources: 167 | - secrets 168 | verbs: 169 | - get 170 | - list 171 | - watch 172 | - apiGroups: 173 | - "" 174 | resources: 175 | - configmaps 176 | verbs: 177 | - get 178 | - list 179 | - watch 180 | - update 181 | - create 182 | - apiGroups: 183 | - apps 184 | resources: 185 | - daemonsets 186 | - deployments 187 | - replicasets 188 | - statefulsets 189 | verbs: 190 | - get 191 | - apiGroups: 192 | - "" 193 | resources: 194 | - nodes 195 | verbs: 196 | - list 197 | - apiGroups: 198 | - "" 199 | resources: 200 | - pods 201 | verbs: 202 | - get 203 | - list 204 | - watch 205 | - update 206 | # Comment out this scc block before running the bundle command if running in a non-OpenShift environment 207 | - apiGroups: 208 | - security.openshift.io 209 | verbs: 210 | - use 211 | resources: 212 | - securitycontextconstraints 213 | resourceNames: 214 | - nginx-ingress-admin 215 | - apiGroups: 216 | - "" 217 | resources: 218 | - namespaces 219 | verbs: 220 | - get 221 | - list 222 | - watch 223 | - apiGroups: 224 | - "" 225 | resources: 226 | - events 227 | verbs: 228 | - create 229 | - patch 230 | - list 231 | - apiGroups: 232 | - coordination.k8s.io 233 | resources: 234 | - leases 235 | verbs: 236 | - get 237 | - list 238 | - watch 239 | - update 240 | - create 241 | - apiGroups: 242 | - networking.k8s.io 243 | resources: 244 | - ingresses 245 | verbs: 246 | - get 247 | - list 248 | - watch 249 | - apiGroups: 250 | - networking.k8s.io 251 | resources: 252 | - ingressclasses 253 | verbs: 254 | - get 255 | - apiGroups: 256 | - networking.k8s.io 257 | resources: 258 | - ingresses/status 259 | verbs: 260 | - update 261 | - apiGroups: 262 | - k8s.nginx.org 263 | resources: 264 | - virtualservers 265 | - virtualserverroutes 266 | - globalconfigurations 267 | - transportservers 268 | - policies 269 | verbs: 270 | - list 271 | - watch 272 | - get 273 | - apiGroups: 274 | - k8s.nginx.org 275 | resources: 276 | - virtualservers/status 277 | - virtualserverroutes/status 278 | - policies/status 279 | - transportservers/status 280 | - virtualservers/finalizers 281 | verbs: 282 | - update 283 | - apiGroups: 284 | - cis.f5.com 285 | resources: 286 | - ingresslinks 287 | verbs: 288 | - list 289 | - watch 290 | - get 291 | - apiGroups: 292 | - cert-manager.io 293 | resources: 294 | - certificates 295 | verbs: 296 | - list 297 | - watch 298 | - get 299 | - update 300 | - create 301 | - delete 302 | - apiGroups: 303 | - externaldns.nginx.org 304 | resources: 305 | - dnsendpoints 306 | verbs: 307 | - list 308 | - watch 309 | - get 310 | - update 311 | - create 312 | - delete 313 | - apiGroups: 314 | - externaldns.nginx.org 315 | resources: 316 | - dnsendpoints/status 317 | verbs: 318 | - update 319 | -------------------------------------------------------------------------------- /.github/workflows/sync-chart.yml: -------------------------------------------------------------------------------- 1 | name: Prepare Operator Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | chart_version: 7 | description: "Chart version, e.g. x.y.z" 8 | type: string 9 | required: true 10 | operator_version: 11 | description: "Operator version, e.g. x.y.z" 12 | type: string 13 | required: true 14 | k8s_version: 15 | description: "Kubernetes version, e.g. vx.y.z" 16 | type: string 17 | required: true 18 | dry_run: 19 | description: "Do not commit to a PR" 20 | type: boolean 21 | default: false 22 | 23 | workflow_call: 24 | inputs: 25 | chart_version: 26 | description: "Chart version, e.g. x.y.z" 27 | type: string 28 | required: true 29 | operator_version: 30 | description: "Operator version, e.g. x.y.z" 31 | type: string 32 | required: true 33 | k8s_version: 34 | description: "Kubernetes version, e.g. vx.y.z" 35 | type: string 36 | required: true 37 | dry_run: 38 | description: "Do not commit to a PR" 39 | type: boolean 40 | default: false 41 | 42 | permissions: 43 | contents: read 44 | 45 | jobs: 46 | sync: 47 | runs-on: ubuntu-24.04 48 | permissions: 49 | contents: write 50 | pull-requests: write 51 | steps: 52 | - name: Checkout Operator 53 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 54 | with: 55 | repository: nginx/nginx-ingress-helm-operator 56 | 57 | - name: Checkout Kubernetes json schemas 58 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 59 | with: 60 | repository: nginxinc/kubernetes-json-schema 61 | path: schemas 62 | token: ${{ secrets.GITHUB_TOKEN }} 63 | sparse-checkout: | 64 | ${{ inputs.k8s_version}} 65 | 66 | - name: Setup Helm 67 | uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 68 | 69 | - name: Sync 70 | id: sync 71 | run: | 72 | current_nic_version=$(yq e '.appVersion' helm-charts/nginx-ingress/Chart.yaml) 73 | echo "Current NIC version: $current_nic_version" 74 | 75 | current_operator_version=$(egrep '^VERSION' Makefile | awk '{ print $3 }') 76 | echo "Current Operator version: $current_operator_version" 77 | 78 | echo "Current working directory: $(pwd)" 79 | ls -l 80 | echo "Contents of schema directory:" 81 | ls -l schemas/ 82 | 83 | cd helm-charts/ 84 | rm -rf nginx-ingress 85 | 86 | echo "Current working directory after helm-charts: $(pwd)" 87 | ls -l 88 | 89 | helm pull oci://ghcr.io/nginx/charts/nginx-ingress --untar --version ${{ inputs.chart_version }} 90 | rm -f nginx-ingress/templates/clusterrole.yaml 91 | rm -f nginx-ingress/templates/controller-role.yaml 92 | rm -f nginx-ingress/templates/controller-rolebinding.yaml 93 | sed -i '14s/name: {{ include "nginx-ingress.fullname" . }}/name: nginx-ingress-operator-nginx-ingress-admin/' nginx-ingress/templates/clusterrolebinding.yaml 94 | mkdir -p nginx-ingress/${{ inputs.k8s_version }} 95 | mv ../schemas/${{ inputs.k8s_version }}/_definitions.json nginx-ingress/${{ inputs.k8s_version }}/_definitions.json 96 | sed -i -e "s#ref\":.*_def#ref\": \"file://./helm-charts/nginx-ingress/${{ inputs.k8s_version }}/_def#" nginx-ingress/values.schema.json 97 | rm -rf ../schemas 98 | 99 | echo "Current working directory after removing schema: $(pwd)" 100 | ls -l 101 | echo "Contents of parent directory after removing schema:" 102 | ls -l ../ 103 | 104 | new_nic_version=$(yq e '.appVersion' nginx-ingress/Chart.yaml) 105 | echo "New NIC version: $new_nic_version" 106 | 107 | echo current_nic_version=$current_nic_version >> $GITHUB_OUTPUT 108 | echo current_operator_version=$current_operator_version >> $GITHUB_OUTPUT 109 | echo new_nic_version=$new_nic_version >> $GITHUB_OUTPUT 110 | echo new_operator_version=${{ inputs.operator_version }} >> $GITHUB_OUTPUT 111 | 112 | - name: Find and Replace NIC version 113 | uses: jacobtomlinson/gha-find-replace@f1069b438f125e5395d84d1c6fd3b559a7880cb5 # v3.0.5 114 | with: 115 | find: ${{ steps.sync.outputs.current_nic_version }} 116 | replace: ${{ steps.sync.outputs.new_nic_version }} 117 | regex: false 118 | exclude: .github/** 119 | 120 | - name: Update REPLACES to current version 121 | run: | 122 | current_replaces=$(grep -E '^REPLACES' Makefile | awk -F'=' '{ print $2 }' | xargs) 123 | current_version=$(grep -E '^VERSION' Makefile | awk '{ print $3 }') 124 | echo "Current REPLACES: $current_replaces" 125 | echo "Will set REPLACES to: nginx-ingress-operator.v$current_version" 126 | sed -i "s|^REPLACES ?= .*|REPLACES ?= nginx-ingress-operator.v$current_version|" Makefile 127 | 128 | - name: Find and Replace Operator version 129 | uses: jacobtomlinson/gha-find-replace@f1069b438f125e5395d84d1c6fd3b559a7880cb5 # v3.0.5 130 | with: 131 | find: ${{ steps.sync.outputs.current_operator_version }} 132 | replace: ${{ steps.sync.outputs.new_operator_version }} 133 | regex: false 134 | exclude: .github/** 135 | 136 | - name: Update bundle files 137 | run: | 138 | make bundle 139 | if: ${{ inputs.dry_run }} 140 | 141 | - name: Run Diff 142 | run: | 143 | git diff 144 | if: ${{ inputs.dry_run }} 145 | 146 | - name: Create Pull Request 147 | env: 148 | GITHUB_USERNAME: ${{ github.actor }} 149 | GITHUB_EMAIL: ${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com 150 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 151 | run: | 152 | git config --global user.name "${GITHUB_USERNAME}" 153 | git config --global user.email "${GITHUB_EMAIL}" 154 | 155 | git checkout -b update-nic-to-${{ steps.sync.outputs.new_nic_version }} 156 | git add -A 157 | git commit -m "Update NGINX Ingress Controller to ${{ steps.sync.outputs.new_nic_version }}" 158 | git push origin update-nic-to-${{ steps.sync.outputs.new_nic_version }} 159 | gh pr create --title "Update NGINX Ingress Controller to ${{ steps.sync.outputs.new_nic_version }}" --body "This automated PR updates the NGINX Ingress Controller to ${{ steps.sync.outputs.new_nic_version }}. 160 | The Helm Chart was updated to ${{ inputs.chart_version }}. 161 | The Operator was updated to ${{ inputs.operator_version }}. 162 | Kubernetes was updated to ${{ inputs.k8s_version }}." 163 | if: ${{ ! inputs.dry_run }} 164 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | env: 12 | platforms: "linux/amd64,linux/arm64" 13 | 14 | concurrency: 15 | group: ${{ github.ref_name }}-ci 16 | cancel-in-progress: true 17 | 18 | permissions: 19 | contents: read 20 | 21 | jobs: 22 | variables: 23 | name: Set Variables 24 | runs-on: ubuntu-24.04 25 | outputs: 26 | runner: ${{ steps.vars.outputs.runner }} 27 | version: ${{ steps.vars.outputs.version }} 28 | chart_version: ${{ steps.vars.outputs.chart_version }} 29 | openshift_version: ${{ steps.vars.outputs.openshift_version }} 30 | steps: 31 | - name: Checkout Repository 32 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 33 | 34 | - name: Output Variables 35 | id: vars 36 | run: | 37 | runner=ubuntu-24.04 38 | if [ "${{ github.event_name }}" == "push" ]; then 39 | runner=ubuntu-24.04-amd64 40 | fi 41 | echo "runner=$runner" >> $GITHUB_OUTPUT 42 | echo "version=$(git describe --tags)" >> $GITHUB_OUTPUT 43 | echo "chart_version=$(yq '.appVersion' > $GITHUB_OUTPUT 44 | echo "openshift_version=$(yq '.annotations["com.redhat.openshift.versions"]' > $GITHUB_OUTPUT 45 | 46 | build: 47 | name: Build Image 48 | runs-on: ${{ needs.variables.outputs.runner }} 49 | needs: [variables] 50 | outputs: 51 | version: ${{ steps.meta.outputs.version }} 52 | permissions: 53 | contents: write # to create/update draft release 54 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 55 | packages: write # for docker/build-push-action to push to GHCR 56 | steps: 57 | - name: Checkout Repository 58 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 59 | 60 | - name: DockerHub Login 61 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 62 | with: 63 | username: ${{ secrets.DOCKER_USERNAME }} 64 | password: ${{ secrets.DOCKER_PASSWORD }} 65 | if: github.event_name != 'pull_request' 66 | 67 | - name: Login to GitHub Container Registry 68 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 69 | with: 70 | registry: ghcr.io 71 | username: ${{ github.repository_owner }} 72 | password: ${{ secrets.GITHUB_TOKEN }} 73 | if: github.event_name != 'pull_request' 74 | 75 | - name: Login to Quay.io 76 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 77 | with: 78 | registry: quay.io 79 | username: ${{ secrets.QUAY_USERNAME }} 80 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 81 | if: github.event_name != 'pull_request' 82 | 83 | - name: Setup QEMU 84 | uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 85 | with: 86 | platforms: arm64 87 | if: github.event_name != 'pull_request' 88 | 89 | - name: Docker Buildx 90 | uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 91 | 92 | - name: Docker meta 93 | id: meta 94 | uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 95 | with: 96 | images: | 97 | nginx/nginx-ingress-operator 98 | ghcr.io/nginx/nginx-ingress-operator 99 | quay.io/nginx/nginx-ingress-operator 100 | tags: | 101 | type=edge 102 | type=ref,event=pr 103 | labels: | 104 | org.opencontainers.image.documentation=https://docs.nginx.com/nginx-ingress-controller 105 | org.opencontainers.image.vendor=NGINX Inc 106 | name="NGINX Ingress Operator" 107 | maintainer="kubernetes@nginx.com" 108 | vendor="NGINX Inc" 109 | version=${{ needs.variables.outputs.version }} 110 | release=1 111 | summary="The NGINX Ingress Operator is a Kubernetes/OpenShift component which deploys and manages one or more NGINX/NGINX Plus Ingress Controllers" 112 | description="The NGINX Ingress Operator is a Kubernetes/OpenShift component which deploys and manages one or more NGINX/NGINX Plus Ingress Controllers" 113 | 114 | - name: Build Image 115 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 116 | with: 117 | context: "." 118 | cache-from: type=gha 119 | cache-to: type=gha,mode=max 120 | tags: ${{ steps.meta.outputs.tags }} 121 | labels: ${{ steps.meta.outputs.labels }} 122 | platforms: ${{ github.event_name != 'pull_request' && env.platforms || '' }} 123 | load: ${{ github.event_name == 'pull_request' }} 124 | push: ${{ github.event_name != 'pull_request' }} 125 | no-cache: ${{ github.event_name != 'pull_request' }} 126 | pull: true 127 | sbom: ${{ github.event_name != 'pull_request' }} 128 | provenance: false 129 | 130 | - name: Run Trivy vulnerability scanner 131 | uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1 132 | continue-on-error: true 133 | with: 134 | image-ref: nginx/nginx-ingress-operator:${{ steps.meta.outputs.version }} 135 | format: "sarif" 136 | output: "trivy-results.sarif" 137 | ignore-unfixed: "true" 138 | if: github.event_name != 'pull_request' 139 | 140 | - name: Upload Trivy scan results to GitHub Security tab 141 | uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 142 | continue-on-error: true 143 | with: 144 | sarif_file: "trivy-results.sarif" 145 | if: github.event_name != 'pull_request' 146 | 147 | - name: Upload Scan Results 148 | uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 149 | continue-on-error: true 150 | with: 151 | name: "trivy-results.sarif" 152 | path: "trivy-results.sarif" 153 | if: github.event_name != 'pull_request' 154 | 155 | - name: Create/Update Draft 156 | uses: lucacome/draft-release@0ebb400b90474a1b791602046abf8394e9f5402d # v2.0.2 157 | with: 158 | minor-label: "enhancement" 159 | major-label: "change" 160 | publish: false 161 | variables: | 162 | nic_version=${{ needs.variables.outputs.chart_version }} 163 | openshift_version=${{ needs.variables.outputs.openshift_version }} 164 | notes-footer: | 165 | ## Compatibility 166 | 167 | - NGINX Ingress Controller {{nic_version}} 168 | - OpenShift {{openshift_version}} or newer. 169 | if: github.event_name != 'pull_request' 170 | 171 | e2e-test: 172 | name: Run E2E Tests # Deploy NGINX Ingress Operator and Nginx Ingress Controller 173 | uses: ./.github/workflows/e2e-test.yml 174 | needs: build 175 | with: 176 | operator_version: ${{ needs.build.outputs.version }} 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Continuous Integration](https://github.com/nginx/nginx-ingress-helm-operator/workflows/Continuous%20Integration/badge.svg)](https://github.com/nginx/nginx-ingress-helm-operator/actions) 2 | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/nginx/nginx-ingress-helm-operator/badge)](https://api.securityscorecards.dev/projects/github.com/nginx/nginx-ingress-helm-operator) 3 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 4 | ![Commercial Support](https://badgen.net/badge/support/commercial/green?icon=awesome) 5 | 6 | # NGINX Ingress Operator 7 | 8 | The NGINX Ingress Operator is a Kubernetes/OpenShift component which deploys and manages one or more [NGINX/NGINX Plus Ingress Controllers](https://github.com/nginx/kubernetes-ingress) which in turn handle Ingress traffic for applications running in a cluster. 9 | 10 | Learn more about operators in the [Kubernetes Documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). 11 | 12 | To install a specific version of the NGINX Ingress Controller with the operator, a specific version of the NGINX Ingress Operator is required. 13 | 14 | Up until version 0.5.1, this Operator was Go based. Version 1.0.0 marks an incompatible upgrade as this release switched the Operator to being Helm-based, built from the [NGINX Ingress Controller Helm chart](http://helm.nginx.com/#nginx-ingress-controller). The configuration for the Helm chart can be seen in the [NGINX Ingress Controller documentation](https://docs.nginx.com/nginx-ingress-controller/install/helm#configuration). 15 | 16 | The following table shows the relation between the versions of the two projects: 17 | 18 | | NGINX Ingress Controller | NGINX Ingress Operator | 19 | | ------------------------ | ---------------------- | 20 | | 5.3.x | 3.4.1 | 21 | | 5.2.x | 3.3.1 | 22 | | 5.1.x | 3.2.3 | 23 | | 5.0.x | 3.1.0 | 24 | | 4.0.x | 3.0.1 | 25 | | 3.7.x | 2.4.2 | 26 | | 3.6.x | 2.3.2 | 27 | | 3.5.x | 2.2.2 | 28 | | 3.4.x | 2.1.2 | 29 | | 3.3.x | 2.0.2 | 30 | | 3.2.x | 1.5.2 | 31 | | 3.1.x | 1.4.2 | 32 | | 3.0.x | 1.3.1 | 33 | | 2.4.x | 1.2.1 | 34 | | 2.3.x | 1.1.0 | 35 | | 2.2.x | 1.0.0 | 36 | | 2.1.x | 0.5.1 | 37 | | 2.0.x | 0.4.0 | 38 | | 1.12.x | 0.3.0 | 39 | | 1.11.x | 0.2.0 | 40 | | 1.10.x | 0.1.0 | 41 | | 1.9.x | 0.0.7 | 42 | | 1.8.x | 0.0.6 | 43 | | 1.7.x | 0.0.4 | 44 | | < 1.7.0 | N/A | 45 | 46 | Note: The NGINX Ingress Operator works only for NGINX Ingress Controller versions after `1.7.0`. 47 | 48 | ## Getting Started 49 | 50 | 1. Install the NGINX Ingress Operator. See [docs](./docs/installation.md). 51 |
NOTE: To use TransportServers as part of your NGINX Ingress Controller configuration, a GlobalConfiguration resource must be created _before_ starting the Operator - [see the notes](./examples/deployment-oss-min/README.md#TransportServers) 52 | 2. Creating the default-server-secret.yaml is optional and it is recommended that users provide their own certificate. An example yaml for this can be found in the [examples folder](https://github.com/nginx/nginx-ingress-helm-operator/blob/main/examples/default-server-secret.yaml) 53 | 3. (If using OpenShift) Create the scc resource on the cluster by applying the scc.yaml file found in the `resources` folder of this repo: 54 | 55 | ```shell 56 | kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-ingress-helm-operator/main/resources/scc.yaml 57 | ``` 58 | 59 | 4. Deploy a new NGINX Ingress Controller using the [NginxIngress](./config/samples/charts_v1alpha1_nginxingress.yaml) Custom Resource: 60 | - Use the name of the default server secret created above for `controller.defaultTLS.secret` field (needs to be in the form `namespace/name`) 61 | - If using NGINX Plus: 62 | - Set the `controller.nginxPlus` to true 63 | - Set the `controller.image.repository` and `controller.image.tag` to the appropriate values 64 | - Set the `controller.serviceAccount.imagePullSecretName` if applicable 65 | - For full configuration details see the Helm documentation [here](https://docs.nginx.com/nginx-ingress-controller/install/helm/#configuration). 66 | 67 | ## Notes: Multiple NIC Deployments 68 | 69 | - Please see [the NGINX Ingress Controller documentation](https://docs.nginx.com/nginx-ingress-controller/install/multiple-controllers/) for general information on running multiple NGINX Ingress Controllers in your cluster. 70 | - To run multiple NIC instances deployed by the NGINX Ingress Operator in your cluster in the same namespace, `rbac.create` should be set to `false`, and the ServiceAccount and ClusterRoleBinding need to be created independently of the deployments. Please note that `controller.serviceAccount.imagePullSecretName` will also be ignored in this configuration, and will need to be configured as part of the independent ServiceAccount creation. 71 | - The ClusterRoleBinding needs to configured to bind to the `nginx-ingress-operator-nginx-ingress-admin` ClusterRole. 72 | - See [RBAC example spec](./resources/rbac-example.yaml) for an example ServiceAccount and ClusterRoleBinding manifest. 73 | - To run multiple NIC instances deployed by the NGINX Ingress Operator in your cluster in any namespace but sharing an IngressClass, `controller.ingressClass.name` should be set to an empty string and the IngressClass resource needs to be created independently of the deployments.Please note that `controller.ingressClass.setAsDefaultIngress` will also be ignored in this configuration, and will need to be configured as part of the independent IngressClass creation. 74 | - See [IngressClass example spec](./resources/ingress-class.yaml) for an example IngressClass manifest. 75 | 76 | ## Upgrades 77 | 78 | See [upgrade docs](./docs/upgrades.md) 79 | 80 | ## NGINX Ingress Operator Releases 81 | 82 | We publish NGINX Ingress Operator releases on GitHub. See our [releases page](https://github.com/nginx/nginx-ingress-helm-operator/releases). 83 | 84 | The latest stable release is [3.4.1](https://github.com/nginx/nginx-ingress-helm-operator/releases/tag/v3.4.1). For production use, we recommend that you choose the latest stable release. 85 | 86 | ## Development 87 | 88 | It is possible to run the operator in your local machine. This is useful for testing or during development. 89 | 90 | ### Run Operator locally 91 | 92 | 1. Have access to a Kubernetes/OpenShift cluster. 93 | 2. Apply the IC CRD: 94 | 95 | ```shell 96 | make install 97 | ``` 98 | 99 | 3. Run `make run`. 100 | 101 | The operator will run in your local machine but will be communicating with the cluster. 102 | 103 | ## Contributing 104 | 105 | If you'd like to contribute to the project, please read our [Contributing](./CONTRIBUTING.md) guide. 106 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "packageRules": [ 4 | { 5 | "enabled": false, 6 | "matchPackageNames": [ 7 | "boto3" 8 | ], 9 | "matchUpdateTypes": [ 10 | "patch" 11 | ] 12 | }, 13 | { 14 | "addLabels": [ 15 | "go" 16 | ], 17 | "matchDatasources": [ 18 | "go", 19 | "golang-version" 20 | ] 21 | }, 22 | { 23 | "addLabels": [ 24 | "python" 25 | ], 26 | "matchDatasources": [ 27 | "pypi" 28 | ] 29 | }, 30 | { 31 | "addLabels": [ 32 | "javascript" 33 | ], 34 | "matchDatasources": [ 35 | "npm" 36 | ], 37 | "rangeStrategy": "bump" 38 | }, 39 | { 40 | "addLabels": [ 41 | "docker" 42 | ], 43 | "matchDatasources": [ 44 | "docker" 45 | ] 46 | }, 47 | { 48 | "addLabels": [ 49 | "helm" 50 | ], 51 | "matchDatasources": [ 52 | "helm" 53 | ] 54 | }, 55 | { 56 | "addLabels": [ 57 | "github_actions" 58 | ], 59 | "matchManagers": [ 60 | "github-actions" 61 | ] 62 | }, 63 | { 64 | "matchPackageNames": ["/private-registry\\.nginx\\.com/"], 65 | "matchDatasources": ["docker"], 66 | "enabled": false 67 | }, 68 | { 69 | "matchPackageNames": ["/registry\\.redhat\\.io/"], 70 | "matchDatasources": ["docker"], 71 | "enabled": false 72 | } 73 | ], 74 | "kubernetes": { 75 | "managerFilePatterns": [ 76 | "/bundle/manifests/.+\\.yaml$/", 77 | "/bundle/tests/scorecard/.+\\.yaml$/", 78 | "/config/.+\\.yaml$/", 79 | "/examples/.+\\.yaml$/", 80 | "/helm-charts/nginx-ingress/crds/.+\\.yaml$/", 81 | "/helm-charts/nginx-ingress/templates/.+\\.yaml$/" 82 | ] 83 | }, 84 | "customManagers": [ 85 | { 86 | "customType": "regex", 87 | "description": "Update env variable version in Github Actions workflow", 88 | "managerFilePatterns": [ 89 | "/.github/workflows/.+\\.ya?ml$/" 90 | ], 91 | "matchStrings": [ 92 | "\\s+.+?: (?.+?) # renovate: datasource=(?[a-z-]+?) depName=(?.+?)(?: (?:packageName|lookupName)=(?.+?))?(?: versioning=(?[a-z-]+?))?\\s" 93 | ] 94 | }, 95 | { 96 | "customType": "regex", 97 | "description": "Update Operator SDK version in bundle Dockerfile", 98 | "managerFilePatterns": [ 99 | "/bundle\\.Dockerfile$/" 100 | ], 101 | "matchStrings": [ 102 | "# renovate: datasource=(?[a-z-]+?) depName=(?.+?)(?: (?:packageName|lookupName)=(?.+?))?(?: versioning=(?[a-z-]+?))?\\sLABEL operators.operatorframework.io.metrics.builder=operator-sdk-(?.+?)\\s" 103 | ] 104 | }, 105 | { 106 | "customType": "regex", 107 | "description": "Update _VERSION variables in shell scripts and Makefiles", 108 | "managerFilePatterns": [ 109 | "\\.sh$", 110 | "/Makefile$/" 111 | ], 112 | "matchStrings": [ 113 | "# renovate: datasource=(?[a-z-]+?) depName=(?.+?)?(?: (?:packageName|lookupName)=(?.+?))?(?: versioning=(?[a-z-]+?))?\\s\\w+?_VERSION \\??= (?.+?)\\s" 114 | ] 115 | }, 116 | { 117 | "customType": "regex", 118 | "description": "Update _VERSION variables in Dockerfiles", 119 | "managerFilePatterns": [ 120 | "(^|/|\\.)Dockerfile$", 121 | "(^|/)Dockerfile\\.[^/]*$" 122 | ], 123 | "matchStrings": [ 124 | "# renovate: datasource=(?[a-z-]+?) depName=(?.+?)(?: (?:packageName|lookupName)=(?.+?))?(?: versioning=(?[a-z-]+?))?\\s(?:ENV|ARG) .+?_VERSION=(?.+?)\\s" 125 | ] 126 | }, 127 | { 128 | "customType": "regex", 129 | "datasourceTemplate": "github-tags", 130 | "description": "Update Github Action references in the Markdown files", 131 | "managerFilePatterns": [ 132 | "\\.md$" 133 | ], 134 | "matchStrings": [ 135 | "\\suses: (?[\\w-]+/[\\w-]+)(?/.*)?@(?v\\d+\\.\\d+\\.\\d+)", 136 | "\\suses: (?[\\w-]+/[\\w-]+)(?/.*)?@(?[a-z0-9]{40}|[a-z0-9]{64}) # (?v\\d+\\.\\d+\\.\\d+)" 137 | ], 138 | "versioningTemplate": "semver" 139 | }, 140 | { 141 | "customType": "regex", 142 | "description": "Update `version:` and `_VERSION:` variables in github workflows", 143 | "managerFilePatterns": [ 144 | "^\\.github\\/workflows\\/\\w+\\.ya?ml$" 145 | ], 146 | "matchStrings": [ 147 | "\\s+(?:[[:word:]]-)?version: (?.+?) # renovate: datasource=(?.+?) depName=(?.+?)(?: (?:packageName|lookupName)=(?.+?))?(?: versioning=(?.+?))?\\s", 148 | "\\s*[[:word:]]+?_VERSION=(?.+?) # renovate: datasource=(?.+?) depName=(?.+?)(?: (?:packageName|lookupName)=(?.+?))?(?: versioning=(?.+?))?\\s" 149 | ] 150 | }, 151 | { 152 | "customType": "regex", 153 | "description": "Update `Version:` in go files", 154 | "managerFilePatterns": [ 155 | "\\.go$" 156 | ], 157 | "matchStrings": [ 158 | "// renovate: datasource=(?[a-z-]+?) depName=(?.+?)(?: registryUrl=(?.+?))?(?: (?:packageName|lookupName)=(?.+?))?(?: versioning=(?[a-z-]+?))?\\s.+?Version :?= \"(?.+?)\"\\s" 159 | ], 160 | "versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}" 161 | }, 162 | { 163 | "customType": "regex", 164 | "description": "Update Packer and Packer Plugin versions in Packer files", 165 | "datasourceTemplate": "github-tags", 166 | "depNameTemplate": "{{#if depName}}{{#if (containsString depName \"hashicorp/\")}}{{{replace \"hashicorp/\" \"hashicorp/packer-plugin-\" depName}}}{{else}}{{{depName}}}{{/if}}{{else}}hashicorp/packer{{/if}}", 167 | "managerFilePatterns": [ 168 | "\\.pkr\\.hcl" 169 | ], 170 | "matchStrings": [ 171 | "required_version[\\s]+=[\\s]+\"=?(?\\S*)\"", 172 | "source[\\s]+=[\\s]+\"github.com/(?\\S*)\"[\\s]+version[\\s]+=[\\s]+\"(?\\S*)\"", 173 | "version[\\s]+=[\\s]+\"(?\\S*)[\\s]+source[\\s]+=[\\s]+\"github.com/(?\\S*)\"" 174 | ] 175 | } 176 | ], 177 | "timezone": "Europe/Dublin", 178 | "automerge": true, 179 | "automergeStrategy": "squash", 180 | "commitBodyTable": true, 181 | "configMigration": true, 182 | "customDatasources": { 183 | "nginx-plus": { 184 | "defaultRegistryUrlTemplate": "https://raw.githubusercontent.com/lucacome/renovate-datasource/main/nginx-plus/releases.json", 185 | "format": "json" 186 | } 187 | }, 188 | "extends": [ 189 | "schedule:daily", 190 | "config:recommended", 191 | "docker:enableMajor", 192 | "helpers:pinGitHubActionDigests", 193 | ":gitSignOff", 194 | ":maintainLockFilesMonthly", 195 | ":disableRateLimiting", 196 | ":semanticCommitsDisabled" 197 | ], 198 | "ignorePaths": [ 199 | "**/node_modules/**" 200 | ], 201 | "ignorePresets": [ 202 | ":ignoreModulesAndTests" 203 | ], 204 | "labels": [ 205 | "dependencies" 206 | ], 207 | "postUpdateOptions": [ 208 | "gomodTidy", 209 | "gomodUpdateImportPaths", 210 | "yarnDedupeHighest", 211 | "npmDedupe" 212 | ], 213 | "pre-commit": { 214 | "enabled": true 215 | }, 216 | "rebaseWhen": "behind-base-branch" 217 | } 218 | -------------------------------------------------------------------------------- /.github/workflows/e2e-test.yml: -------------------------------------------------------------------------------- 1 | name: E2E Test 2 | 3 | on: 4 | schedule: 5 | - cron: '00 03 * * *' 6 | workflow_dispatch: # Allow manual triggering 7 | inputs: 8 | operator_version: 9 | description: 'Operator version/tag (e.g., edge, x.y.z)' 10 | required: false 11 | default: 'edge' 12 | type: string 13 | workflow_call: # Allow being called by other workflows 14 | inputs: 15 | operator_version: 16 | description: 'Operator version/tag (e.g., edge, x.y.z)' 17 | required: false 18 | default: 'edge' 19 | type: string 20 | 21 | env: 22 | OPERATOR_VERSION: ${{ inputs.operator_version || 'edge' }} 23 | 24 | concurrency: 25 | group: ${{ github.ref_name }}-e2e 26 | cancel-in-progress: true 27 | 28 | permissions: 29 | contents: read 30 | 31 | jobs: 32 | e2e-test: 33 | name: End-to-End Test 34 | runs-on: ubuntu-24.04 35 | steps: 36 | - name: Checkout Repository 37 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 38 | 39 | - name: Check if image exists 40 | id: image_exists 41 | run: | 42 | exists=false 43 | if docker pull nginx/nginx-ingress-operator:${{ env.OPERATOR_VERSION }}; then 44 | exists=true 45 | fi 46 | echo "exists=${exists}" >> $GITHUB_OUTPUT 47 | 48 | - name: Docker Buildx 49 | uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 50 | if: steps.image_exists.outputs.exists == 'false' 51 | 52 | - name: Build Image 53 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 54 | with: 55 | context: "." 56 | cache-from: type=gha 57 | cache-to: type=gha,mode=max 58 | tags: nginx/nginx-ingress-operator:${{ env.OPERATOR_VERSION }} 59 | platforms: ${{ github.event_name != 'pull_request' && env.platforms || '' }} 60 | load: true 61 | push: false 62 | no-cache: ${{ github.event_name != 'pull_request' }} 63 | pull: true 64 | sbom: false 65 | provenance: false 66 | if: steps.image_exists.outputs.exists == 'false' 67 | 68 | - name: Get Latest Versions 69 | run: | 70 | # Get latest supported Kubernetes version from Minikube 71 | # Handle both old format "Support Kubernetes version vX.Y.Z" and new format "Support for Kubernetes vX.Y.Z" 72 | K8S_VERSION=$(curl -s https://api.github.com/repos/kubernetes/minikube/releases/latest | jq -r '.body' | grep -oE "(Support for Kubernetes|Support Kubernetes version) v[0-9]+\.[0-9]+\.[0-9]+" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+" | head -1) 73 | 74 | # Fallback to a known stable Kubernetes version if extraction fails 75 | if [ -z "$K8S_VERSION" ]; then 76 | echo "Warning: Could not extract Kubernetes version from Minikube release notes, using fallback version 1.34.0" 77 | K8S_VERSION="1.34.0" 78 | fi 79 | 80 | echo "KUBERNETES_VERSION=v$K8S_VERSION" >> $GITHUB_ENV 81 | 82 | # Get latest Minikube version 83 | echo "MINIKUBE_VERSION=$(curl -s https://api.github.com/repos/kubernetes/minikube/releases/latest | jq -r '.tag_name')" >> $GITHUB_ENV 84 | 85 | - name: Install kubectl 86 | run: | 87 | curl -LO "https://dl.k8s.io/release/${{ env.KUBERNETES_VERSION }}/bin/linux/amd64/kubectl" 88 | chmod +x kubectl 89 | sudo mv kubectl /usr/local/bin/ 90 | 91 | - name: Install Minikube 92 | run: | 93 | curl -Lo minikube https://storage.googleapis.com/minikube/releases/${{ env.MINIKUBE_VERSION }}/minikube-linux-amd64 94 | chmod +x minikube 95 | sudo mv minikube /usr/local/bin/ 96 | 97 | - name: Start Minikube 98 | run: | 99 | minikube start --kubernetes-version=${{ env.KUBERNETES_VERSION }} --driver=docker --memory=4g --cpus=2 100 | minikube image load nginx/nginx-ingress-operator:${{ env.OPERATOR_VERSION }} 101 | 102 | - name: Verify Kubernetes cluster 103 | run: | 104 | kubectl cluster-info 105 | kubectl get nodes 106 | kubectl get pods -A 107 | 108 | - name: Deploy NGINX Ingress Operator 109 | run: | 110 | # Deploy the operator using make deploy with specified version, edge otherwise 111 | make deploy IMG=nginx/nginx-ingress-operator:${{ env.OPERATOR_VERSION }} 112 | 113 | # Wait for the operator to be ready 114 | kubectl wait --for=condition=Available --timeout=300s deployment/nginx-ingress-operator-controller-manager -n nginx-ingress-operator-system 115 | 116 | - name: Verify Operator Deployment 117 | run: | 118 | # Check operator deployment status 119 | kubectl get deployments -n nginx-ingress-operator-system 120 | kubectl get pods -n nginx-ingress-operator-system 121 | 122 | # Check operator logs 123 | kubectl logs -l control-plane=controller-manager -n nginx-ingress-operator-system --tail=50 124 | 125 | - name: Deploy Example NGINX Ingress Controller 126 | run: | 127 | # Create the namespace and NGINX Ingress Controller instance in namespace nginx-ingress 128 | kubectl create namespace nginx-ingress 129 | kubectl apply -f tests/nginx-ingress-controller-oss.yaml 130 | 131 | # Wait for the operator to process and install the Helm release 132 | echo "Waiting for operator to install Helm release..." 133 | sleep 30 134 | 135 | # Check what deployments were actually created 136 | echo "Checking deployments in nginx-ingress namespace:" 137 | kubectl get deployments -n nginx-ingress 138 | 139 | # Find the actual deployment name 140 | DEPLOYMENT_NAME=$(kubectl get deployments -n nginx-ingress -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") 141 | 142 | if [ -z "$DEPLOYMENT_NAME" ]; then 143 | echo "No deployment found, waiting longer..." 144 | sleep 30 145 | kubectl get deployments -n nginx-ingress 146 | DEPLOYMENT_NAME=$(kubectl get deployments -n nginx-ingress -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") 147 | fi 148 | 149 | if [ -n "$DEPLOYMENT_NAME" ]; then 150 | echo "Found deployment: $DEPLOYMENT_NAME" 151 | # Export for use in subsequent steps 152 | echo "DEPLOYMENT_NAME=$DEPLOYMENT_NAME" >> $GITHUB_ENV 153 | # Wait for the NGINX Ingress Controller to be ready 154 | kubectl wait --for=condition=Available --timeout=300s deployment/$DEPLOYMENT_NAME -n nginx-ingress 155 | else 156 | echo "Failed to find any deployment in nginx-ingress namespace" 157 | exit 1 158 | fi 159 | - name: Verify NGINX Ingress Controller Deployment 160 | run: | 161 | # Check all resources in the nginx ingress controller namespace 162 | kubectl get all -n nginx-ingress 163 | 164 | # Check if the controller pod is running 165 | kubectl get pods -n nginx-ingress 166 | 167 | # Check the service 168 | kubectl get services -n nginx-ingress 169 | 170 | # Check ingress class 171 | kubectl get ingressclass 172 | 173 | # Verify all pods are ready in nginx-ingress namespace 174 | kubectl wait --for=condition=Ready --timeout=300s pod --all -n nginx-ingress 175 | 176 | - name: Verify NGINX Configuration 177 | run: | 178 | echo "Testing NGINX configuration for deployment: $DEPLOYMENT_NAME" 179 | 180 | # Check NGINX configuration is valid 181 | kubectl exec -n nginx-ingress deployment/$DEPLOYMENT_NAME -- nginx -t 182 | 183 | - name: Check Operator and Controller Logs 184 | if: always() 185 | run: | 186 | echo "=== Operator Logs ===" 187 | kubectl logs -l control-plane=controller-manager -n nginx-ingress-operator-system 188 | 189 | echo "=== NGINX Ingress Controller Logs ===" 190 | # Get logs from all pods in nginx-ingress namespace 191 | for pod in $(kubectl get pods -n nginx-ingress -o jsonpath='{.items[*].metadata.name}' 2>/dev/null || echo ""); do 192 | if [ -n "$pod" ]; then 193 | echo "--- Logs for pod: $pod ---" 194 | kubectl logs $pod -n nginx-ingress 195 | fi 196 | done 197 | -------------------------------------------------------------------------------- /helm-charts/nginx-ingress/templates/controller-daemonset.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.controller.kind "daemonset" }} 2 | apiVersion: apps/v1 3 | kind: DaemonSet 4 | metadata: 5 | name: {{ include "nginx-ingress.controller.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "nginx-ingress.labels" . | nindent 4 }} 9 | {{- if .Values.controller.annotations }} 10 | annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | selector: 14 | matchLabels: 15 | {{- include "nginx-ingress.selectorLabels" . | nindent 6 }} 16 | template: 17 | metadata: 18 | labels: 19 | {{- include "nginx-ingress.podLabels" . | nindent 8 }} 20 | {{- if or .Values.prometheus.create .Values.controller.pod.annotations }} 21 | annotations: 22 | {{- if .Values.prometheus.create }} 23 | prometheus.io/scrape: "true" 24 | prometheus.io/port: "{{ .Values.prometheus.port }}" 25 | prometheus.io/scheme: "{{ .Values.prometheus.scheme }}" 26 | {{- end }} 27 | {{- if .Values.controller.pod.annotations }} 28 | {{ toYaml .Values.controller.pod.annotations | indent 8 }} 29 | {{- end }} 30 | {{- end }} 31 | spec: 32 | serviceAccountName: {{ include "nginx-ingress.serviceAccountName" . }} 33 | automountServiceAccountToken: true 34 | securityContext: 35 | {{ toYaml .Values.controller.podSecurityContext | indent 8 }} 36 | terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} 37 | {{- if .Values.controller.nodeSelector }} 38 | nodeSelector: 39 | {{ toYaml .Values.controller.nodeSelector | indent 8 }} 40 | {{- end }} 41 | {{- if .Values.controller.tolerations }} 42 | tolerations: 43 | {{ toYaml .Values.controller.tolerations | indent 6 }} 44 | {{- end }} 45 | {{- if .Values.controller.affinity }} 46 | affinity: 47 | {{ toYaml .Values.controller.affinity | indent 8 }} 48 | {{- end }} 49 | {{- include "nginx-ingress.volumes" . | indent 6 }} 50 | {{- if .Values.controller.priorityClassName }} 51 | priorityClassName: {{ .Values.controller.priorityClassName }} 52 | {{- end }} 53 | hostNetwork: {{ .Values.controller.hostNetwork }} 54 | dnsPolicy: {{ .Values.controller.dnsPolicy }} 55 | {{- if .Values.controller.shareProcessNamespace }} 56 | shareProcessNamespace: true 57 | {{- end }} 58 | containers: 59 | - name: {{ include "nginx-ingress.name" . }} 60 | image: {{ include "nginx-ingress.image" . }} 61 | imagePullPolicy: "{{ .Values.controller.image.pullPolicy }}" 62 | {{- if .Values.controller.lifecycle }} 63 | lifecycle: 64 | {{ toYaml .Values.controller.lifecycle | indent 10 }} 65 | {{- end }} 66 | ports: 67 | {{- range $key, $value := .Values.controller.containerPort }} 68 | - name: {{ $key }} 69 | containerPort: {{ $value }} 70 | protocol: TCP 71 | {{- if and $.Values.controller.hostPort.enable (index $.Values.controller.hostPort $key) }} 72 | hostPort: {{ index $.Values.controller.hostPort $key }} 73 | {{- end }} 74 | {{- end }} 75 | {{ if .Values.controller.customPorts }} 76 | {{ toYaml .Values.controller.customPorts | indent 8 }} 77 | {{ end }} 78 | {{- if .Values.prometheus.create }} 79 | - name: prometheus 80 | containerPort: {{ .Values.prometheus.port }} 81 | {{- end }} 82 | {{- if .Values.serviceInsight.create }} 83 | - name: service-insight 84 | containerPort: {{ .Values.serviceInsight.port }} 85 | {{- end }} 86 | {{- if .Values.controller.readyStatus.enable }} 87 | - name: readiness-port 88 | containerPort: {{ .Values.controller.readyStatus.port }} 89 | {{- end }} 90 | {{- if .Values.controller.startupStatus.enable }} 91 | - name: startup-port 92 | containerPort: {{ .Values.controller.startupStatus.port }} 93 | {{- end }} 94 | {{- if .Values.controller.readyStatus.enable }} 95 | readinessProbe: 96 | httpGet: 97 | path: /nginx-ready 98 | port: readiness-port 99 | periodSeconds: 1 100 | initialDelaySeconds: {{ .Values.controller.readyStatus.initialDelaySeconds }} 101 | {{- end }} 102 | {{- if .Values.controller.startupStatus.enable }} 103 | startupProbe: 104 | httpGet: 105 | path: {{ .Values.controller.startupStatus.path }} 106 | port: startup-port 107 | initialDelaySeconds: {{ .Values.controller.startupStatus.initialDelaySeconds }} 108 | periodSeconds: {{ .Values.controller.startupStatus.periodSeconds }} 109 | timeoutSeconds: {{ .Values.controller.startupStatus.timeoutSeconds }} 110 | successThreshold: {{ .Values.controller.startupStatus.successThreshold }} 111 | failureThreshold: {{ .Values.controller.startupStatus.failureThreshold }} 112 | {{- end }} 113 | {{- if .Values.controller.securityContext }} 114 | securityContext: 115 | {{ toYaml .Values.controller.securityContext | indent 10 }} 116 | {{- else }} 117 | securityContext: 118 | allowPrivilegeEscalation: false 119 | readOnlyRootFilesystem: {{ .Values.controller.readOnlyRootFilesystem }} 120 | runAsUser: 101 #nginx 121 | runAsNonRoot: true 122 | capabilities: 123 | drop: 124 | - ALL 125 | add: 126 | - NET_BIND_SERVICE 127 | {{- end }} 128 | {{- include "nginx-ingress.volumeMounts" . | indent 8 }} 129 | env: 130 | - name: POD_NAMESPACE 131 | valueFrom: 132 | fieldRef: 133 | fieldPath: metadata.namespace 134 | - name: POD_NAME 135 | valueFrom: 136 | fieldRef: 137 | fieldPath: metadata.name 138 | {{- if .Values.controller.env }} 139 | {{ toYaml .Values.controller.env | indent 8 }} 140 | {{- end }} 141 | {{- if .Values.nginxServiceMesh.enable }} 142 | - name: POD_SERVICEACCOUNT 143 | valueFrom: 144 | fieldRef: 145 | fieldPath: spec.serviceAccountName 146 | {{- end }} 147 | {{- if hasKey .Values.controller.mgmt "usageReport" -}} 148 | {{- if hasKey .Values.controller.mgmt.usageReport "proxyCredentialsSecretName" }} 149 | {{- if not (hasKey .Values.controller.mgmt.usageReport "proxyHost") -}} 150 | {{- fail "Error: 'controller.mgmt.usageReport.proxyHost' must be set when using 'controller.mgmt.usageReport.proxyCredentialsSecretName'." }} 151 | {{- end }} 152 | - name: PROXY_USER 153 | valueFrom: 154 | secretKeyRef: 155 | name: {{ .Values.controller.mgmt.usageReport.proxyCredentialsSecretName }} 156 | key: username 157 | - name: PROXY_PASS 158 | valueFrom: 159 | secretKeyRef: 160 | name: {{ .Values.controller.mgmt.usageReport.proxyCredentialsSecretName }} 161 | key: password 162 | {{- end }} 163 | {{- end }} 164 | resources: 165 | {{ toYaml .Values.controller.resources | indent 10 }} 166 | args: 167 | {{- include "nginx-ingress.args" . | nindent 10 }} 168 | {{- if .Values.controller.extraContainers }} 169 | {{ toYaml .Values.controller.extraContainers | nindent 6 }} 170 | {{- end }} 171 | 172 | {{- include "nginx-ingress.appprotect.v5" . | nindent 6 }} 173 | 174 | {{- if or (eq (include "nginx-ingress.readOnlyRootFilesystem" .) "true" ) .Values.controller.initContainers }} 175 | initContainers: 176 | {{- end }} 177 | {{- if eq (include "nginx-ingress.readOnlyRootFilesystem" .) "true" }} 178 | - name: init-{{ include "nginx-ingress.name" . }} 179 | image: {{ include "nginx-ingress.image" . }} 180 | imagePullPolicy: "{{ .Values.controller.image.pullPolicy }}" 181 | command: ['cp', '-vdR', '/etc/nginx/.', '/mnt/etc'] 182 | {{- if .Values.controller.initContainerResources }} 183 | resources: 184 | {{ toYaml .Values.controller.initContainerResources | indent 10 }} 185 | {{- end }} 186 | {{- if .Values.controller.initContainerSecurityContext }} 187 | securityContext: 188 | {{ toYaml .Values.controller.initContainerSecurityContext | indent 10 }} 189 | {{- else }} 190 | securityContext: 191 | allowPrivilegeEscalation: false 192 | readOnlyRootFilesystem: true 193 | runAsUser: 101 #nginx 194 | runAsNonRoot: true 195 | capabilities: 196 | drop: 197 | - ALL 198 | {{- end }} 199 | volumeMounts: 200 | - mountPath: /mnt/etc 201 | name: nginx-etc 202 | {{- end }} 203 | {{- if .Values.controller.initContainers }} 204 | {{ toYaml .Values.controller.initContainers | indent 6 }} 205 | {{- end }} 206 | {{- if .Values.controller.strategy }} 207 | updateStrategy: 208 | {{ toYaml .Values.controller.strategy | indent 4 }} 209 | {{- end }} 210 | {{- if .Values.controller.minReadySeconds }} 211 | minReadySeconds: {{ .Values.controller.minReadySeconds }} 212 | {{- end }} 213 | {{- end }} 214 | -------------------------------------------------------------------------------- /docs/nginx-ingress-controller.md: -------------------------------------------------------------------------------- 1 | # NginxIngress Custom Resource 2 | 3 | The `NginxIngress` Custom Resource is the definition of a deployment of the Ingress Controller. 4 | With this Custom Resource, the NGINX Ingress Operator will be able to deploy and configure instances of the Ingress Controller in your cluster. 5 | 6 | ## Configuration 7 | 8 | There are several fields to configure the deployment of an Ingress Controller. 9 | 10 | The following example shows the usage of all fields (required and optional): 11 | 12 | ```yaml 13 | apiVersion: charts.nginx.org/v1alpha1 14 | kind: NginxIngress 15 | metadata: 16 | name: nginxingress-sample 17 | spec: 18 | # Default values copied from /helm-charts/nginx-ingress/values.yaml 19 | controller: 20 | name: controller 21 | kind: deployment 22 | selectorLabels: {} 23 | annotations: {} 24 | nginxplus: false 25 | mgmt: 26 | licenseTokenSecretName: "license-token" 27 | nginxReloadTimeout: 60000 28 | appprotect: 29 | enable: false 30 | # logLevel: fatal 31 | appprotectdos: 32 | enable: false 33 | debug: false 34 | maxWorkers: 0 35 | maxDaemons: 0 36 | memory: 0 37 | hostNetwork: false 38 | hostPort: 39 | enable: false 40 | http: 80 41 | https: 443 42 | containerPort: 43 | http: 80 44 | https: 443 45 | dnsPolicy: ClusterFirst 46 | nginxDebug: false 47 | shareProcessNamespace: false 48 | logFormat: glog 49 | logLevel: info 50 | customPorts: [] 51 | image: 52 | repository: nginx/nginx-ingress 53 | tag: "5.3.1-ubi" 54 | # digest: "sha256:CHANGEME" 55 | pullPolicy: IfNotPresent 56 | lifecycle: {} 57 | customConfigMap: "" 58 | config: 59 | # name: nginx-config 60 | annotations: {} 61 | entries: {} 62 | defaultTLS: 63 | cert: "" 64 | key: "" 65 | secret: "" 66 | wildcardTLS: 67 | cert: "" 68 | key: "" 69 | secret: "" 70 | # nodeSelector: {} 71 | terminationGracePeriodSeconds: 30 72 | autoscaling: 73 | enabled: false 74 | annotations: {} 75 | minReplicas: 1 76 | maxReplicas: 3 77 | targetCPUUtilizationPercentage: 50 78 | targetMemoryUtilizationPercentage: 50 79 | behavior: {} 80 | resources: 81 | requests: 82 | cpu: 100m 83 | memory: 128Mi 84 | # limits: 85 | # cpu: 1 86 | # memory: 1Gi 87 | initContainerResources: 88 | requests: 89 | cpu: 100m 90 | memory: 128Mi 91 | # limits: 92 | # cpu: 1 93 | # memory: 1Gi 94 | tolerations: [] 95 | affinity: {} 96 | # topologySpreadConstraints: {} 97 | env: [] 98 | # - name: MY_VAR 99 | # value: myvalue 100 | volumes: [] 101 | # - name: extra-conf 102 | # configMap: 103 | # name: extra-conf 104 | volumeMounts: [] 105 | # - name: extra-conf 106 | # mountPath: /etc/nginx/conf.d/extra.conf 107 | # subPath: extra.conf 108 | initContainers: [] 109 | # - name: init-container 110 | # image: busybox:1.34 111 | # command: ['sh', '-c', 'echo this is initial setup!'] 112 | minReadySeconds: 0 113 | podDisruptionBudget: 114 | enabled: false 115 | annotations: {} 116 | # minAvailable: 1 117 | # maxUnavailable: 1 118 | strategy: {} 119 | extraContainers: [] 120 | # - name: container 121 | # image: busybox:1.34 122 | # command: ['sh', '-c', 'echo this is a sidecar!'] 123 | replicaCount: 1 124 | ingressClass: 125 | name: nginx 126 | create: true 127 | setAsDefaultIngress: false 128 | watchNamespace: "" 129 | watchNamespaceLabel: "" 130 | watchSecretNamespace: "" 131 | enableCustomResources: true 132 | enableOIDC: false 133 | enableTLSPassthrough: false 134 | tlsPassthroughPort: 443 135 | enableCertManager: false 136 | enableExternalDNS: false 137 | globalConfiguration: 138 | create: false 139 | spec: {} 140 | # listeners: 141 | # - name: dns-udp 142 | # port: 5353 143 | # protocol: UDP 144 | # - name: dns-tcp 145 | # port: 5353 146 | # protocol: TCP 147 | enableSnippets: false 148 | healthStatus: false 149 | healthStatusURI: "/nginx-health" 150 | nginxStatus: 151 | enable: true 152 | port: 8080 153 | allowCidrs: "127.0.0.1" 154 | service: 155 | create: true 156 | type: LoadBalancer 157 | externalTrafficPolicy: Local 158 | annotations: {} 159 | extraLabels: {} 160 | loadBalancerIP: "" 161 | clusterIP: "" 162 | externalIPs: [] 163 | loadBalancerSourceRanges: [] 164 | # allocateLoadBalancerNodePorts: false 165 | # ipFamilyPolicy: SingleStack 166 | # ipFamilies: 167 | # - IPv6 168 | httpPort: 169 | enable: true 170 | port: 80 171 | # nodePort: 80 172 | targetPort: 80 173 | httpsPort: 174 | enable: true 175 | port: 443 176 | # nodePort: 443 177 | targetPort: 443 178 | customPorts: [] 179 | serviceAccount: 180 | annotations: {} 181 | # name: nginx-ingress 182 | imagePullSecretName: "" 183 | imagePullSecretsNames: [] 184 | reportIngressStatus: 185 | enable: true 186 | # externalService: nginx-ingress 187 | ingressLink: "" 188 | enableLeaderElection: true 189 | leaderElectionLockName: "nginx-ingress-leader" 190 | annotations: {} 191 | pod: 192 | annotations: {} 193 | extraLabels: {} 194 | # priorityClassName: "" 195 | readyStatus: 196 | enable: true 197 | port: 8081 198 | initialDelaySeconds: 0 199 | enableLatencyMetrics: false 200 | disableIPV6: false 201 | defaultHTTPListenerPort: 80 202 | defaultHTTPSListenerPort: 443 203 | readOnlyRootFilesystem: false 204 | enableSSLDynamicReload: true 205 | rbac: 206 | create: true 207 | prometheus: 208 | create: true 209 | port: 9113 210 | secret: "" 211 | scheme: http 212 | service: 213 | create: false 214 | labels: 215 | service: "nginx-ingress-prometheus-service" 216 | serviceMonitor: 217 | create: false 218 | labels: {} 219 | selectorMatchLabels: 220 | service: "nginx-ingress-prometheus-service" 221 | endpoints: 222 | - port: prometheus 223 | serviceInsight: 224 | create: false 225 | port: 9114 226 | secret: "" 227 | scheme: http 228 | nginxServiceMesh: 229 | enable: false 230 | enableEgress: false 231 | ``` 232 | 233 | For detailed documentation of individual parameters, please refer to the [Configuration](https://docs.nginx.com/nginx-ingress-controller/install/helm/#configuration) section in our documentation on installing NGINX Ingress Controller with Helm. 234 | 235 | ## Notes 236 | * The service account name cannot be overridden and needs to be set to `nginx-ingress`. This is a requirement due to the RBAC and SCC configuration on OpenShift clusters. 237 | * The defaultServerSecret needs to be created before the IC deployment with the name of the secret supplied in the NginxIngress manifest, and cannot be created by instead supplying a cert and key. 238 | * If required, the `controller.wildcardTLS.secret` must also be created separately with the name of the secret supplied in the NginxIngress manifest. 239 | 240 | ## Notes: Multiple NIC Deployments 241 | * Please see [the NGINX Ingress Controller doumentation](https://docs.nginx.com/nginx-ingress-controller/install/multiple-controllers/) for general information on running multiple NGINX Ingress Controllers in your cluster. 242 | * To run multiple NIC instances deployed by the NGINX Ingress Operator in your cluster in the same namespace, `rbac.create` should be set to `false`, and the ServiceAccount and ClusterRoleBinding need to be created independently of the deployments. Please note that `controller.serviceAccount.imagePullSecretName` will also be ignored in this configuration, and will need to be configured as part of the independant ServiceAccount creation. 243 | * The ClusterRoleBinding needs to configured to bind to the `nginx-ingress-operator-nginx-ingress-admin` ClusterRole. 244 | * See [RBAC example spec](../resources/rbac-example.yaml) for an example ServiceAccount and ClusterRoleBinding manifest. 245 | * To run multiple NIC instances deployed by the NGINX Ingress Operator in your cluster in any namespace but sharing an IngressClass, `controller.ingressClass` should be set to an empty string and the IngressClass resource needs to be created independantly of the deployments.Please note that `controller.setAsDefaultIngress` will also be ignored in this configuration, and will need to be configured as part of the independant IngressClass creation. 246 | * See [IngressClass example spec](../resources/ingress-class.yaml) for an example IngressClass manifest. 247 | --------------------------------------------------------------------------------