GCP Cloud DNS and Certificates

Published: Jul 25, 2024 by Isaac Johnson

I know I cover a lot of Azure DNS (any post that covers tpk.pw). I also cover Route53 in AWS quite often (freshbrewed.science). However, as I look back, I realize I have yet to cover GCP CloudDNS.

Today we’ll setup a new Domain and add it as a fresh Hosted Zone in CloudDNS. We’ll look at configuring an on-prem cluster using a ClusterIssuer with a new GCP SA. Once we have shown using CloudDNS with the ACME resolver in k3s, we’ll move on to creating certificates. We’ll look at a few ways one can (try) to create in the UI, then look at using certbot for a self-managed LE cert.

Lastly, we’ll touch on using CloudDNS with certificates, both GCP managed and self-managed as we create static websites in GCP Buckets. We’ll setup up Application Load Balancers (ALBs) using Cloud DNS and Certificate Manager with public buckets.

Setup

To use CloudDNS, we need a domain we can host there.

Here I have a steeped.space DNS I registered in Gandi

/content/images/2024/07/clouddns-01.png

As you can see, I so far have no HZ in Cloud DNS

/content/images/2024/07/clouddns-02.png

I’ll click “Create Zone” to start and fill out the Hosted Zone entry for our TLD

/content/images/2024/07/clouddns-03.png

While this has now created a Hosted Zone

/content/images/2024/07/clouddns-04.png

We need to update our DNS nameservers to actually make it live

I’ll use gcloud auth login then ask GCP what those should be

$ gcloud dns managed-zones describe steepedspace
cloudLoggingConfig:
  enableLogging: false
  kind: dns#managedZoneCloudLoggingConfig
creationTime: '2024-07-23T11:24:04.427Z'
description: steeped.space
dnsName: steeped.space.
id: '4856857326296579750'
kind: dns#managedZone
name: steepedspace
nameServers:
- ns-cloud-a1.googledomains.com.
- ns-cloud-a2.googledomains.com.
- ns-cloud-a3.googledomains.com.
- ns-cloud-a4.googledomains.com.
visibility: public

In Gandi, right now it’s set as the DNS Registrar and host

/content/images/2024/07/clouddns-05.png

I’ll click the pencil in the “Gandi’s LiveDNS nameservers” table to change them

I switch to external and put in the GCP values

/content/images/2024/07/clouddns-06.png

I can now seem them change over

/content/images/2024/07/clouddns-07.png

Testing

Let’s add a standard record set in the Hosted Zone

I’ll start with a CName to just repoint fb over to this website

/content/images/2024/07/clouddns-08.png

My first error in trying to use it came from cloudfront

/content/images/2024/07/clouddns-09.png

That said, I know it works because I tried redirecting Harbor

/content/images/2024/07/clouddns-10.png

and that gave me a nice 404 from my Nginx host telling me it got to the Prod cluster (my Nginx filters on hostname so this is expected)

/content/images/2024/07/clouddns-11.png

Intergaration with Kubernetes

To really use this, we need a CloudDNS cluster issuer

We can follow the docs to get a Service Account setup in GCP

I’ll create a new SA and apply the DNS Admin policy to it

builder@DESKTOP-QADGF36:~$ PROJECT_ID=myanthosproject2

builder@DESKTOP-QADGF36:~$ gcloud iam service-accounts create dns01-solver --display-name "dns01-solver"
Created service account [dns01-solver].

builder@DESKTOP-QADGF36:~$ gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:dns01-solver@$PROJECT_ID.iam.gserviceaccount.com --role roles/dns.admin
Updated IAM policy for project [myanthosproject2].
bindings:
- members:
  - serviceAccount:service-511842454269@gcp-sa-aiplatform.iam.gserviceaccount.com
  role: roles/aiplatform.serviceAgent
- members:

... snip ...

- members:
  - serviceAccount:configure8-demo@myanthosproject2.iam.gserviceaccount.com
  role: roles/viewer
etag: BwYd6KN5O7Q=
version: 1

We then need an SA Secret for the credentials

$ gcloud iam service-accounts keys create gcpsakey.json --iam-account dns01-solver@$PROJECT_ID.iam.gserviceaccount.com
created key [ddcb59e7cb63a5e6cc9384ae3e1d91305a8173f1] of type [json] as [gcpsakey.json] for [dns01-solver@myanthosproject2.iam.gserviceaccount.com]

