# Introduction
This repository aims to leverage the automatic renewal of SSL certficates within Azure Cloud in a secure manner.
A wrapper library is provided to automatically renew certifactes based on the [ACME DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#:~:text=all%20of%20them.-,DNS%2D01%20challenge,-This%20challenge%20asks) by using [certbot](https://certbot.eff.org/).
The library supports the usage of best practices for securely handling certificates by:
- using certbot
- removing the need of a file system for storing certificates
- Azure Key Vault for central and only storage of secrets and certificates
- enabling easy and flexible automation
# Installing acme-dns-azure
acme-dns-azure is available on PyPi:
```bash
python -m pip install acme-dns-azure
```
For usage exampless please refer to [examples](examples)
## Scope
Based on the provided configuration and trigger, the wrapper library supports following flow.
![architecture](https://github.com/ZEISS/acme-dns-azure/blob/main/docs/architecture_concept.png?raw=true)
1. Receive certificates, receive EAB & ACME credentials (if configured), receive ACME account information (if already present) from KeyVault. Resolve DNS and setup certbot related configuration.
2. Certbot: Init renewal process to certificate authority
3. Certbot: DNS Challenge - create TXT record
4. Certbot: Renew certificates
5. Certbot: DNS Challenge - delete TXT record
6. Upload renewed certificates, create/update ACME account information as secret within KeyVault.
Note: When using [DNS delegation](https://docs.certbot-dns-azure.co.uk/en/latest/#:~:text=management.microsoftazure.de/-,DNS%20delegation,%C2%B6,-DNS%20delegation%2C%20also) step _3._ and _5._ differ as the TXT record won´t be deleted.
### Features
The library handles following use cases:
- Create new certificates
- Update domain references in existing certificates
- Renew existing certificates
Auth is possible by using:
- Service Principal
- User Assigned Identity
### Integration
The library can be used by:
- running as script
- Python package within your app
Within [examples](examples) you can find example implementations for running the python package:
- Azure function
- Container
![usage](https://github.com/ZEISS/acme-dns-azure/blob/main/docs/wrapper_usage.png?raw=true)
# Contribute
Fork, then clone the repo:
```bash
git clone https://github.com/ZEISS/acme-dns-azure
```
Install Poetry if you not have it already:
```bash
curl -sSL https://install.python-poetry.org | python3 -
```
Configure the virtual environment with full example support and activate it:
## Install dependencies
```bash
poetry install --all-extras
source .venv/bin/activate
```
## Lint
```bash
poetry run black .
```
## Run unit tests
```bash
poetry run coverage run
poetry run coverage report
```
## Run integration tests
See [How to run integration tests](tests/integration/README.md)
## Release
For releasing a new version, create a PR with one of following labels:
- minor
- major
- patch
- prepatch
- preminor
- premajor
- prerelease
# Usage
## Config
The config is written in [YAML format](http://en.wikipedia.org/wiki/YAML), defined by the scheme described below.
Brackets indicate that a parameter is optional.
For non-list parameters the value is set to the specified default.
Generic placeholders are defined as follows:
- `<boolean>`: a boolean that can take the values `true` or `false`
- `<int>`: a regular integer
- `<string>`: a regular string
- `<secret>`: a regular string that is a secret, such as a password
The other placeholders are specified separately.
See [examples](examples/README.md) for configuration examples.
```yml
[managed_identity_id: <string>]
[sp_client_id: <string>]
[sp_client_secret: <secret>]
[azure_environment: <string> | default = "AzurePublicCloud"]
# Flag if existing certificates containing multiple domains should be renewed and updated based on the definition of the config file. If not set, mismatching certificates will be skipped.
[update_cert_domains: <boolean> | default = False]
# key vault uri for renewal of certifcate
key_vault_id : <string>
# ACME Certificate Authority
server : <string>
# Secret name within key vault for storing ACME Certificate authority account information
[keyvault_account_secret_name: <regex> | default "acme-account-$(network location of server)"]
# when server=https://example.com/something, then keyvault_account_secret_name="acme-account-example-com"
# config file content for certbot client
[certbot.ini : <string> | default = ""]
#
```
NOTE: Either **managed_identity_id** or **sp_client_id** and **sp_client_secret** must be specified.
NOTE: **certbot.ini** represents the [CERTBOT configuration file](https://eff-certbot.readthedocs.io/en/latest/using.html#configuration-file) and will be passed into certbot by the _acme_dns_azure_ library as defined. Misconfiguration will lead to failures of certbot and therefore of the renewal process.
Following values will be added to the configurataion file by the _acme_dns_azure_ library per default:
```yml
preferred-challenges: dns
authenticator: dns-azure
agree-tos: true
```
### `[<eab>]`
```yml
# External account binding configuration for ACME, with key ID and base64encoded HMAC key
[enabled: <boolean> | default = false]
[kid_secret_name : <string> | default="acme-eab-kid"]
[hmac_key_secret_name : <secret> default="acme-eab-hmac-key"]
```
```yml
certificates:
- <certificate>
```
### `<certificate>`
```yml
# Certbot certficate name. The name will also be used for Azure keyvault certificate name.
name: <string>
# Azure dns zone resource ID used for ACME DNS01 challenge
dns_zone_resource_id: <string>
# renewal in days before expiry for certificate to be renewed
[renew_before_expiry: <int>]
domains:
- <domain>
```
### `<domain>`
```yml
# domain name this certificate is valid for. Wildcard supported.
name: <string>
# Azure dns zone resource ID used for ACME DNS01 challenge
[dns_zone_resource_id: <string>]
```
## Manual running the library
For running the module as script 'sp_client_id' and 'sp_client_secret' are required. 'managed_identity_id' is not supported.
```bash
# from config file
python acme_dns_azure/client.py --config-file-path $CONFIG_File_PATH
# from env
python acme_dns_azure/client.py --config-env-var $ENV_VAR_NAME_CONTAINING_CONFIG
```
## Permission Handling
Best follow [security recommendations from Azure](https://docs.certbot-dns-azure.co.uk/en/latest/#:~:text=Example%3A%20Delegation%20%2B%20more,%C2%B6).
When working with shared DNS Zones, one can work with DNS delegation with limited permissions:
Example:
| Record | Name | Value | Permission |
| ------ | ---------------------------- | ------------------------- | -------------------- |
| TXT | \_acme-dedicated | - | DNS Zone Contributor |
| CNAME | \_acme-challenge.mysubdomain | \_acme-dedicated.mydomain | None |
The CNAME and TXT record must be created upfront to enable users to use certbot. The permissions are required on the identity triggering certbot.
With this setup, a DNS Zone owner can limit permissions and enable Users to Create/Renew certificates for their subdomain and ensuring that users cannot aquire certificates for other domains or interfer with existsing records.
Raw data
{
"_id": null,
"home_page": "https://github.com/ZEISS/acme-dns-azure",
"name": "acme-dns-azure",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "acme, dns, azure, certbot, letsencrypt",
"author": "ZEISS Digital Innovation Partners",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/a9/11/d2d6d9450532aa902717c2079a49a25f58c7a81e9d6ff31d21d2d1427ae6/acme_dns_azure-0.3.0.tar.gz",
"platform": null,
"description": "# Introduction\n\nThis repository aims to leverage the automatic renewal of SSL certficates within Azure Cloud in a secure manner.\n\nA wrapper library is provided to automatically renew certifactes based on the [ACME DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#:~:text=all%20of%20them.-,DNS%2D01%20challenge,-This%20challenge%20asks) by using [certbot](https://certbot.eff.org/).\n\nThe library supports the usage of best practices for securely handling certificates by:\n\n- using certbot\n- removing the need of a file system for storing certificates\n- Azure Key Vault for central and only storage of secrets and certificates\n- enabling easy and flexible automation\n\n# Installing acme-dns-azure\n\nacme-dns-azure is available on PyPi:\n\n```bash\npython -m pip install acme-dns-azure\n```\n\nFor usage exampless please refer to [examples](examples)\n\n## Scope\n\nBased on the provided configuration and trigger, the wrapper library supports following flow.\n\n![architecture](https://github.com/ZEISS/acme-dns-azure/blob/main/docs/architecture_concept.png?raw=true)\n\n1. Receive certificates, receive EAB & ACME credentials (if configured), receive ACME account information (if already present) from KeyVault. Resolve DNS and setup certbot related configuration.\n2. Certbot: Init renewal process to certificate authority\n3. Certbot: DNS Challenge - create TXT record\n4. Certbot: Renew certificates\n5. Certbot: DNS Challenge - delete TXT record\n6. Upload renewed certificates, create/update ACME account information as secret within KeyVault.\n\nNote: When using [DNS delegation](https://docs.certbot-dns-azure.co.uk/en/latest/#:~:text=management.microsoftazure.de/-,DNS%20delegation,%C2%B6,-DNS%20delegation%2C%20also) step _3._ and _5._ differ as the TXT record won\u00b4t be deleted.\n\n### Features\n\nThe library handles following use cases:\n\n- Create new certificates\n- Update domain references in existing certificates\n- Renew existing certificates\n\nAuth is possible by using:\n\n- Service Principal\n- User Assigned Identity\n\n### Integration\n\nThe library can be used by:\n\n- running as script\n- Python package within your app\n\nWithin [examples](examples) you can find example implementations for running the python package:\n\n- Azure function\n- Container\n\n![usage](https://github.com/ZEISS/acme-dns-azure/blob/main/docs/wrapper_usage.png?raw=true)\n\n# Contribute\n\nFork, then clone the repo:\n\n```bash\ngit clone https://github.com/ZEISS/acme-dns-azure\n```\n\nInstall Poetry if you not have it already:\n\n```bash\ncurl -sSL https://install.python-poetry.org | python3 -\n```\n\nConfigure the virtual environment with full example support and activate it:\n\n## Install dependencies\n\n```bash\npoetry install --all-extras\nsource .venv/bin/activate\n```\n\n## Lint\n\n```bash\npoetry run black .\n```\n\n## Run unit tests\n\n```bash\npoetry run coverage run\npoetry run coverage report\n```\n\n## Run integration tests\n\nSee [How to run integration tests](tests/integration/README.md)\n\n## Release\n\nFor releasing a new version, create a PR with one of following labels:\n\n- minor\n- major\n- patch\n- prepatch\n- preminor\n- premajor\n- prerelease\n\n# Usage\n\n## Config\n\nThe config is written in [YAML format](http://en.wikipedia.org/wiki/YAML), defined by the scheme described below.\nBrackets indicate that a parameter is optional.\nFor non-list parameters the value is set to the specified default.\n\nGeneric placeholders are defined as follows:\n\n- `<boolean>`: a boolean that can take the values `true` or `false`\n- `<int>`: a regular integer\n- `<string>`: a regular string\n- `<secret>`: a regular string that is a secret, such as a password\n\nThe other placeholders are specified separately.\n\nSee [examples](examples/README.md) for configuration examples.\n\n```yml\n[managed_identity_id: <string>]\n\n[sp_client_id: <string>]\n[sp_client_secret: <secret>]\n\n[azure_environment: <string> | default = \"AzurePublicCloud\"]\n\n# Flag if existing certificates containing multiple domains should be renewed and updated based on the definition of the config file. If not set, mismatching certificates will be skipped.\n[update_cert_domains: <boolean> | default = False]\n\n# key vault uri for renewal of certifcate\nkey_vault_id : <string>\n\n# ACME Certificate Authority\nserver : <string>\n\n# Secret name within key vault for storing ACME Certificate authority account information\n[keyvault_account_secret_name: <regex> | default \"acme-account-$(network location of server)\"]\n# when server=https://example.com/something, then keyvault_account_secret_name=\"acme-account-example-com\"\n\n# config file content for certbot client\n[certbot.ini : <string> | default = \"\"]\n#\n```\n\nNOTE: Either **managed_identity_id** or **sp_client_id** and **sp_client_secret** must be specified.\n\nNOTE: **certbot.ini** represents the [CERTBOT configuration file](https://eff-certbot.readthedocs.io/en/latest/using.html#configuration-file) and will be passed into certbot by the _acme_dns_azure_ library as defined. Misconfiguration will lead to failures of certbot and therefore of the renewal process.\n\nFollowing values will be added to the configurataion file by the _acme_dns_azure_ library per default:\n\n```yml\npreferred-challenges: dns\nauthenticator: dns-azure\nagree-tos: true\n```\n\n### `[<eab>]`\n\n```yml\n\n # External account binding configuration for ACME, with key ID and base64encoded HMAC key\n [enabled: <boolean> | default = false]\n [kid_secret_name : <string> | default=\"acme-eab-kid\"]\n [hmac_key_secret_name : <secret> default=\"acme-eab-hmac-key\"]\n```\n\n```yml\ncertificates:\n - <certificate>\n```\n\n### `<certificate>`\n\n```yml\n# Certbot certficate name. The name will also be used for Azure keyvault certificate name.\nname: <string>\n# Azure dns zone resource ID used for ACME DNS01 challenge\ndns_zone_resource_id: <string>\n# renewal in days before expiry for certificate to be renewed\n[renew_before_expiry: <int>]\ndomains:\n - <domain>\n```\n\n### `<domain>`\n\n```yml\n# domain name this certificate is valid for. Wildcard supported.\nname: <string>\n# Azure dns zone resource ID used for ACME DNS01 challenge\n[dns_zone_resource_id: <string>]\n```\n\n## Manual running the library\n\nFor running the module as script 'sp_client_id' and 'sp_client_secret' are required. 'managed_identity_id' is not supported.\n\n```bash\n# from config file\npython acme_dns_azure/client.py --config-file-path $CONFIG_File_PATH\n# from env\npython acme_dns_azure/client.py --config-env-var $ENV_VAR_NAME_CONTAINING_CONFIG\n```\n\n## Permission Handling\n\nBest follow [security recommendations from Azure](https://docs.certbot-dns-azure.co.uk/en/latest/#:~:text=Example%3A%20Delegation%20%2B%20more,%C2%B6).\n\nWhen working with shared DNS Zones, one can work with DNS delegation with limited permissions:\n\nExample:\n\n| Record | Name | Value | Permission |\n| ------ | ---------------------------- | ------------------------- | -------------------- |\n| TXT | \\_acme-dedicated | - | DNS Zone Contributor |\n| CNAME | \\_acme-challenge.mysubdomain | \\_acme-dedicated.mydomain | None |\n\nThe CNAME and TXT record must be created upfront to enable users to use certbot. The permissions are required on the identity triggering certbot.\n\nWith this setup, a DNS Zone owner can limit permissions and enable Users to Create/Renew certificates for their subdomain and ensuring that users cannot aquire certificates for other domains or interfer with existsing records.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "ACME client setup based on Certbot for dns-01 challenges via Azure Cloud services",
"version": "0.3.0",
"project_urls": {
"Documentation": "https://zeiss.github.io/acme-dns-azure/",
"Homepage": "https://github.com/ZEISS/acme-dns-azure",
"PyPI": "https://pypi.org/project/acme-dns-azure/",
"Repository": "https://github.com/ZEISS/acme-dns-azure"
},
"split_keywords": [
"acme",
" dns",
" azure",
" certbot",
" letsencrypt"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "75e7cb44c33214e0ec81b3a4888f6ad9fa5f242392659f1652aabbd1233f750f",
"md5": "7fc72d6cba003ace1ce317bdbaa7609f",
"sha256": "ad70ca9470293091bbfc02de3c0ae798eeb7ce7b9101a4eda76ab4fc0b2eb1bd"
},
"downloads": -1,
"filename": "acme_dns_azure-0.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "7fc72d6cba003ace1ce317bdbaa7609f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 18767,
"upload_time": "2024-04-12T10:16:44",
"upload_time_iso_8601": "2024-04-12T10:16:44.908554Z",
"url": "https://files.pythonhosted.org/packages/75/e7/cb44c33214e0ec81b3a4888f6ad9fa5f242392659f1652aabbd1233f750f/acme_dns_azure-0.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a911d2d6d9450532aa902717c2079a49a25f58c7a81e9d6ff31d21d2d1427ae6",
"md5": "23eb4488a4432a3fa5cef9ce37dd2d0b",
"sha256": "83a38cefdeffc075f2d6d37fc13ce3c1544fc71f19611a74e7862546ceefdf5f"
},
"downloads": -1,
"filename": "acme_dns_azure-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "23eb4488a4432a3fa5cef9ce37dd2d0b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 17762,
"upload_time": "2024-04-12T10:16:48",
"upload_time_iso_8601": "2024-04-12T10:16:48.543652Z",
"url": "https://files.pythonhosted.org/packages/a9/11/d2d6d9450532aa902717c2079a49a25f58c7a81e9d6ff31d21d2d1427ae6/acme_dns_azure-0.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-04-12 10:16:48",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ZEISS",
"github_project": "acme-dns-azure",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "acme-dns-azure"
}