Skip to content
15 changes: 15 additions & 0 deletions changelogs/fragments/icinga2_api_deprecations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
deprecated_features:
- The use of :code:`ca_host` inside the Icinga 2 API feature is deprecated.
Use :code:`parent_host` instead.
- The use of :code:`ca_host_port` inside the Icinga 2 API feature is deprecated.
Use :code:`parent_port` instead.
- |
The use of :code:`ticket_salt` inside the Icinga 2 API feature is deprecated.
Use :code:`ticket` instead.
Such a ticket can be created from the actual secret :code:`TicketSalt` passing it to the :code:`netways.icinga.icinga2_ticket` filter.
Example: :code:`ticket: "{{ <common_name> | netways.icinga.icinga2_ticket(ticketsalt='<secret_ticket_salt>') }}"`.

minor_changes:
- Add new module :code:`netways.icinga.icinga2_api` to handle setting up the node as a master / agent instance.
The module handles the constant :code:`NodeName` as well as writing :code:`zones.conf`,
and makes certificate requests to the parent node (if :code:`mode=agent`).
6 changes: 3 additions & 3 deletions doc/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ This is an example on how to install an Icinga 2 server/master instance.
- zones.d/main/services
icinga2_features:
- name: api # Enable Feature API
ca_host: none # No CA host, CA will be created locally
parent_host: none # No parent host, CA will be created locally
endpoints:
- name: NodeName
zones:
Expand Down Expand Up @@ -169,7 +169,7 @@ This is an example on how to install an Icinga 2 agent instance.
icinga2_purge_features: yes # Ansible will manage all features
icinga2_features:
- name: api # Enable Feature API
ca_host: master.localdomain
parent_host: master.localdomain
# Trusted Cert and Ticket will be gathered from this host.
# Ticket will be "delegated" if the FQDN is not in your Ansible environment
# use the variable icinga2_delegate_host in addition.
Expand Down Expand Up @@ -237,7 +237,7 @@ This is a example on how to install Icinga 2 server with Icinga Web 2 and Icinga
- name: checker
- name: mainlog
- name: api
ca_host: none
parent_host: none
endpoints:
- name: "{{ ansible_facts['fqdn'] }}"
zones:
Expand Down
2 changes: 1 addition & 1 deletion doc/role-icinga2/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ icinga2_features:
host: localhost
port: 3000
- name: api
ca_host: none
parent_host: none
force_newcert: false
endpoints:
- name: NodeName
Expand Down
105 changes: 72 additions & 33 deletions doc/role-icinga2/features/feature-api.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Feature API

The API Feature configures the API. The feature will manage
The API feature configures the API. The feature will manage
certificate, private key and CA certificate or will create
a certificate signing requests. It also manages the **zones.conf**.

Expand All @@ -16,7 +16,7 @@ Example how to install an Agent:
icinga2_features:
- name: api
force_newcert: false
ca_host: icinga-server.localdomain
parent_host: <ParentNodeFQDN/IPAddress>
endpoints:
- name: NodeName
- name: <ParentNodeName>
Expand All @@ -37,7 +37,7 @@ Example how to install a master/server instance:
icinga2_features:
- name: api
force_newcert: false
ca_host: none
parent_host: none
endpoints:
- name: NodeName
zones:
Expand All @@ -48,56 +48,81 @@ icinga2_features:

### Instance with Certificate Authority

To create an instance with a local CA, the API Feature parameter `ca_host` should be `none`.
To create an instance with a local CA, the API feature parameter `parent_host` should be `none`.

```yaml
ca_host: none
parent_host: none
```

### Agent Setup

An agent (or satellite) setup can work in four different ways.

In all cases of auto-signing the master instance must have a secret `TicketSalt` defined.

**On-demand signing:**
The agent creates a CSR locally and then request signing via API.
Manual signing on the master instance is necessary.
For this, pass an empty ticket `ticket: ""`.
The `netways.icinga.icinga2_api` module used here will report changes with each execution until the certificate is signed.

**Auto-signing, providing a ticket:**
The agent can pass a ticket which the master instance can validate.
If the validation is successful, the agent receives its signed certificate.
You can use the `netways.icinga.icinga2_ticket` filter to create a valid ticket if you know the secret `TicketSalt`.
Example: `ticket: "{{ <common_name> | netways.icinga.icinga2_ticket(ticketsalt='<secret_ticket_salt>') }}"`

**Auto-signing, without providing a ticket:**
Before the agent requests its certificate, the ticket is generated on the master instance.
For this to work `parent_host` must be the master since ticket creation is delegated to `parent_host`.
If the `parent_host` is not the master, `icinga2_delegate_host: <inventory_hostname of master>` can be set to delegate there instead.

**Auto-signing, with reverse connection:**
Used in environments where the agent cannot connect to its parent but the parent can connect to the agent.
Here delegation to `parent_host` (or `icinga2_delegate_host`) is used to retrieve the CA certificate and generate a ticket,
so some tasks will be run on `parent_host` (or `icinga2_delegate_host`) directly.
This requires `delegate_pki: true` to be set.

### Generate Certificate Signing Requests

Create Signing Request to get a certificate managed by the parameter `ca_host` and `ca_host_port`. If
Create Signing Request to get a certificate managed by the parameter `parent_host` and `parent_port`. If
set to the master/server hostname, FQDN or IP, the node setup tries to connect
via API an retrieve the trusted certificate.
via API and retrieve the trusted certificate.

> [!INFO]
> Ansible will delegate the ticket creation to the CA host. You can change this behaviour by setting 'icinga2_delegate_host' to match another Ansible alias.
> Ansible will delegate the ticket creation to the `parent_host`. You can change this behaviour by setting 'icinga2_delegate_host' to match another Ansible alias.

```yaml
ca_host: icinga-server.localdomain
ca_host_port: 5665
parent_host: icinga-server.localdomain
parent_port: 5665
```

> [!INFO]
> In case your agent can't connect to the CA host/master, you can change ca_host to your satellite.
> In addition you can use the variables `icinga2_delegate_host`
> and `ticket_salt` to delegate ticket creation to one of your satellites instead.
> But is will also work because the delegation task will be initiated by the Ansible controlhost.

Example if connection and ticket creation should be on the satellite:
Example if connection should be established to the satellite, resulting in on-demand certificate signing:

```yaml
icinga2_features:
- name: api
ca_host: icinga-satellite.localdomain
ticket_salt: "{{ icinga2_constants.ticket_salt }}"
parent_host: icinga-satellite.localdomain
ticket: ""
[...]
icinga2_delegate_host: icinga-satellite.localdomain
```
Example if agent should connect to satellite and the tickets are generated on the
master host.

> In the above case, the `parent_host` is a satellite which does not have the secret `TicketSalt`, so we cannot delegate there for ticket creation.

Example if agent should connect to satellite and the tickets are generated on the master host.

```yaml
icinga2_features:
- name: api
ca_host: icinga-satellite.localdomain
ticket_salt: "{{ icinga2_constants.ticket_salt }}"
parent_host: icinga-satellite.localdomain
[...]
icinga2_delegate_host: icinga-master.localdomain
```

By default the FQDN is used as certificate common name, to put a name
yourself:
By default the FQDN is used as certificate common name, to put a name yourself:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add some disclaimer here that this is typically not best practice and can require additional work in further steps like the director setup not being able to utilize the agent setting or at least give the one good example for this feature which would be lower-casing the hostname?


> This is not recommended! Apart from endpoints in the master zone and satellite zones (due to the option for high availability)
> every endpoint should use its FQDN as the `cert_name`. This is considered best practice.

```yaml
cert_name: myown-commonname.fqdn
Expand All @@ -109,7 +134,7 @@ To force a new request set `force_newcert` to `true`:
force_newcert: true
```

To increase your security set `ca_fingerprint` to validate the certificate of the `ca_host`:
To increase your security set `ca_fingerprint` to validate the CA certificate:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add how to get the fingerprint for using it here?


```yaml
ca_fingerprint: "00 DE AD BE EF"
Expand All @@ -123,6 +148,9 @@ The fingerprint can be retrieved with OpenSSL:

```bash
openssl x509 -noout -fingerprint -sha256 -inform pem -in /path/to/ca.crt

# Verbose example for use on the master
openssl x509 -noout -fingerprint -sha256 -inform pem -in /var/lib/icinga2/ca/ca.crt | cut -d '=' -f 2 | tr '[A-Z]' '[a-z]' | tr -d ':'
```

### Top-down connections
Expand All @@ -131,17 +159,17 @@ Use `delegate_pki: true` when the agent cannot initiate a connection to the CA h

In this mode, the role:

- fetches `ca.crt` from the `ca_host` via Ansible and copies it to the agent
- fetches `ca.crt` from the `parent_host` via Ansible and copies it to the agent
- generates a self-signed certificate on the agent
- creates a ticket on the `ca_host` via `delegate_to`
- creates a ticket on the `parent_host` via `delegate_to`
- writes the ticket to `{{ icinga2_cert_path }}/ticket`

Icinga then completes certificate signing automatically when the parent connects to the agent and the cluster handshake starts. This is not a fully offline / disconnected workflow.

```yaml
icinga2_features:
- name: api
ca_host: icinga-master.localdomain
parent_host: icinga-master.localdomain
delegate_pki: true
endpoints:
- name: icinga-agent.localdomain
Expand All @@ -167,7 +195,7 @@ ssl_key: certificate.key
```

> **_NOTE:_** All three parameters have to be set otherwise a signing request is built
and `ca_host` must be defined.
and `parent_host` must be defined.

The role will copy the files from your Ansible controller node to
**/var/lib/icinga2/certs** on the remote host. File names are
Expand All @@ -194,18 +222,29 @@ icinga2_features:

### Feature variables

* `ca_host: string`
* Use to decide where to gather the certificates. When set to **None**, Ansible will create a local Certificate Authority on the Host. Use **hostname** or **ipaddress** as value.
* `parent_host: string`
* Use to decide where to gather the certificates. When set to **none**, Ansible will create a local Certificate Authority on the Host. Use **FQDN**, **hostname** or **ipaddress** as value.

* `force_newcert: boolean`
* Force new certificates on the destination hosts.

* `force_newca: boolean`
* Force new CA on the destination hosts (master instance).
This of course invalidates the current certificates of all Icinga nodes in the cluster. They also need to be recreated from the new CA.
Use with caution!

* `ticket: string`
* A valid ticket for the given `cert_name`. Used for auto-signing the CSR. Can be generated using the `netways.icinga.icinga2_ticket` filter. If `ticket: ""`, on-demand signing is used.

* `delegate_pki: boolean`
* Skip outbound `pki save-cert` and `pki request` on the agent. Provision `ca.crt` and ticket through Ansible delegation and rely on Icinga CSR auto-signing when the parent connects inbound.

* `cert_name: string`
* Common name of Icinga client/server instance. Default is **ansible_facts['fqdn']**.

* `ca_fingerprint: string`
* SHA256 fingerprint of the CA certificate. If defined, the fingerprint is validated.

* `ssl_cacert: string`
* Path to the ca file when using manual certificates

Expand Down
2 changes: 1 addition & 1 deletion molecule/default/converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
auth_token: testtoken123
- name: mainlog
- name: api
ca_host: none
parent_host: none
endpoints:
- name: "{{ ansible_facts['fqdn'] }}"
zones:
Expand Down
2 changes: 1 addition & 1 deletion molecule/local-default-pgsql/converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
icinga2_features:
- name: mainlog
- name: api
ca_host: none
parent_host: none
endpoints:
- name: "{{ ansible_facts['fqdn'] }}"
zones:
Expand Down
2 changes: 1 addition & 1 deletion molecule/local-default/converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
auth_token: testtoken123
- name: mainlog
- name: api
ca_host: none
parent_host: none
endpoints:
- name: "{{ ansible_facts['fqdn'] }}"
zones:
Expand Down
2 changes: 1 addition & 1 deletion molecule/reverse-connect-pki/host_vars/icinga-agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ icinga2_features:
- name: checker
- name: mainlog
- name: api
ca_host: icinga-master
parent_host: icinga-master
delegate_pki: true
endpoints:
- name: icinga-master
Expand Down
2 changes: 1 addition & 1 deletion molecule/reverse-connect-pki/host_vars/icinga-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ icinga2_features:
- name: checker
- name: mainlog
- name: api
ca_host: none
parent_host: none
endpoints:
- name: icinga-master
- name: icinga-agent
Expand Down
2 changes: 1 addition & 1 deletion playbooks/full_stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@
- name: icingadb
environment_id: "df29d078f1fdeffddf3bcce81a8194397d326e11" # just something fixed, so destroy/build work right away
- name: api
ca_host: none
parent_host: none
cert_name: "{{ icinga2_constants.NodeName }}"
endpoints:
- name: "{{ icinga2_constants.NodeName }}"
Expand Down
Loading