openfire-docker-compose/federation
Dan Caseley 460fc98fc0 Add config to allow for GitHub Packages instead 2025-02-12 10:00:49 +01:00
..
plugins Move all the files into purpose-defined subdirectories 2022-07-27 14:47:41 +02:00
scripts feat: Support certificate revocation 2024-11-21 14:09:03 +01:00
sql refactor: rename openfire.sql to clarify execution order 2025-01-07 13:41:20 +00:00
xmpp chore: Update java.security with new from Openfire 2024-12-03 15:40:53 +01:00
README.md refactor: rename openfire.sql to clarify execution order 2025-01-07 13:41:20 +00:00
docker-compose-federated.yml Add config to allow for GitHub Packages instead 2025-02-12 10:00:49 +01:00
docker-compose-network-dualstack.yml feat: Add IPv6 support to OCSP responder 2024-11-21 14:09:03 +01:00
docker-compose-network-ipv4-only.yml fix: Use unique IP for OCSP 2024-11-21 14:09:03 +01:00
docker-compose-ocsp-responder-ipv4.yml feat: Add IPv6 support to OCSP responder 2024-11-21 14:09:03 +01:00
docker-compose-ocsp-responder-ipv6.yml feat: Add IPv6 support to OCSP responder 2024-11-21 14:09:03 +01:00
docker-compose-ocsp-responder.yml fix: Keep OCSP responder running on invalid request 2024-11-21 14:09:03 +01:00
start.sh feat: Add IPv6 support to OCSP responder 2024-11-21 14:09:03 +01:00

README.md

Federated configuration

Running ./start.sh will perform some cleanup then start the containers in a federated configuration. When running, the system looks like this:

                   +---------------------------------------------+
                   |      172.50.0.10           172.50.0.20      |
                   |      +--------+            +--------+       |
(XMPP-C2S)   5221 -|      |        |            |        |       |- 5222 (XMPP-C2S)
(XMPP-S2S)   5261 -|------| XMPP 1 +============+ XMPP 2 |-------|- 5262 (XMPP-S2S)
(HTTP-Admin) 9091 -|      |        |            |        |       |- 9092 (HTTP-Admin)
(BOSH)  7071/7441 -|      +----+---+            +----+---+       |- 7072/7442 (BOSH)
                   |           |                     |           |
                   |           |                     |           |
                   |       +---+--+               +--+---+       |
                   |       |      |               |      |       |
(Database)   5431 -|-------| DB 1 |               | DB 2 |-------|- 5432 (Database)
                   |       |      |               |      |       |
                   |       +------+               +------+       |
                   |      172.50.0.11            172.50.0.21     |
                   |                                             |
                   +----------------172.50.0.0/24----------------+

Openfire is configured with the following hostnames/XMPP domain names:

  • xmpp1.localhost.example
  • xmpp2.localhost.example

XMPP 1 has the following users:

  • user1 password
  • user2 password

XMPP 1 hosts the following MUC rooms:

  • muc1
  • muc2

XMPP 2 has the following users:

  • user3 password
  • user4 password

XMPP 2 hosts the following MUC rooms:

  • muc3
  • muc4

Hosts file entries

To access the XMPP servers and (optional) OCSP responder from your local machine you should add entries to your hosts file:

127.0.0.1 xmpp.localhost.example
127.0.0.1 xmpp1.localhost.example
127.0.0.1 xmpp2.localhost.example
127.0.0.1 xmpp3.localhost.example
127.0.0.1 ocsp.localhost.example

This helps when testing with various clients and tools.

Network

The Docker compose file defines a custom bridge network with a single subnet of 172.50.0.0/24

When the -6 argument to ./start.sh is provided, then an additional subnet of fd23:0d79:d076::/64 is configured. Then, IPv6 is preferred for internal networking. Note that the IPv4 network remains in place, as Docker does not support IPv6-only containers.

When running with the optional -6 and -o flags (that add IPv6 and OCSP support respectively), and the system looks like this:

                   +---------------------------------------------+
                   |  [fd23:d79:d076::10]  [fd23:d79:d076::20]   |
                   |      172.50.0.10           172.50.0.20      |
                   |      +--------+            +--------+       |
(XMPP-C2S)   5221 -|      |        |            |        |       |- 5222 (XMPP-C2S)
(XMPP-S2S)   5261 -|------| XMPP 1 +============+ XMPP 2 |-------|- 5262 (XMPP-S2S)
(HTTP-Admin) 9091 -|      |        |            |        |       |- 9092 (HTTP-Admin)
(BOSH)  7071/7441 -|      +----+---+            +----+---+       |- 7072/7442 (BOSH)
                   |           |                     |           |
                   |           |                     |           |
                   |       +---+--+               +--+---+       |
                   |       |      |               |      |       |
(Database)   5431 -|-------| DB 1 |               | DB 2 |-------|- 5432 (Database)
                   |       |      |               |      |       |
                   |       +------+               +------+       |
                   |      172.50.0.11            172.50.0.21     |
                   |  [fd23:d79:d076::11]  [fd23:d79:d076::21]   |
                   |                                             |
                   |               +-----------+                 |
(OCSP)       8888 -|---------------|   OCSP    |                 |
                   |               | Responder |                 |
                   |               +-----------+                 |
                   |                172.50.0.88                  |
                   |            [fd23:d79:d076::88]              |
                   |                                             |
                   |                                             |
                   +----------------172.50.0.0/24----------------+
                                fd23:0d79:d076::/64

Removing a node from the network

To remove a node from the network run the following command:

docker network disconnect NETWORK-NAME CONTAINER-NAME

For example:

docker network disconnect openfire-testing_openfire-federated-net openfire-testing_xmpp1_1

Adding a node to the network

To add a node to the network fun the following command:

docker network connect NETWORK-NAME CONTAINER-NAME

For example:

docker network connect openfire-testing_openfire-federated-net openfire-testing_xmpp1_1

Certificates

By default, the system uses self-signed certificates that have been pre-generated and added to the identity and trust stores for xmpp1.localhost.example and xmpp2.localhost.example.

This simplifies the setup process and avoids the need to generate certificates, but the limitations of this are that the certificates are not trusted by anything other than our own servers (xmpp1 and xmpp2), and that they do not support OCSP (Online Certificate Status Protocol).

OCSP Support

A more fully featured solution is provided by the -o option of the start.sh script which enables OCSP support for the Openfire federated environment. When passed the -o option the script will generate a complete certificate hierarchy with OCSP support, and deploy an OCSP responder service configured to respond to OCSP requests for the server certificates.

Here's what the script creates:

  • Root CA certificate (self-signed)
  • Intermediate CA certificate (signed by Root CA)
  • Two server certificates with OCSP information (one for each Openfire instance)
  • An OCSP responder certificate (for signing OCSP responses)
  • Full certificate chains for each XMPP server (server + intermediate + root)
  • Certificate database (index.txt) for the OCSP responder to track certificate statuses

All certificates are stored in ./_data/certs/.

                           Root CA
                      (Top level trust root)
                      (Kept offline/secure)
                              |
                              v
                       Intermediate CA
                  (Day-to-day certificate issuer)
                  (OCSP configuration point)
                              |
         +--------------------+------------------+
         v                    v                  v
    XMPP1 Cert            XMPP2 Cert         OCSP Cert
         |                    |              (Signs OCSP responses)
         v                    v
   XMPP1 Server          XMPP2 Server
    [keystore]             [keystore]
    (server's identity)    (server's identity)
    [truststore]           [truststore]
    (who to trust)         (who to trust)

This setup allows certificates to be checked for revocation status making a request to the OCSP responder:

openssl ocsp -url http://localhost:8888 \
    -issuer _data/certs/ca/intermediate-ca/intermediate.crt \
    -CAfile _data/certs/xmpp1_chain.pem \
    -cert _data/certs/xmpp1.crt \
    -text

Certificate Revocation

The ./scripts/revocation.sh script allows you to revoke SSL certificates and update the OCSP responder's database. You can also un-revoke certificates that were previously revoked.

Important: Run this script from the root federation/ directory, as it requires direct access to the _data directory containing the certificates and PKI infrastructure.

./scripts/revocation.sh --server xmpp1 [--reason reason] [--unrevoke]

Available revocation reasons:

  • unspecified (default)
  • keyCompromise
  • CACompromise
  • affiliationChanged
  • superseded
  • cessationOfOperation
  • certificateHold

Examples:

# Revoke xmpp1's certificate
./scripts/revocation.sh --server xmpp1