That created and saved the local Key JSON. This is a pretty important file as it gives all the details a system would need to be my DNS Admin for my GCP Hosted Zones

builder@DESKTOP-QADGF36:~$ ls -ltra | tail -n1
-rw-------   1 builder builder      2370 Jul 23 06:43 gcpsakey.json

I now need to add as a secret the ClusterIssuer can reference.

I honestly cannot decide if it just needs to be created in the default or cert-manager namespace. I created in both, to be honest. My past examples used both namespaces. Likely something I’ll have to tease out in time.

builder@DESKTOP-QADGF36:~$ kubectl create secret generic clouddns-dns01-solver-svc-acct --from-file=gcpsakey.json
secret/clouddns-dns01-solver-svc-acct created
builder@DESKTOP-QADGF36:~$ kubectl create secret generic clouddns-dns01-solver-svc-acct -n cert-manager --from-file=gcpsakey.json
secret/clouddns-dns01-solver-svc-acct created

The documentation leaves a few details out

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: example-issuer
spec:
  acme:
    ...
    solvers:
    - dns01:
        cloudDNS:
          # The ID of the GCP project
          project: $PROJECT_ID
          # This is the secret used to access the service account
          serviceAccountSecretRef:
            name: clouddns-dns01-solver-svc-acct
            key: key.json

For example, knowing my project is myAnthosProject2 and the HZ is steepedspace, I will use

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: gcp-le-prod
spec:
  acme:
    email: isaac.johnson@gmail.com
    privateKeySecretRef:
      name: gcp-le-prod
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - dns01:
        cloudDNS:
          project: myanthosproject2
          serviceAccountSecretRef:
            name: clouddns-dns01-solver-svc-acct
            key: gcpsakey.json
      selector:
        dnsZones:
        - steepedspace

I can apply it

builder@DESKTOP-QADGF36:~$ kubectl apply -f ./gcpclusterissuer.yaml
clusterissuer.cert-manager.io/gcp-le-prod created

Because it is a ClusterIssuer, we can see it sans namespace

builder@DESKTOP-QADGF36:~$ kubectl get clusterissuer
NAME               READY   AGE
letsencrypt-prod   True    142d
azuredns-tpkpw     True    141d
gcp-le-prod        True    21s

I know it’s in a good way as the status shows it’s registered

builder@DESKTOP-QADGF36:~$ kubectl get clusterissuer gcp-le-prod -o yaml | tail -n12
status:
  acme:
    lastPrivateKeyHash: EHYYbkOQl0cw1UJ65mEFlaBHwK5HHOAZwETqBQu8rcg=
    lastRegisteredEmail: isaac.johnson@gmail.com
    uri: https://acme-v02.api.letsencrypt.org/acme/acct/1852596517
  conditions:
  - lastTransitionTime: "2024-07-23T11:52:33Z"
    message: The ACME account was registered with the ACME server
    observedGeneration: 1
    reason: ACMEAccountRegistered
    status: "True"
    type: Ready

Testing in Kubernetes

Let’s look at a recent service

builder@DESKTOP-QADGF36:~$ kubectl get ingress | tail -n1
filegatoringress          <none>   filegator.tpk.pw                            80, 443   9d

I’ll dump it local so i can change it

builder@DESKTOP-QADGF36:~$ kubectl get ingress filegatoringress -o yaml > filegatorgcp.yaml

I’ll rework it to use the same backend service, but a new secret, issuer and ingress name

$ cat filegatorgcp.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: gcp-le-prod
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: filegator-external-ip
  labels:
    app.kubernetes.io/instance: filegatorgcpingress
  name: filegatorgcpingress
spec:
  rules:
  - host: filegator.steeped.space
    http:
      paths:
      - backend:
          service:
            name: filegator-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - filegator.steeped.space
    secretName: filegatorgcp-tls

I can now create my A record

$ gcloud dns --project=myanthosproject2 record-sets create filegator.steeped.space --zone="steepedspace" --type="A" --ttl="300" --r
rdatas="75.73.224.240"
NAME                      TYPE  TTL  DATA
filegator.steeped.space.  A     300  75.73.224.240

And apply it

$ cat ./filegatorgcp.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    ingress.kubernetes.io/proxy-body-size: "0"
    ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: "0"
    nginx.org/proxy-connect-timeout: "3600"
    nginx.org/proxy-read-timeout: "3600"
    cert-manager.io/cluster-issuer: gcp-le-prod
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: filegator-external-ip
  labels:
    app.kubernetes.io/instance: filegatorgcpingress
  name: filegatorgcpingress
