-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Azure blob storage provider (#89)
* Trigger tests * Trigger more tests * Add azure block storage provider * Use FQCN * Adjust file permissions * Link to service principal creation docs * Remove prerequisite not needed when using azbs provider * Add azure.azcollection dependency * Fix missing loop/when part --------- Co-authored-by: Andreas Hering <[email protected]>
- Loading branch information
Showing
3 changed files
with
154 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Variables for Azure blob storage http-challenge | ||
|
||
| Variable | Required | Default | Description | ||
|-----------------------|----------|-----------|------------ | ||
| acme_azbs_resource_group | yes | | Name of the Azure resource group to which the storage account has been allocated | ||
| acme_azbs_storage_account_name | yes | | Azure storage account name which should be used | ||
| acme_azbs_container_name | yes | | Azure container name which will be used/created in Azure storage account | ||
| acme_azbs_subscription_id | yes | | Azure subscription id | ||
| acme_azbs_client_id | yes | | Client ID of service principal/application | ||
| acme_azbs_secret | yes | | Value of secret of service principal/application (Note: not the ID) | ||
| acme_azbs_tenant_id | yes | | Tenant ID of service principal/application | ||
|
||
## Validation | ||
|
||
You have to create a service principal/application in the Azure Active Directory. | ||
This can be done via Frontend, Azure CLI or terraform. | ||
See https://learn.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals | ||
|
||
You also have to set a redirect rule in your proxy or webserver to allow the acme challenge bot to read the file, during the http-01 challenge to work. | ||
|
||
*Please note that the URL for the storage account and container needs to be adjusted to the name of your used account and container.* | ||
|
||
**HaProxy:** | ||
*works with version >= 1.6* | ||
|
||
```bash | ||
http-request redirect code 301 location https://your-storage-account-name.blob.core.windows.net[url,regsub(^/.well-known/acme-challenge,/my-containername,)] if { path_beg /.well-known/acme-challenge } | ||
``` | ||
(can be set in frontend or backend definition) | ||
**Apache:** | ||
```bash | ||
RewriteRule \.well-known/acme-challenge/(.*) https://your-storage-account-name.blob.core.windows.net/your-container-name/$1 | ||
``` | ||
**Nginx:** | ||
```bash | ||
rewrite \.well-known/acme-challenge/(.*) https://your-storage-account-name.blob.core.windows.net/your-container-name/$1 | ||
``` | ||
## Usage | ||
> you should think about encrypting all azure account infos | ||
```yaml | ||
- name: create the certificate for example.com | ||
hosts: localhost | ||
collections: | ||
- t_systems_mms.acme | ||
roles: | ||
- acme | ||
vars: | ||
acme_domain: | ||
certificate_name: "example.com" | ||
zone: "example.com" | ||
email_address: "[email protected]" | ||
subject_alt_name: | ||
- example.com | ||
- domain1.example.com | ||
- domain2.example.com | ||
acme_challenge_provider: "azbs" | ||
acme_use_live_directory: false | ||
acme_account_email: "[email protected]" | ||
acme_azbs_resource_group: "my-resource-group" | ||
acme_azbs_storage_account_name: "my-storage-account-name" | ||
acme_azbs_container_name: "my-container" | ||
acme_azbs_subscription_id: "0000-11111-2222-3333-444444" | ||
acme_azbs_client_id: "1234-21231-14152-1231" | ||
acme_azbs_secret: !vault | | ||
$ANSIBLE_VAULT;1.1;AES256 | ||
... | ||
acme_azbs_tenant_id: "2132184-3534543-54354-3543" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
--- | ||
- name: Validate challenge only if it is created or changed # noqa no-handler | ||
when: challenge is changed | ||
block: | ||
- name: Create challenge file with SAN domain for azure blob storage upload # noqa template-instead-of-copy | ||
ansible.builtin.copy: | ||
dest: "acme-challenge.{{ item }}" | ||
content: "{{ challenge['challenge_data'][item]['http-01']['resource_value'] }}" | ||
mode: 0640 | ||
loop: "{{ acme_domain.subject_alt_name }}" | ||
when: | ||
- acme_domain.subject_alt_name is defined | ||
# only runs if the challenge is run the first time, because then there is challenge_data | ||
- challenge['challenge_data'][item] is defined | ||
|
||
- name: Create storage container and upload challenge file to it | ||
azure.azcollection.azure_rm_storageblob: | ||
resource_group: "{{ acme_azbs_resource_group }}" | ||
storage_account_name: "{{ acme_azbs_storage_account_name }}" | ||
public_access: "blob" | ||
container: "{{ acme_azbs_container_name }}" | ||
blob: "{{ challenge['challenge_data'][item]['http-01']['resource'] }}" | ||
src: "acme-challenge.{{ item }}" | ||
content_type: 'text/plain' # _type or _encoding have to be set | ||
subscription_id: "{{ acme_azbs_subscription_id }}" | ||
client_id: "{{ acme_azbs_client_id }}" | ||
secret: "{{ acme_azbs_secret }}" | ||
tenant: "{{ acme_azbs_tenant_id }}" | ||
loop: "{{ acme_domain.subject_alt_name }}" | ||
when: | ||
- acme_domain.subject_alt_name is defined | ||
# only runs if the challenge is run the first time, because then there is challenge_data | ||
- challenge['challenge_data'][item] is defined | ||
|
||
# validate certficate | ||
- name: Let the challenge be validated and retrieve the cert and intermediate certificate | ||
community.crypto.acme_certificate: | ||
account_key_src: "{{ acme_account_key_path }}" | ||
account_email: "{{ acme_account_email }}" | ||
csr: "{{ acme_csr_path }}" | ||
cert: "{{ acme_cert_path }}" | ||
fullchain: "{{ acme_fullchain_path }}" | ||
chain: "{{ acme_intermediate_path }}" | ||
challenge: http-01 | ||
force: "{{ acme_force_renewal | default(false) }}" | ||
acme_directory: "{{ acme_directory }}" | ||
acme_version: 2 | ||
terms_agreed: true | ||
remaining_days: "{{ acme_remaining_days }}" | ||
data: "{{ challenge }}" | ||
|
||
- name: Remove challenge file for SAN domain from azure blob storage container | ||
azure.azcollection.azure_rm_storageblob: | ||
resource_group: "{{ acme_azbs_resource_group }}" | ||
storage_account_name: "{{ acme_azbs_storage_account_name }}" | ||
container: "{{ acme_azbs_container_name }}" | ||
blob: "{{ challenge['challenge_data'][item]['http-01']['resource'] }}" | ||
state: absent | ||
subscription_id: "{{ acme_azbs_subscription_id }}" | ||
client_id: "{{ acme_azbs_client_id }}" | ||
secret: "{{ acme_azbs_secret }}" | ||
tenant: "{{ acme_azbs_tenant_id }}" | ||
loop: "{{ acme_domain.subject_alt_name }}" | ||
when: | ||
- acme_domain.subject_alt_name is defined | ||
# only runs if the challenge is run the first time, because then there is challenge_data | ||
- challenge['challenge_data'][item] is defined | ||
|
||
- name: Remove challenge file for SAN domain from fs | ||
ansible.builtin.file: | ||
dest: "acme-challenge.{{ item }}" | ||
state: absent | ||
loop: "{{ acme_domain.subject_alt_name }}" | ||
when: | ||
- acme_domain.subject_alt_name is defined | ||
# only runs if the challenge is run the first time, because then there is challenge_data | ||
- challenge['challenge_data'][item] is defined |