# Revoke with specific reason
./scripts/revocation.sh --server xmpp1 --reason keyCompromise

# Remove revocation status
./scripts/revocation.sh --server xmpp1 --unrevoke

To verify the current status:

openssl ocsp -url http://localhost:8888 \
    -issuer _data/certs/ca/intermediate-ca/intermediate.crt \
    -CAfile _data/certs/xmpp1_chain.pem \
    -cert _data/certs/xmpp1.crt \
    -text

Note: The first OCSP status check may return the previous status. Run the check again if this happens - subsequent checks will show the current status.

How it's built

To recreate the known good state for the system we first create base Openfire and Postgres containers. We then perform the manual setup and any other configuration that we require, such as adding users and MUC rooms. Once the setup is complete we dump the database from the container to the Docker host and copy the Openfire config files from the container to the Docker host. These are then used with Docker volumes for creating the same state in subsequent Openfire and Postgres containers.

Adding a new node

Configure a docker-compose file to stand up:

  1. a base Openfire container (named xmpp3)
  2. a base Postgres Docker container (named db3)

We will use these containers to configure our third node before exporting the DB and Openfire configuration. Be sure to set the correct IP addresses and increment the host port numbers so they don't clash with existing exposed ports. The convention I have followed is to increment the IP addresses by 10 and the port numbers by 1:

For xmpp1

  • Openfire IP: 172.50.0.10 / fd23:d79:d076::10
  • DB IP: 172.50.0.11 / fd23:d79:d076::11
  • XMPP port: 5221
  • Admin port: 9091

For xmpp2

  • Openfire IP: 172.50.0.20 / fd23:d79:d076::20
  • DB IP: 172.50.0.21 / fd23:d79:d076::21
  • XMPP port: 5222
  • Admin port: 9092

Example docker-compose file for our third node:

db3:
  image: library/postgres:9.6.24-alpine
  environment:
    - "POSTGRES_DB=openfire"
    - "POSTGRES_USER=openfire"
    - "POSTGRES_PASSWORD=hunter2"
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.31
      ipv6_address: fd23:d79:d076::31

xmpp3:
  image: openfire:latest
  ports:
    - "5223:5222"
    - "9093:9090"
  depends_on:
    - "db3"
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.30
      ipv6_address: fd23:d79:d076::30

networks:
  openfire-federated-net:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 172.50.0.0/24
        - subnet: fd23:0d79:d076::/64

Run this with the start.sh. Once running navigate to http://localhost:9093 and manually configure the Openfire server. The database hostname should be the name of the DB node in the compose file (so db3 in this case). You should also get the database name, username, and password, from the compose file.

Create any configuration you require (e.g. adding users).

Create directories for the exported DB and config:

mkdir -p ./sql/3
mkdir -p ./xmpp/3  

Export the database:

docker exec -t openfire-testing_db3_1 pg_dump -U openfire openfire > ./sql/3/000-init-openfire.sql

Export the Openfire configuration:

docker cp openfire-testing_xmpp3_1:/var/lib/openfire/conf ./xmpp/3/

Add the new node to the main docker-compose-federated.yml including the volume definitions to pull in your exported base configuration data:

...

db3:
  image: library/postgres:9.6.24-alpine
  environment:
    - "POSTGRES_DB=openfire"
    - "POSTGRES_USER=openfire"
    - "POSTGRES_PASSWORD=hunter2"
  volumes:
    - ./sql/3:/docker-entrypoint-initdb.d
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.31

xmpp3:
  image: openfire:latest
  ports:
    - "5223:5222"
    - "9093:9090"
  depends_on:
    - "db3"
  volumes:
    - ./_data/xmpp/3/conf:/var/lib/openfire/conf
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.30
...

Add the IPv4-only network definition in docker-compose-federated-ipv4-only.yml:

...

db3:
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.31

xmpp3:
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.30
...

Add the dual-stack network definition in docker-compose-federated-dualstack.yml (note that this also includes IPv4 config):

...

db3:
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.31
      ipv6_address: fd23:d79:d076::31

xmpp3:
  networks:
    openfire-federated-net:
      ipv4_address: 172.50.0.30
      ipv6_address: fd23:d79:d076::30
...

Lastly, add the new host in all extra_hosts configuration blocks.