Skip to content

Commit

Permalink
Add Azure blob storage provider (#89)
Browse files Browse the repository at this point in the history
* 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
avalor1 and Andreas Hering authored Mar 23, 2023
1 parent f854d03 commit 957eb9a
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 0 deletions.
76 changes: 76 additions & 0 deletions docs/http-challenge/azbs.md
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"
```
1 change: 1 addition & 0 deletions galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies:
community.crypto: '>=1.0.0'
openstack.cloud: '>=1.2.1'
amazon.aws: '>=5.0.0'
azure.azcollection: '>=1.14.0'
repository: 'https://github.com/T-Systems-MMS/ansible-collection-acme'
documentation: 'https://github.com/T-Systems-MMS/ansible-collection-acme'
homepage: 'https://github.com/T-Systems-MMS/ansible-collection-acme'
Expand Down
77 changes: 77 additions & 0 deletions roles/acme/tasks/challenge/http-01/azbs.yml
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

0 comments on commit 957eb9a

Please sign in to comment.