Installing an NGINX Ingress controller with a certificate from Certificate Manager
- Getting started
- Add a certificate to Certificate Manager
- Install the External Secrets Operator
- Configure the Kubernetes cluster
- Create an ExternalSecret
- Install the NGINX Ingress controller
- Create a web resource in your cluster
- Configure a DNS record for the Ingress controller
- Create an Ingress resource
- Check resource availability
- Delete the resources you created
Manage the TLS certificate for the NGINX Ingress controller via Certificate Manager.
The External Secrets Operator
Getting started
-
If you don't have the Nebius Israel command line interface yet, install and initialize it.
The folder specified in the CLI profile is used by default. You can specify a different folder using the
--folder-name
or--folder-id
parameter. -
Install the
jq
utility:sudo apt update && sudo apt install jq
-
Create a service account named
eso-service-account
. You'll need it to work with the External Secrets Operator. -
Create an authorized key for the service account and save it to the
authorized-key.json
file:yc iam key create \ --service-account-name eso-service-account \ --output authorized-key.json
-
Create a Managed Service for Kubernetes cluster and a node group in any suitable configuration.
-
Install kubectl
and configure it to work with the created cluster.
Required paid resources
The infrastructure support cost includes:
- Using the master and outgoing Managed Service for Kubernetes traffic (see Managed Service for Kubernetes pricing).
- Using Managed Service for Kubernetes cluster nodes (see Compute Cloud pricing).
- Using public IPs (see Virtual Private Cloud pricing).
- Incoming traffic (processed by the load balancer) and using a network load balancer (see Network Load Balancer pricing).
Add a certificate to Certificate Manager
-
Issue a Let's Encrypt® certificate and add it to Certificate Manager or upload your own certificate.
-
For the Let's Encrypt® certificate, have your rights checked for the domain named in the certificate.
-
Grant the
certificate-manager.certificates.downloader
role to theeso-service-account
service account for it to be able to read the certificate contents:yc cm certificate add-access-binding \ --id <certificate_ID> \ --service-account-name eso-service-account \ --role certificate-manager.certificates.downloader
-
Check to see that the rights have been granted:
yc cm certificate list-access-bindings --id <certificate_ID>
Command result:
+---------------------------------------------+----------------+-------------------------------------+ | ROLE ID | SUBJECT TYPE | SUBJECT ID | +---------------------------------------------+----------------+-------------------------------------+ | certificate-manager.certificates.downloader | serviceAccount | <service_account_ID> | +---------------------------------------------+----------------+-------------------------------------+
Install the External Secrets Operator
-
Add a Helm repository named
external-secrets
:helm repo add external-secrets https://charts.external-secrets.io
-
Install the External Secrets Operator in the Kubernetes cluster:
helm install external-secrets \ external-secrets/external-secrets \ --namespace external-secrets \ --create-namespace
This command creates a new
external-secrets
namespace required for using the External Secrets Operator.Command result:
NAME: external-secrets LAST DEPLOYED: Sun Sep 19 11:20:58 2021 NAMESPACE: external-secrets STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: external-secrets has been deployed successfully! ...
Configure the Kubernetes cluster
-
Create a namespace called
ns
for External Secrets Operator objects:kubectl create namespace ns
-
Create a
yc-auth
secret with theeso-service-account
key:kubectl --namespace ns create secret generic yc-auth \ --from-file=authorized-key=authorized-key.json
-
Create a SecretStore
calledsecret-store
containing a secret namedyc-auth
:kubectl --namespace ns apply -f - <<< ' apiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata: name: secret-store spec: provider: yandexcertificatemanager: auth: authorizedKeySecretRef: name: yc-auth key: authorized-key'
Create an ExternalSecret
-
Create an ExternalSecret
object namedexternal-secret
pointing to the certificate from Certificate Manager:kubectl --namespace ns apply -f - <<< ' apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: external-secret spec: refreshInterval: 1h secretStoreRef: name: secret-store kind: SecretStore target: name: k8s-secret template: type: kubernetes.io/tls data: - secretKey: tls.crt remoteRef: key: <certificate_ID> property: chain - secretKey: tls.key remoteRef: key: <certificate_ID> property: privateKey'
Where:
k8s-secret
: Name of the secret to be used by the External Secret Operator to contain the certificate from Certificate Manager.tls.crt
: Parameter of thek8s-secret
secret that will contain the certificate.tls.key
: Parameter of thek8s-secret
secret that will contain the certificate's private key.
The following are the legal values of the
property
parameter:chain
: Retrieve a chain of certificates in PEM format.privateKey
: Retrieve the private key in PEM format.chainAndPrivateKey
or null value: Retrieve both the certificate chain and the private key.
The External Secrets Operator will get a certificate from Certificate Manager and place it in the
k8s-secret
secret. -
Make sure the certificate was placed in the
k8s-secret
secret:kubectl -n ns get secret k8s-secret -ojson \ | jq '."data"."tls.crt"' -r \ | base64 --decode
Result example:
-----BEGIN CERTIFICATE----- MIIFKTCCBBGgAwIBAgISBAlQtxTUnXa75N1TnPYRWbSLMA0GCSqGSIb3DQEBCwUA MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD EwJSMzAeFw0yMjA3MTMxNDMxNTVaFw0yMjEwMTExNDMxNTRaMB0xGzAZBgNVBAMT EmRkb3Mtd2ViLm5yay5tZS51azCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC…
To view the certificate in human-readable format, run the following commands:
kubectl -n ns get secret k8s-secret -ojson | jq '."data"."tls.crt"' -r \
| base64 --decode > cert.pem
openssl x509 -in cert.pem -text
Result example:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
04:09:50:b7:14:d4:9d:76:bb:e4:dd:53:9c:f6:11:59:b4:8b
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Let's Encrypt, CN = R3
Validity
Not Before: Jul 13 14:31:55 2022 GMT
Not After : Oct 11 14:31:54 2022 GMT
Subject: CN = example.com
...
Install the NGINX Ingress controller
-
Add a repository for NGINX to Helm:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
Command result:
"ingress-nginx" has been added to your repositories
-
Update the dataset to create an application instance in the Kubernetes cluster:
helm repo update
Command result:
Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "ingress-nginx" chart repository Update Complete. ⎈Happy Helming!⎈
-
Install the controller in the standard configuration. The controller will be installed with Network Load Balancer:
helm install ingress-nginx ingress-nginx/ingress-nginx
Command result:
NAME: ingress-nginx LAST DEPLOYED: Sun Jul 18 22:35:37 2021 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: The ingress-nginx controller has been installed. It may take a few minutes for the LoadBalancer IP to be available. You can watch the status by running 'kubectl --namespace default get services -o wide -w ingress-nginx-controller' ...
To be able to install the controller on your own, review the Helm documentationvalues.yaml
.
Create a web resource in your cluster
Create a Deployment object
kubectl --namespace ns apply -f - <<< '
apiVersion: v1
kind: Service
metadata:
name: app
spec:
selector:
app: app
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
labels:
app: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: nginx:latest
ports:
- containerPort: 80'
Configure a DNS record for the Ingress controller
-
Find out the IP address of the Ingress controller (the value in the
EXTERNAL-IP
column):kubectl get svc
Command result:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... ingress-nginx-controller LoadBalancer 10.96.164.252 84.201.153.122 80:31248/TCP,443:31151/TCP 2m19s ...
-
Host an A record with your DNS provider or on your own DNS server that will indicate the public IP address of the Ingress controller:
<domain_name> IN A 84.201.153.122
Note
Registering the Let's Encrypt® certificate and an A record may take a few minutes.
Create an Ingress resource
Create an Ingressk8s-secret
certificate for HTTPS:
kubectl --namespace ns apply -f - <<< '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-test
namespace: ns
spec:
tls:
- hosts:
- <domain_name>
secretName: k8s-secret
ingressClassName: nginx
rules:
- host: <domain_name>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80'
Where <domain_name>
is the name of the domain for which the certificate is issued.
Check resource availability
Issue a GET request to the resource via HTTPS, for example, by this command:
curl <domain_name> -vv
Result example:
* Trying 51.250.64.86:443...
* Connected to <domain_name> (51.250.64.86) port 443 (#0)
...
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=<domain_name>
* start date: Jul 13 14:31:55 2022 GMT
* expire date: Oct 11 14:31:54 2022 GMT
* subjectAltName: host "<domain_name>" matched cert's "<domain_name>"
...
* SSL certificate verify ok.
The Let's Encrypt® certificate must update automatically after the certificate update in Certificate Manager.
You can specify a sync timeout in the refreshInterval
parameter of the ExternalSecret object.
Delete the resources you created
Some resources are not free of charge. To avoid paying for them, delete the resources you no longer need: