Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions roles/caddy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# caddy

Install Caddy proxy packages.

This role installs the Caddy web server and reverse proxy, configuring it for self-signed TLS by default, or optionally with external CA certificates, or via Let's Encrypt. It sets up an initial global configuration with an import directory, configures a WWW root directory, and can retrieve Caddy's generated self-signed CA certificate to the target host if applicable.

The role will:
- Install the Caddy proxy service.
- Configure an initial global configuration for Caddy, including an import directory for modular configuration.
- Set up a designated WWW root directory for serving static content.
- Manage TLS certificates based on the `caddy_self_signed`, `caddy_ca_pem`, and `caddy_ca_key` parameters:
- If `caddy_self_signed` is `true` (default) and `caddy_ca_pem` is not defined, Caddy will generate its own self-signed root CA and issue certificates. The Caddy self-signed CA certificate will be retrieved to the target host.
- If `caddy_self_signed` is `false`, Caddy will attempt to use Let's Encrypt's ACME service to obtain trusted certificates.
- If `caddy_ca_pem` and `caddy_ca_key` are provided, Caddy will use these external CA credentials for TLS.
- Ensure the Caddy service is running and enabled.

# Requirements

- A DNS A record resolving `caddy_domain` to the target host's IP address is recommended for proper certificate validation.
- Ports 80 and 443 must be open on the target host and accessible for inbound connections.
- For Let's Encrypt (when `caddy_self_signed` is `false`), ports 80/443 must be publicly accessible and the domain must be resolvable via public DNS.

# Dependencies

None.

# Parameters

| Variable | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `caddy_domain` | `str` | `True` | | Domain name for the Caddy reverse proxy (e.g., `proxy.example.com`). |
| `caddy_www_root` | `path` | `False` | `/var/www_root` | Directory where static WWW service content will be served from. |
| `caddy_self_signed` | `bool` | `False` | `true` | Flag enabling Caddy to issue self-signed TLS certificates. If `false`, Caddy defaults to using the Let's Encrypt ACME service. If `true` and `caddy_ca_pem` is not defined, Caddy generates its own root CA. |
| `caddy_ca_pem` | `path` | `False` | | Path to an external CA certificate file (PEM format) to be used by Caddy for TLS. |
| `caddy_ca_key` | `path` | `False` | | Path to the private key for the external CA (`caddy_ca_pem`). This parameter is required if `caddy_ca_pem` is defined. |

# Example Playbook

```yaml
- hosts: proxy_servers
tasks:
- name: Install Caddy with default self-signed TLS
ansible.builtin.import_role:
name: cloudera.exe.caddy
vars:
caddy_domain: "dev-proxy.example.com"
# caddy_self_signed will default to true
# caddy_www_root will default to /var/www_root

- name: Install Caddy using Let's Encrypt
ansible.builtin.import_role:
name: cloudera.exe.caddy
vars:
caddy_domain: "prod-proxy.example.com"
caddy_self_signed: false # Enable Let's Encrypt ACME

- name: Install Caddy with external CA certificates
ansible.builtin.import_role:
name: cloudera.exe.caddy
vars:
caddy_domain: "internal-proxy.example.com"
caddy_self_signed: true # Still technically self-signed, but by an external CA
caddy_ca_pem: "/path/to/my_org_ca.pem"
caddy_ca_key: "/path/to/my_org_ca.key"
caddy_www_root: "/srv/my_app_html"
```

# License

```
Copyright 2025 Cloudera, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
21 changes: 21 additions & 0 deletions roles/caddy/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2025 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---

caddy_self_signed: true
caddy_domain: "{{ undef(hint='Please provide the default domain for the Caddy service') }}"
caddy_www_root: /var/www_root
# caddy_ca_pem: # path
# caddy_ca_key: # path
20 changes: 20 additions & 0 deletions roles/caddy/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2025 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---

- name: Reload Caddy
ansible.builtin.service:
name: "{{ caddy_service }}"
state: reloaded
54 changes: 54 additions & 0 deletions roles/caddy/meta/argument_specs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2025 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

argument_specs:
main:
short_description: Install Caddy proxy packages.
description:
- Install Caddy proxy service using self-signed TLS.
- Configure an initial global configuration with an import directory.
- Configure a WWW root directory.
- Optionally, provision the Caddy with external CA certificates.
- If Caddy self-signing is enabled and an external CA certificate is not defined, retrieve the Caddy self-signed CA certificate to the target host.
author: Cloudera Labs
version_added: "5.0.0"
options:
caddy_domain:
description: Domain name for the Caddy reverse proxy.
type: str
required: true
caddy_www_root:
description: Directory of the static WWW service.
type: path
required: false
default: /var/www_root
caddy_self_signed:
description:
- Flag enabling Caddy to issue self-signed TLS certificates.
- If not defined, Caddy will default to the Let's Encrypt ACME service.
- If defined and O(caddy_ca_pem) is not defined, Caddy will generate a root CA certificate for self-signed TLS.
type: bool
required: false
default: true
caddy_ca_pem:
description:
- External CA for the Caddy TLS service.
type: path
required: false
caddy_ca_key:
description:
- External CA key for the Caddy TLS service.
- Required if O(caddy_ca_pem) is defined.
type: path
required: false
23 changes: 23 additions & 0 deletions roles/caddy/tasks/RedHat-pre.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2025 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---

- name: Enable Fedora COPR
ansible.builtin.package:
name: "dnf-command(copr)"

- name: Enable Caddy project in COPR
ansible.builtin.command: "dnf copr enable -y @caddy/caddy"
changed_when: false
17 changes: 17 additions & 0 deletions roles/caddy/tasks/default-pre.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2025 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---

# no op
142 changes: 142 additions & 0 deletions roles/caddy/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Copyright 2025 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---

- name: Gather host distribution details
ansible.builtin.setup:
gather_subset: distribution

- name: Load distribution variables
ansible.builtin.include_vars: "{{ item }}"
with_first_found:
- "{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}.yml"
- "{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}.yml"
- "{{ ansible_facts['distribution'] }}.yml"
- "{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_version'] }}.yml"
- "{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_major_version'] }}.yml"
- "{{ ansible_facts['os_family'] }}.yml"
- "default.yml"

- name: Run distribution pre-tasks
ansible.builtin.include_tasks: "{{ item }}"
with_first_found:
- "{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}-pre.yml"
- "{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}-pre.yml"
- "{{ ansible_facts['distribution'] }}-pre.yml"
- "{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_version'] }}-pre.yml"
- "{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_major_version'] }}-pre.yml"
- "{{ ansible_facts['os_family'] }}-pre.yml"
- "default-pre.yml"

- name: Install Caddy binaries
ansible.builtin.package:
name: "{{ caddy_package }}"
loop: "{{ caddy_packages }}"
loop_control:
loop_var: caddy_package

- name: Set up Caddyfile imports directory
ansible.builtin.file:
path: "/etc/caddy/Caddyfile.d"
mode: "0755"
state: directory

- name: Set up Caddy WWW root directory
ansible.builtin.file:
path: "{{ caddy_www_root }}"
mode: "0755"
state: directory

- name: Set up default index page for Caddy WWW root
ansible.builtin.file:
path: "{{ [caddy_www_root, 'index.html'] | path_join }}"
mode: "0755"
state: touch

- name: Provision external CA certificates
when: caddy_ca_pem is defined
block:
- name: Set up external PKI directory
ansible.builtin.file:
path: "{{ caddy_external_pki_dir }}"
state: directory
owner: caddy
group: caddy
mode: "0700"

- name: Install external CA certificates
ansible.builtin.copy:
src: "{{ __ca_file }}"
dest: "{{ [caddy_external_pki_dir, __ca_file | basename] | path_join }}"
owner: caddy
group: caddy
mode: "0600"
loop:
- "{{ caddy_ca_pem }}"
- "{{ caddy_ca_key }}"
loop_control:
loop_var: __ca_file

# - name: Set up Caddyfile ACME issuers
# ansible.builtin.blockinfile:
# backup: no
# path: "/etc/caddy/Caddyfile"
# insertbefore: BOF
# # append_newline: yes <=2.16
# block: |
# {
# cert_issuer acme
# cert_issuer acme {
# dir https://acme.zerossl.com/v2/DV90
# email [email protected]
# }
# }

# - name: Set up Caddy CA
# when: caddy_self_signed
# ansible.builtin.blockinfile:
# backup: no
# path: "/etc/caddy/Caddyfile"
# insertbefore: BOF
# # append_newline: yes <=2.16
# block: "{{ lookup('template', 'internal_ca.json.j2') }}"

# - name: Set up Caddyfile imports directive
# ansible.builtin.blockinfile:
# backup: no
# path: "/etc/caddy/Caddyfile"
# insertafter: EOF
# # prepend_newline: yes <=2.16
# block: |
# import Caddyfile.d/*.caddyfile

- name: Provision Caddy configuration
ansible.builtin.template:
src: Caddyfile.j2
dest: /etc/caddy/Caddyfile
mode: "0755"

- name: Enable and run the Caddy service
ansible.builtin.service:
name: "{{ caddy_service }}"
enabled: true
state: started

- name: Retrieve the Caddy self-signed CA certificate
when: caddy_self_signed and caddy_ca_pem is undefined
ansible.builtin.fetch:
src: /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt
dest: "{{ [playbook_dir, name_prefix + '-caddy-root.crt'] | path_join }}"
flat: true
26 changes: 26 additions & 0 deletions roles/caddy/templates/Caddyfile.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Managed by cloudera.exe.caddy

{% if caddy_self_signed %}
{
local_certs
{% if caddy_ca_pem is defined %}
pki {
ca {
root {
cert {{ [caddy_external_pki_dir, caddy_ca_pem | basename] | path_join }}
key {{ [caddy_external_pki_dir, caddy_ca_key | basename] | path_join }}
}
}
}
{% endif %}
}
{% endif %}

# Caddy default WWW root
{{ caddy_domain }} {
root * {{ caddy_www_root }}
file_server
}

# Caddy routes
import Caddyfile.d/*.caddyfile
Loading
Loading