spec:
  rules:
  - host: filegator.steeped.space
    http:
      paths:
      - backend:
          service:
            name: filegator-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - filegator.steeped.space
    secretName: filegatorgcp-tls

$ kubectl apply -f ./filegatorgcp.yaml
ingress.networking.k8s.io/filegatorgcpingress created

I waited a while, but did not see my cert get created

$ kubectl get cert filegatorgcp-tls
NAME               READY   SECRET             AGE
filegatorgcp-tls   False   filegatorgcp-tls   3m42s

Seems it is hung on the order

$ kubectl get order filegatorgcp-tls-1-3422691204 -o yaml | tail -n19
status:
  authorizations:
  - challenges:
    - token: udByIEDfNBzh4LLFX_eQWIHJfn0CpqgHuDzMm9wXyCk
      type: http-01
      url: https://acme-v02.api.letsencrypt.org/acme/chall-v3/380786063777/b3mOVw
    - token: udByIEDfNBzh4LLFX_eQWIHJfn0CpqgHuDzMm9wXyCk
      type: dns-01
      url: https://acme-v02.api.letsencrypt.org/acme/chall-v3/380786063777/zZu-1w
    - token: udByIEDfNBzh4LLFX_eQWIHJfn0CpqgHuDzMm9wXyCk
      type: tls-alpn-01
      url: https://acme-v02.api.letsencrypt.org/acme/chall-v3/380786063777/bT3Vsg
    identifier: filegator.steeped.space
    initialState: pending
    url: https://acme-v02.api.letsencrypt.org/acme/authz-v3/380786063777
    wildcard: false
  finalizeURL: https://acme-v02.api.letsencrypt.org/acme/finalize/1852596517/289858846917
  state: pending
  url: https://acme-v02.api.letsencrypt.org/acme/order/1852596517/289858846917

Let’s try another way.

I removed the current request by just nixing the ingress

builder@DESKTOP-QADGF36:~$ kubectl delete -f ./filegatorgcp.yaml
ingress.networking.k8s.io "filegatorgcpingress" deleted

# Check it's now gone and not pending
builder@DESKTOP-QADGF36:~$ kubectl get cert | tail -n5
teampass-tls                      True    teampass-tls                      74d
filegator-tls                     True    filegator-tls                     9d
yaade-tls                         True    yaade-tls                         62d
glance-tls                        True    glance-tls                        61d
timetagger-tls                    True    timetagger-tls                    61d

I used Copilot to build out a proper ClusterIssuer

$ cat ./clusterissuer2.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: gcpleprod2
spec:
  acme:
    email: isaac.johnson@gmail.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: gcpleprod2
    solvers:
    - dns01:
        cloudDNS:
          project: myanthosproject2
          serviceAccountSecretRef:
            name: clouddns-dns01-solver-svc-acct
            key: gcpsakey.json
$ kubectl apply -f ./clusterissuer2.yaml
clusterissuer.cert-manager.io/gcpleprod2 created

I can see that is live

$ kubectl get clusterissuer
NAME               READY   AGE
letsencrypt-prod   True    143d
azuredns-tpkpw     True    141d
gcp-le-prod        True    29m
gcpleprod2         True    117s

let’s try that Filegator again, this time using the simplified GCP ClusterIssuer

builder@DESKTOP-QADGF36:~$ cat filegatorgcp.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    ingress.kubernetes.io/proxy-body-size: "0"
    ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: "0"
    nginx.org/proxy-connect-timeout: "3600"
    nginx.org/proxy-read-timeout: "3600"
    cert-manager.io/cluster-issuer: gcpleprod2
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: filegator-external-ip
  labels:
    app.kubernetes.io/instance: filegatorgcpingress
  name: filegatorgcpingress
spec:
  rules:
  - host: filegator.steeped.space
    http:
      paths:
      - backend:
          service:
            name: filegator-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - filegator.steeped.space
    secretName: filegatorgcp-tls
builder@DESKTOP-QADGF36:~$ kubectl apply -f ./filegatorgcp.yaml
ingress.networking.k8s.io/filegatorgcpingress created

I did a quick refresh on CloudDNS and was pleased to see a resolver record existed now

/content/images/2024/07/clouddns-12.png

And in Kubernetes, the cert was satisfied

