Closed drivard closed 4 years ago
Here is a script which works for an Azure DNS. I was testing with a subdomain on a delegated zone and I had to do the reconstruction of the domain and hostname in the script.
The value of the variables are as sush:
{{DOMAIN}} == example.org
{{RECORD}} == _acme-challenge.www.dev
But they should have been:
{{DOMAIN}} == dev.example.org
{{RECORD}} == _acme-challenge.www
It would have supported the www.example.org
but not the www.dev.example.org
. So using bash
I created the full record and I used substring to create the values from the 3 available values.
One more issue I encountered is that the environment variables set in the settings page where not injected to the script. I added them directly.
The script requires to install the Azure CLI on the server. I was using an Ubuntu 18.04 LTS. https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-apt?view=azure-cli-latest
Here is my script.
#!/usr/bin/env bash
export AZURE_TENANT_ID=
export AZURE_SUBSCRIPTION_ID=
export AZURE_CLIENT_SECRET=
export AZURE_CLIENT_ID=
export AZURE_RESOURCE_GROUP=
# Do not forget to set this App Registration has a DNS Contributor in the IAM of the DNS Zone
while getopts d:v:r:c:a: option
do
case "${option}"
in
d) DOMAIN=${OPTARG};;
r) RECORD=${OPTARG};;
v) TXT_VALUE=${OPTARG};;
c) CLEAN_RECORD=${OPTARG};;
a) ADD_RECORD=${OPTARG};;
esac
done
echo "${DOMAIN}"
echo "${RECORD}"
echo "${TXT_VALUE}"
FULL_RECORD="${RECORD}.${DOMAIN}"
echo "${FULL_RECORD}"
TMP_RECORD=$(printf '%s\n' "${FULL_RECORD//_acme-challenge./}")
echo "${TMP_RECORD}"
FULL_DOMAIN=$(cut -d "." -f2- <<< "$TMP_RECORD")
echo "${FULL_DOMAIN}"
az login --service-principal --username "${AZURE_CLIENT_ID}" --tenant "${AZURE_TENANT_ID}" --password "${AZURE_CLIENT_SECRET}"
# ./update_dns.sh -d "dev.example.org" -r "_acme-challenge.www1.dev.example.org" -v 'G1Ip7qxPeMy1-061OFJ_illiY4bYNvKcAefVG4NDFRe' -a true
if [[ "${ADD_RECORD}" == "true" ]];
then
echo "Adding the record"
NEW_DOMAIN=".${FULL_DOMAIN}"
NEW_RECORD=$(printf '%s\n' "${FULL_RECORD//$NEW_DOMAIN/}")
az network dns record-set txt add-record -g "${AZURE_RESOURCE_GROUP}" -z "${FULL_DOMAIN}" -n "${NEW_RECORD}" -v "${TXT_VALUE}"
fi
# ./update_dns.sh -d "dev.example.org" -r "_acme-challenge.www1.dev.example.org" -v 'G1Ip7qxPeMy1-061OFJ_illiY4bYNvKcAefVG4NDFRe' -c true
if [[ "${CLEAN_RECORD}" == "true" ]];
then
echo "Deleting the record"
NEW_DOMAIN=".${FULL_DOMAIN}"
NEW_RECORD=$(printf '%s\n' "${FULL_RECORD//$NEW_DOMAIN/}")
az network dns record-set txt delete -g "${AZURE_RESOURCE_GROUP}" -z "${FULL_DOMAIN}" -n "${NEW_RECORD}" -y
fi
az logout
exit 0;
At the end I was able to get a completed certificate order.
Regards
Hi @drivard Thanks for using Certera and contributing!
Looks like you've got it working, which is great news. I'll post my solution here for posterity.
dnsc is this project here: https://github.com/certera-io/dns-client-project
One more issue I encountered is that the environment variables set in the settings page where not injected to the script. I added them directly.
You're correct. I found this issue as well and have been working on a fix. The problem is with the differences in how environment variables work between Windows and *Nix. In the meanwhile, I did exactly what you did, set the environment variable values in a script and invoke that script.
Here's what my current solution looks like:
Set DNS record script
/opt/dnsc/setdns
Set script arguments
{{Domain}} {{Record}} {{Value}}
Here's my setdns
bash script:
#!/bin/bash
AZURE_SUBSCRIPTION_ID=<VALUE> \
AZURE_TENANT_ID=<VALUE> \
AZURE_RESOURCE_GROUP=<VALUE> \
AZURE_CLIENT_ID=<VALUE> \
AZURE_CLIENT_SECRET=<VALUE> \
/opt/dnsc/dnsc --set -p azure -d $1 -n $2 -r TXT -v $3
The cleanup is very similar. Similarly set the environment variables and call dnsc like so.
/opt/dnsc/dnsc --delete -p azure -d $1 -n $2 -r TXT -v $3
Your script is certainly better in that you have all of your functionality in one place.
I wanted to make Certera flexible to be used however people wanted, with whatever DNS provider they had, in case it wasn't feasible to get their DNS provider into dnsc in a timely fashion.
As far as the script parameters, {{DOMAIN}}
and {{RECORD}}
. I'll work on those as well and likely create all possible permutations to make it as flexible as possible. I hadn't considered the scenario you described with the delegated subdomain. Thanks for the heads up!
It is true that my script could have been more generic, which it is not at the moment. I should have inspired myself from the nice acme.sh
client they have a lot of dns provider supported. https://github.com/acmesh-official/acme.sh/tree/master/dnsapi. For Azure they used the REST api exactly like you have done in your dns client with namecheap and azure.
One question that comes to my head, is how will you handle the multiple domains hosting company? By example I could be hosting example.org and mynewproduct.org at different DNS providers. But maybe I only want to host one certera instance. Lets say example.org
is on Azure and mynewproduct.org
is on Google Cloud.
I am thinking that actually to help you support the DNS-01 you would have to setup a per domain settings, instead of a global setting page.
Thanks for the answer. Best.
Thanks for sharing that link. I think I've seen that in the past. There are many such solutions out there and I didn't want to create yet another one like it. The idea behind dnsc was that it wouldn't be ACME specific. I'm glad though you've made it work using an external script and customizing it to fit your scenario and needs!
how will you handle the multiple domains hosting company?
That's a great question. I haven't put much thought into that. Originally, when I was thinking about adding DNS support, I thought about having many different configurations and then allowing you to set a default or specifying the DNS configuration at the certificate level. I may end up going that route eventually, but I wanted something quick and simple to get support for it out as soon as possible. For now, the scenario you described can still work using an external script and pushing the logic downstream, within the script. It's not ideal, but it should work in the meantime and keeps things flexible. Thanks for the suggestion on the per-domain level!
So, I've been thinking about the domain in your example: www.dev.example.org
I don't have enough information to know that the domain above is delegated to a subdomain or not. The pieces that I can return are based on the subject value that's used when creating a certificate via the UI and the parts available here: https://publicsuffix.nager.at/
Unfortunately, none of that will give you exactly what you want:
{{DOMAIN}} == dev.example.org
{{RECORD}} == _acme-challenge.www
I'll add FullRecord
and Subject
parameters and this is what will be available:
{{FullRecord}}
- The full record (e.g. _acme-challenge.www.dev.mysite.com)
{{Subject}}
- The domain for which the certificate is being obtained (e.g. www.dev.mysite.com)
{{Domain}}
- The registrable domain of the certificate being requested (e.g. mysite.com)
{{Record}}
- The TXT record name to create (e.g. _acme-challenge.www.dev)
{{Value}}
- The value to place in the TXT DNS record (this is the token that will be verified by ACME)
Only you know that dev.example.org has been delegated, so you must keep that context within the script. I'll keep mulling over this and we'll see if there's a better, cleaner way of handling this. Thanks for the heads up!
The {{ Subject }}
would make it much easier to script. Thanks.
Let me know once available I will test it.
I made the update in version 2.0.1-beta on Saturday. Available here: https://github.com/certera-io/certera/releases/tag/2.0.1-beta
@drivard going to close this for now. If there are any issues, let's re-open it.
Hi,
Is it possible to get more information on how to test DNS-01 challenge in the beta version 2.0.0-beta? I can help test the feature if you can tell me how to setup the script which is name in the image /opt/dnsc/dnsc.
By example what is the script expecting has an output for certera to catch the created certificates?
Regards