|
| 1 | +# Publish your website as a public secured service |
| 2 | + |
| 3 | + |
| 4 | +## Requirements |
| 5 | + |
| 6 | + |
| 7 | +- read the previous chapter [Deploy abcdesktop on AWS with Amazon Elastic Kubernetes Service](aws) |
| 8 | +- an AWS account |
| 9 | +- your own internet domain |
| 10 | +- `aws` command line interface [aws-cli](https://aws.amazon.com/cli/) |
| 11 | +- `kubectl` command line |
| 12 | +- `helm` command line |
| 13 | + |
| 14 | +## Overview |
| 15 | + |
| 16 | +In this chapter we are going to, use a `nginx-ingress-controller` to host your abcdesktop service with a public IP Address, then configure dns zone file to use your own domain name, and activate TLS to secure your service. |
| 17 | + |
| 18 | +## Add tags for public subnets |
| 19 | + |
| 20 | +By default, when creating your VPC, the public subnets does not have the `kubernetes.io/role/elb=1` tag, but this tag is mandatory in order to expose our service using a nginx ingress controller with AWS. Actually, AWS scans your VPC, searching for the subnets with this percise tag to place the ingress controller. |
| 21 | + |
| 22 | +To do so, run the following command |
| 23 | + |
| 24 | +``` |
| 25 | +aws ec2 create-tags --resources <your_public_subnets_ids> --tags Key=kubernetes.io/role/elb,Value=1 |
| 26 | +``` |
| 27 | + |
| 28 | +> You can find your public subnets ids in the `Subnets` page of the VPC dashboard on AWS console |
| 29 | +
|
| 30 | +You can check if the tags have been applied by running the following command |
| 31 | + |
| 32 | +``` |
| 33 | +aws ec2 describe-subnets --filters "Name=vpc-id,Values=<your_vpc_id>" "Name=tag:kubernetes.io/role/elb,Values=1" --query 'Subnets[*].[SubnetId,AvailabilityZone]' --output table |
| 34 | +``` |
| 35 | + |
| 36 | +You should read on stdout |
| 37 | + |
| 38 | +``` |
| 39 | +-------------------------------------------- |
| 40 | +| DescribeSubnets | |
| 41 | ++---------------------------+--------------+ |
| 42 | +| subnet-09c09bd5bbdec72a6 | us-east-1b | |
| 43 | +| subnet-02d592feab5bb0faf | us-east-1a | |
| 44 | ++---------------------------+--------------+ |
| 45 | +
|
| 46 | +``` |
| 47 | + |
| 48 | +## Deploy nginx ingress controller |
| 49 | + |
| 50 | +You will now deploy a nginx ingress controller on your cluster using `helm`. |
| 51 | + |
| 52 | +First, run the following command to add the nginx ingress controller repository : |
| 53 | + |
| 54 | +``` |
| 55 | +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx && helm repo update |
| 56 | +``` |
| 57 | + |
| 58 | +Then install it on your cluster |
| 59 | + |
| 60 | +``` |
| 61 | +helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx --create-namespace |
| 62 | +``` |
| 63 | + |
| 64 | +Once the installation process completed, you can check that the service has been createed by running this command : |
| 65 | + |
| 66 | +``` |
| 67 | +kubectl get svc ingress-nginx-controller -n ingress-nginx |
| 68 | +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
| 69 | +ingress-nginx-controller LoadBalancer 172.20.87.254 <pending> 80:32337/TCP,443:30414/TCP 34s |
| 70 | +``` |
| 71 | + |
| 72 | +You will see that `EXTERNAL-IP` is `<pending>` forever, it is totally noraml because by default the controller is considering your service as internal, to make it public you should add annotations by running the following command |
| 73 | + |
| 74 | +``` |
| 75 | +kubectl annotate svc ingress-nginx-controller -n ingress-nginx service.beta.kubernetes.io/aws-load-balancer-scheme=internet-facing service.beta.kubernetes.io/aws-load-balancer-type=nlb --overwrite |
| 76 | +``` |
| 77 | + |
| 78 | +Now you can run the previous command until you get an `EXTERNAL-IP` |
| 79 | + |
| 80 | +``` |
| 81 | +kubectl get svc ingress-nginx-controller -n ingress-nginx |
| 82 | +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
| 83 | +ingress-nginx-controller LoadBalancer 172.20.87.254 k8s-ingressn-ingressn-dc31c08978-b7ef1bca03cfe45b.elb.us-east-1.amazonaws.com 80:32337/TCP,443:30414/TCP 3m |
| 84 | +
|
| 85 | +``` |
| 86 | + |
| 87 | +### Create new record |
| 88 | + |
| 89 | +We are going to create a new record `hello` (`hello.aws.pepins.net`) using `k8s-ingressn-ingressn-dc31c08978-b7ef1bca03cfe45b.elb.us-east-1.amazonaws.com` as an alias. |
| 90 | + |
| 91 | +First, you will need your load balancer hosted zone id. To get, run the following command : |
| 92 | + |
| 93 | +``` |
| 94 | +aws elbv2 describe-load-balancers --region us-east-1 --query 'LoadBalancers[0].CanonicalHostedZoneId' |
| 95 | +``` |
| 96 | + |
| 97 | +You should get something like this |
| 98 | + |
| 99 | +``` |
| 100 | +Z26RNL4JYFTOTI |
| 101 | +``` |
| 102 | + |
| 103 | +Now paste the following lines in a `create-record-abcdesktop-aws.json` file : |
| 104 | + |
| 105 | +``` |
| 106 | +{ |
| 107 | + "Comment": "create a new record for abcdesktop on aws", |
| 108 | + "Changes": [ |
| 109 | + { |
| 110 | + "Action": "CREATE", |
| 111 | + "ResourceRecordSet": { |
| 112 | + "Name": "hello.aws.pepins.net.", |
| 113 | + "Type": "A", |
| 114 | + "AliasTarget": { |
| 115 | + "HostedZoneId": "<your_hosted_zone_id>", |
| 116 | + "DNSName": "k8s-ingressn-ingressn-dc31c08978-b7ef1bca03cfe45b.elb.us-east-1.amazonaws.com", |
| 117 | + "EvaluateTargetHealth": false |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + ] |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +Now, you will need to retrieve the hosted zone id of your domain on route 53. You can get it through the Route 53 web interface, or by running the following command : |
| 126 | + |
| 127 | +``` |
| 128 | +aws route53 list-hosted-zones --query 'HostedZones[*].[Name,Id]' --output table |
| 129 | +``` |
| 130 | + |
| 131 | +You should read something like this |
| 132 | + |
| 133 | +``` |
| 134 | +-------------------------------------------------------- |
| 135 | +| ListHostedZones | |
| 136 | ++------------------+-----------------------------------+ |
| 137 | +| aws.pepins.net. | /hostedzone/Z0710575SCBYT0OUKZP | |
| 138 | ++------------------+-----------------------------------+ |
| 139 | +``` |
| 140 | + |
| 141 | +Finally, run this command to add the record : |
| 142 | + |
| 143 | +``` |
| 144 | +aws route53 change-resource-record-sets --hosted-zone-id <your_domain_hosted_zone_id> --change-batch file://create-record-abcdesktop-aws.json |
| 145 | +``` |
| 146 | + |
| 147 | +For example |
| 148 | + |
| 149 | +``` |
| 150 | +aws route53 change-resource-record-sets --hosted-zone-id Z0710575SCBYT0OUKZP --change-batch file://create-record-abcdesktop-aws.json |
| 151 | +``` |
| 152 | + |
| 153 | +You should read something like this |
| 154 | + |
| 155 | +``` |
| 156 | +{ |
| 157 | + "ChangeInfo": { |
| 158 | + "Id": "/change/C07091422RBO1K64I6QWN", |
| 159 | + "Status": "PENDING", |
| 160 | + "SubmittedAt": "2026-01-14T13:30:30.133000+00:00", |
| 161 | + "Comment": "create a new record for abcdesktop on aws" |
| 162 | + } |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +If you go to your Route 53 web console, you should see the record you juste added |
| 167 | + |
| 168 | + |
| 169 | + |
| 170 | +From your local device, you can open a web browser |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | + |
| 175 | +Web browser doesn't allow usage of websocket without secure protocol. To login you need `https` protocol. |
| 176 | + |
| 177 | +As you can see, your website is `Not Secured`, we are going to add X509 SSL certificate to secure your service. |
| 178 | + |
| 179 | +## Enable HTTPS |
| 180 | + |
| 181 | +### Install Cert Manager add-on on AWS EKS marketplace |
| 182 | + |
| 183 | +Navigate to the Add-ons section of your cluster on the AWS console, then click on `Get more add-ons` and install Cert Manager. |
| 184 | + |
| 185 | + |
| 186 | + |
| 187 | +Once intalled, you can inspect the Kubernetes ressources created by Cert Manager : |
| 188 | + |
| 189 | +``` |
| 190 | +kubectl get all -n cert-manager |
| 191 | +``` |
| 192 | + |
| 193 | +The output looks similar to the following |
| 194 | + |
| 195 | +``` |
| 196 | +NAME READY STATUS RESTARTS AGE |
| 197 | +pod/cert-manager-76f99d9cbb-z5dgj 1/1 Running 0 3h26m |
| 198 | +pod/cert-manager-76f99d9cbb-z7jnv 1/1 Running 0 3h26m |
| 199 | +pod/cert-manager-cainjector-558b47dbf9-h55gx 1/1 Running 0 3h26m |
| 200 | +pod/cert-manager-cainjector-558b47dbf9-lvgzl 1/1 Running 0 3h26m |
| 201 | +pod/cert-manager-webhook-7db584cc6c-9qz5q 1/1 Running 0 3h26m |
| 202 | +pod/cert-manager-webhook-7db584cc6c-l5s58 1/1 Running 0 3h26m |
| 203 | +
|
| 204 | +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
| 205 | +service/cert-manager ClusterIP 172.20.196.196 <none> 9402/TCP 3h26m |
| 206 | +service/cert-manager-cainjector ClusterIP 172.20.102.95 <none> 9402/TCP 3h26m |
| 207 | +service/cert-manager-webhook ClusterIP 172.20.105.49 <none> 443/TCP,9402/TCP 3h26m |
| 208 | +
|
| 209 | +NAME READY UP-TO-DATE AVAILABLE AGE |
| 210 | +deployment.apps/cert-manager 2/2 2 2 3h26m |
| 211 | +deployment.apps/cert-manager-cainjector 2/2 2 2 3h26m |
| 212 | +deployment.apps/cert-manager-webhook 2/2 2 2 3h26m |
| 213 | +
|
| 214 | +NAME DESIRED CURRENT READY AGE |
| 215 | +replicaset.apps/cert-manager-76f99d9cbb 2 2 2 3h26m |
| 216 | +replicaset.apps/cert-manager-cainjector-558b47dbf9 2 2 2 3h26m |
| 217 | +replicaset.apps/cert-manager-webhook-7db584cc6c 2 2 2 3h26m |
| 218 | +``` |
| 219 | + |
| 220 | +The cert-manager pods and webhook service are running. |
| 221 | + |
| 222 | +Cert-Manager creates custom resource definitions (CRDs). Cert-Manager relies on three important CRDs to issue certificates from a Certificate Authority (such as Let’s Encrypt): |
| 223 | + |
| 224 | +Issuer: Defines a namespaced certificate issuer, which allows you to use different CAs in each namespace. |
| 225 | + |
| 226 | +ClusterIssuer: Similar to Issuer, but it does not belong to a namespace and can be used to issue certificates in any namespace. |
| 227 | + |
| 228 | +Certificate: Defines a namespaced resource that references an Issuer or ClusterIssuer for issuing certificates. |
| 229 | + |
| 230 | +Inspect the CRDs by running the following command : |
| 231 | + |
| 232 | +``` |
| 233 | +kubectl get crd -l app.kubernetes.io/name=cert-manager |
| 234 | +``` |
| 235 | + |
| 236 | +The output looks similar to the following |
| 237 | + |
| 238 | +``` |
| 239 | +NAME CREATED AT |
| 240 | +certificaterequests.cert-manager.io 2026-01-19T10:23:33Z |
| 241 | +certificates.cert-manager.io 2026-01-19T10:23:34Z |
| 242 | +challenges.acme.cert-manager.io 2026-01-19T10:23:33Z |
| 243 | +clusterissuers.cert-manager.io 2026-01-19T10:23:34Z |
| 244 | +issuers.cert-manager.io 2026-01-19T10:23:34Z |
| 245 | +orders.acme.cert-manager.io 2026-01-19T10:23:33Z |
| 246 | +``` |
| 247 | + |
| 248 | +### Configure Production-Ready TLS Certificates for nginx |
| 249 | + |
| 250 | +You can issue the certificate using an Issuer. Configure a certificate issuers resource for Cert-Manager, which fetches the TLS certificate for nginx to use. The certificate issuer uses the HTTP-01 challenge provider to accomplish this task. |
| 251 | + |
| 252 | +Create the following manifest, replace `<your-valid-email-address>` with your own value, and save it as cert-manager-issuer.yaml : |
| 253 | + |
| 254 | +``` |
| 255 | +apiVersion: cert-manager.io/v1 |
| 256 | +kind: Issuer |
| 257 | +metadata: |
| 258 | + name: letsencrypt-nginx |
| 259 | +spec: |
| 260 | + acme: |
| 261 | + email: <your-valid-email-address> |
| 262 | + server: https://acme-v02.api.letsencrypt.org/directory |
| 263 | + privateKeySecretRef: |
| 264 | + name: letsencrypt-nginx-private-key |
| 265 | + solvers: |
| 266 | + # Use the HTTP-01 challenge provider |
| 267 | + - http01: |
| 268 | + ingress: |
| 269 | + class: nginx |
| 270 | +``` |
| 271 | + |
| 272 | +The ACME issuer configuration has the following fields: |
| 273 | + |
| 274 | +email: Email address to be associated with the ACME account. |
| 275 | +server: URL used to access the ACME server’s directory endpoint. |
| 276 | +privateKeySecretRef: Kubernetes secret to store the automatically generated ACME account private key. |
| 277 | + |
| 278 | +The ingress resources use the HTTP-01 challenge. |
| 279 | + |
| 280 | +``` |
| 281 | +kubectl apply -f cert-manager-issuer.yaml -n abcdesktop |
| 282 | +``` |
| 283 | + |
| 284 | +The output looks similar to the following |
| 285 | + |
| 286 | +``` |
| 287 | +issuer.cert-manager.io/letsencrypt-nginx created |
| 288 | +``` |
| 289 | + |
| 290 | +Verify that the Issuer resource is created: |
| 291 | + |
| 292 | +``` |
| 293 | +kubectl get issuer -n abcdesktop |
| 294 | +``` |
| 295 | + |
| 296 | +The output looks similar to the following |
| 297 | + |
| 298 | +``` |
| 299 | +NAME READY AGE |
| 300 | +letsencrypt-nginx True 7s |
| 301 | +``` |
| 302 | + |
| 303 | +Next, configure each nginx ingress resource to use TLS. Open the previous `abcdesktop_host.yaml` manifest you created previously for the route application, add the `annotations` and `tls` sections shown below, and save the `abcdesktop_host.yaml` file : You can also add dedicated `nginx.ingress.kubernetes.io` annotations to increase default timeout values. Replace `hello.aws.pepins.net` by own FQDN |
| 304 | + |
| 305 | +``` |
| 306 | +apiVersion: networking.k8s.io/v1 |
| 307 | +kind: Ingress |
| 308 | +metadata: |
| 309 | + name: ingress-abcdesktop |
| 310 | + namespace: abcdesktop |
| 311 | + annotations: |
| 312 | + cert-manager.io/issuer: letsencrypt-nginx |
| 313 | + nginx.org/client-max-body-size: "256M" |
| 314 | + nginx.ingress.kubernetes.io/proxy-connect-timeout: "30" |
| 315 | + nginx.ingress.kubernetes.io/proxy-read-timeout: "1800" |
| 316 | + nginx.ingress.kubernetes.io/proxy-send-timeout: "1800" |
| 317 | + nginx.ingress.kubernetes.io/proxy-body-size: "256M" |
| 318 | +spec: |
| 319 | + tls: |
| 320 | + - hosts: |
| 321 | + - hello.aws.pepins.net |
| 322 | + secretName: letsencrypt-nginx-echo |
| 323 | + rules: |
| 324 | + - host: hello.aws.pepins.net |
| 325 | + http: |
| 326 | + paths: |
| 327 | + - path: / |
| 328 | + pathType: Prefix |
| 329 | + backend: |
| 330 | + service: |
| 331 | + name: http-router |
| 332 | + port: |
| 333 | + number: 80 |
| 334 | + ingressClassName: nginx |
| 335 | +``` |
| 336 | + |
| 337 | +Run the following command to configure the hosts to use TLS: |
| 338 | + |
| 339 | +``` |
| 340 | +kubectl apply -f abcdesktop_host.yaml -n abcdesktop |
| 341 | +``` |
| 342 | + |
| 343 | +After a few minutes, check the state of the ingress object: |
| 344 | + |
| 345 | +``` |
| 346 | +kubectl get ingress -n abcdesktop |
| 347 | +NAME CLASS HOSTS ADDRESS PORTS AGE |
| 348 | +ingress-abcdesktop nginx hello.aws.pepins.net k8s-ingressn-ingressn-dc31c08978-b7ef1bca03cfe45b.elb.us-east-1.amazonaws.com 80, 443 42m |
| 349 | +``` |
| 350 | + |
| 351 | +You see that 443 appeard in the `PORTS` section. |
| 352 | + |
| 353 | +Check that the certificate resource is created |
| 354 | + |
| 355 | +``` |
| 356 | +kubectl get certificates -n abcdesktop |
| 357 | +``` |
| 358 | + |
| 359 | +The output looks similar to the following |
| 360 | + |
| 361 | +``` |
| 362 | +NAME READY SECRET AGE |
| 363 | +letsencrypt-nginx-echo True letsencrypt-nginx-echo 3m27s |
| 364 | +``` |
| 365 | + |
| 366 | +Run a simple curl command line `curl -Li https://hello.aws.pepins.net/` to confirm that your secured abcdesktop service is running. |
| 367 | + |
| 368 | +``` |
| 369 | +curl -Li https://hello.aws.pepins.net/ |
| 370 | +HTTP/2 200 |
| 371 | +date: Mon, 19 Jan 2026 14:06:46 GMT |
| 372 | +content-type: text/html |
| 373 | +content-length: 56291 |
| 374 | +vary: Accept-Encoding |
| 375 | +last-modified: Tue, 16 Dec 2025 11:52:18 GMT |
| 376 | +etag: "694147f2-dbe3" |
| 377 | +accept-ranges: bytes |
| 378 | +x-frame-options: SAMEORIGIN |
| 379 | +x-xss-protection: 1; mode=block |
| 380 | +strict-transport-security: max-age=31536000; includeSubDomains |
| 381 | +
|
| 382 | +<!doctype html> |
| 383 | +... |
| 384 | +``` |
| 385 | + |
| 386 | +## Reach your website using `https` protocol |
| 387 | + |
| 388 | +You can now connect to your abcdesktop desktop pulic web site using `https` protocol. |
| 389 | + |
| 390 | + |
| 391 | + |
| 392 | +The status is secured and we get some informations from the certificate |
| 393 | + |
| 394 | + |
0 commit comments