$ kubectl get cert filegatorgcp-tls
NAME               READY   SECRET             AGE
filegatorgcp-tls   True    filegatorgcp-tls   100s

And that works!

/content/images/2024/07/clouddns-13.png

Certs

I want to test creating a Certificate. For instance, perhaps I have a legacy server that doesn’t use a ClusterIssuer and I really would like to get a working cert to use for testing.steeped.space

Since I am not assuming I have a GCP LB I can use for authorization, I’ll do DNS which prompts me to create the missing DNS authorization

/content/images/2024/07/clouddns-14.png

I can click Create to create the DNS Authorization

/content/images/2024/07/clouddns-15.png

I now see the DNS entry used by the GCP authority

/content/images/2024/07/clouddns-16.png

Just to be clear - Google needs some way to ensure you still own that domain if it is handing out a Certificate it signs. This means when it goes to create a new certificate, it has a DNS entry it can check (and/or change) to ensure you still own that DNS name.

I then click Create to start the process

/content/images/2024/07/clouddns-17.png

We can check the status and see the certificate is being provisioned

/content/images/2024/07/clouddns-18.png

I have no idea why they fail. I tried three times and all three got stuck in pending

/content/images/2024/07/clouddns-20.png

There really isn’t any detail for me to solve - it just sets to failed.

/content/images/2024/07/clouddns-19.png

Certbot

I can get certs using certbot on the command-line, provided I can use CloudDNS to set the challenge.

I’ll first add certbot to a linux host

isaac@isaac-MacBookAir:~$ sudo snap install certbot --classic
certbot 2.11.0 from Certbot Project (certbot-eff✓) installed
isaac@isaac-MacBookAir:~$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Now I can start the process with DNS Challenge

isaac@isaac-MacBookAir:~$ sudo certbot certonly --manual --preferred-challenges dns --debug-challenges -d \*.steeped.space -d steeped.space
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): isaac.johnson@gmail.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in
order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Account registered.
Requesting a certificate for *.steeped.space and steeped.space

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.steeped.space.

with the following value:

RWkylXBrNyn2p0nHtL2n_4zk3uaHqJYvjWD4IktjTcU

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

I’ll then create that record in CloudDNS

/content/images/2024/07/clouddns-21.png

I found trying to do both a wildcard and root did not work as the challenge record was the same for both and failed

Certbot failed to authenticate some domains (authenticator: manual). The Certificate Authority reported these problems:
  Domain: steeped.space
  Type:   unauthorized
  Detail: Incorrect TXT record "paZrBCD7F24RBWH9Bh3XTppdRyFHeGtjmRLDr4DCcU8" found at _acme-challenge.steeped.space

Hint: The Certificate Authority failed to verify the manually created DNS TXT records. Ensure that you created these in the correct location, or try waiting longer for DNS propagation on the next attempt.

When I kept it simple and just used the wildcard it worked

isaac@isaac-MacBookAir:~$ sudo certbot certonly --manual --preferred-challenges dns --debug-challenges -d \*.steeped.space
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for *.steeped.space

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.steeped.space.

with the following value:

7j3k8sZLJ0g7HyfdTyNp7jKCKU1bkGnmnJjKnnlIql8

Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.steeped.space.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Challenges loaded. Press continue to submit to CA.
Pass "-v" for more info about challenges.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/steeped.space/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/steeped.space/privkey.pem
This certificate expires on 2024-10-22.
These files will be updated when the certificate renews.

NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

We can now see the keys were created

/content/images/2024/07/clouddns-22.png

Since I would rather not leave this sitting on a utility host, I’ll now add to Classic Certificates

/content/images/2024/07/clouddns-23.png

I can now see the cert and as expected, it’s valid for 90 days

/content/images/2024/07/clouddns-24.png

I can also use this in creating an external Application Load Balancer

In the configuration section we see highlighted in red “Certificate”

/content/images/2024/07/clouddns-25.png

While it won’t let me pick my self-created, i can create a new certificate

/content/images/2024/07/clouddns-26.png

Then I’ll just use the details I have from my utility box here

/content/images/2024/07/clouddns-27.png

That now shows up as the certificate in the Frontend IP Setup pane

/content/images/2024/07/clouddns-28.png

In the wizard for backend, I created a bucket (and allowed public access).

/content/images/2024/07/clouddns-29.png

/content/images/2024/07/clouddns-30.png

Because I’m using simple routing, all DNS names I end up mapping to the external IP will route to the bucket

/content/images/2024/07/clouddns-31.png

We could get more fancy with the Advanced host and path

/content/images/2024/07/clouddns-32.png

But we’ll stick with simple.

Before I review and finalize, let’s add a basic index.html to the bucket so we have something to see

I’ll make a quick index.html we can use

builder@DESKTOP-QADGF36:~$ vi /mnt/c/Users/isaac/Documents/index.html
builder@DESKTOP-QADGF36:~$ cat /mnt/c/Users/isaac/Documents/index.html
<HTML>
        <HEAD>
                <TITLE>Hello Steeped</TITLE>
        </HEAD>
        <BODY>
                <h1>Hello there, Steeped.space</h1>
        </BODY>
</HTML>

In the bucket, I’ll click upload files

/content/images/2024/07/clouddns-33.png

It’s now uploaded to the bucket

/content/images/2024/07/clouddns-34.png

I can now finish the ALB create wizard

/content/images/2024/07/clouddns-35.png

I gave it a name and clicked create

/content/images/2024/07/clouddns-36.png

In the resulting details page, I can see my Public IP is 34.54.220.77

/content/images/2024/07/clouddns-37.png

I can now make a www A Record to serve that up

/content/images/2024/07/clouddns-38.png

This is also a good time to cleanup our acme challenges as it’s just kind of derpy to leave them sitting around

/content/images/2024/07/clouddns-39.png

The first issue I see is an access denied

/content/images/2024/07/clouddns-40.png

Reviewing the upload, I realized I neglected to set it to public

/content/images/2024/07/clouddns-41.png

We can follow the steps here to enable all users to access our bucket contents. Note: I would only do this for static websites.

/content/images/2024/07/clouddns-42.png

I can now see a warning it is “Public to internet” now on the bucket

/content/images/2024/07/clouddns-43.png

But now we can see our little HTTPS backed website

/content/images/2024/07/clouddns-44.png

I can also view the certificate to see the Validity matches our manual

We can also see the Wildcard that was created in our Certificates section of Certificate Manager

/content/images/2024/07/clouddns-46.png

Before we expire in October, I would need to come back to the frontend configuration and create a replacement certificate

/content/images/2024/07/clouddns-47.png

GCP Managed in ALB

Let’s do a similar activity but let GCP manage the cert. At the cert page, I’ll switch to Google-managed and use the domain “testing5.steeped.space”

/content/images/2024/07/clouddns-48.png

I can see it applied

/content/images/2024/07/clouddns-49.png

I used the same bucket then clicked Create at the finalize screen

/content/images/2024/07/clouddns-50.png

I can see the new IP is 34.49.221.120 in the details page

/content/images/2024/07/clouddns-51.png

For which I’ll create an A record

/content/images/2024/07/clouddns-52.png

This is a bit slower and I can see the site is not up

/content/images/2024/07/clouddns-53.png

Checking in to the details, I can see it’s still in the provisioning state

/content/images/2024/07/clouddns-54.png

However, within 10 minutes I saw a satisfied certificate

/content/images/2024/07/clouddns-55.png

And the website comes up

/content/images/2024/07/clouddns-56.png

Note: If you are testing in a browser window, it sometimes caches the cert and you need to try Incognito or Private windows.

Just as before, we can view the cert details and see its just as long as the free LE, but the signing authority is “Google Trust Services” now

/content/images/2024/07/clouddns-57.png

Summary

I think this is a pretty good start. We covered creating our first Hosted Zone in CloudDNS and using it. We started with CNames then moved on to A Records. We showed how we might use it to serve up an Open-Source app like FileGator in an on-prem cluster using a GCP-enabled ACME (LE) ClusterIssuer.

We tried to create some GCP managed certificates but were not successful doing that directly. However, using certbot on a utility Linux VM worked and we showed how to create a Classic Cert as well as use it in the creation of a new static website fronted by GCP CDN and a newer ALB. We also were successful in creating a GCP managed Cert using the ALB approach. The end result was to have two tiny websites hosted with certs tied to our new DNS, testing5 with GCP certs and www with LE certs.

I had planned to show some OpenToFu usage and CICD Automation, but we’ll save that for another day. Hopefully you got a good tour of how to use it.

Though, there is a part of me that thinks no one will read this directly and it just feeds the AI’s out there coming back as knowledge in Copilot or Gemini. Frankly I’m fine with that too - it’s digital cosmic karma I suppose.

GCP DNS CloudDNS Certificates

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes