Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the DNS-01 challenge #7

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

Rijul-A
Copy link

@Rijul-A Rijul-A commented Dec 23, 2021

If SNIKKET_DNS_CHALLENGE is set to 1, or SNIKKET_TWEAK_XMPP_DOMAIN is
set, spin up an instance of CoreDNS to respond to DNS challenges. Shut
it down once certificates are obtained, and only enable it on renewal.

Rationale
"Many DNS servers do not provide an API to enable automation for the
ACME DNS challenges. Those which do, give the keys way too much power.
Leaving the keys laying around your random boxes is too often a
requirement to have a meaningful process automation."
See https://github.com/joohoi/acme-dns

Requirements (domain.tld is SNIKKET_TWEAK_XMPP_DOMAIN or SNIKKET_DOMAIN
if not set)

  • The CNAME record of _acme-challenge.domain.tld needs to point to
    cert.snikket.domain.tld
  • The NS record of snikket.domain.tld needs to point to
    ns-snikket.domain.tld
  • The A/AAAA record(s) of ns-snikket.domain.tld needs(s) to point to
    the instance IP address

Mechanics

  • Through a certbot pre-hook, the requirements are first validated
    (although a failure is only logged). Next, a CoreDNS instance is spun
    up with just SOA and NS records for snikket.domain.tld, with the NS
    set to ns-snikket.domain.tld
  • Using certbot's manual plugin + its authorization hook, the relevant
    TXT record is added to cert.snikket.domain.tld. Multiple records are
    supported here, so this script can be used for domain.tld and
    *.domain.tld in one go.
  • Through a certbot post-hook, the DNS server is shut down after
    certificates are obtained.

Considerations

  • Since it is possible that people are hosting their instance on
    snikket.domain.tld, an override for the DNS ZONE can be added.
    Alternatively, we can keep the DNS server running, but that will add
    much more complexity to the setup.

Related issues

If SNIKKET_DNS_CHALLENGE is set to 1, or SNIKKET_TWEAK_XMPP_DOMAIN is
set, spin up an instance of CoreDNS to respond to DNS challenges. Shut
it down once certificates are obtained, and only enable it on renewal.

Rationale
"Many DNS servers do not provide an API to enable automation for the
ACME DNS challenges. Those which do, give the keys way too much power.
Leaving the keys laying around your random boxes is too often a
requirement to have a meaningful process automation."
See https://github.com/joohoi/acme-dns

Requirements (domain.tld is SNIKKET_TWEAK_XMPP_DOMAIN or SNIKKET_DOMAIN
if not set)
 - The CNAME record of _acme-challenge.domain.tld needs to point to
   cert.snikket.domain.tld
 - The NS record of snikket.domain.tld needs to point to
   ns-snikket.domain.tld
 - The A/AAAA record(s) of ns-snikket.domain.tld needs(s) to point to
   the instance IP address

Mechanics
 - Through a certbot pre-hook, the requirements are first validated
   (although a failure is only logged). Next, a CoreDNS instance is spun
   up with just SOA and NS records for snikket.domain.tld, with the NS
   set to ns-snikket.domain.tld
 - Using certbot's manual plugin + its authorization hook, the relevant
   TXT record is added to cert.snikket.domain.tld. Multiple records are
   supported here, so this script can be used for domain.tld and
   *.domain.tld in one go.
 - Through a certbot post-hook, the DNS server is shut down after
   certificates are obtained.

Considerations
 - Since it is possible that people are hosting their instance on
   snikket.domain.tld, an override for the DNS ZONE can be added.
   Alternatively, we can keep the DNS server running, but that will add
   much more complexity to the setup.
 - Add wait time between DNS server creation and NS record check
 - Add logs to indicate if DNS challenge is being used, and why
 - Add logs to indicate wait time and completion
 - Add style file and pre-commit hook (activate with `git config
   --local core.hooksPath .githooks`)
@Rijul-A
Copy link
Author

Rijul-A commented Mar 12, 2022

Instructions to test this out are below:

Prerequisites:

  • Follow other instructions from https://snikket.org (DNS / firewall / Docker)
  • If you run a firewall, allow UDP traffic over port 53 to your server.
  • Disable any existing Snikket installations (docker-compose stop in /etc/snikket/).
  • Assuming you are hosting your XMPP server on example.com (JIDs of the format [email protected]) with the Snikket web portal hosted on chat.example.com, you will need the following additional DNS records. The word snikket in the additional DNS records is mandatory right now, but can be changed. Some SRV records are also needed, I think, but not sure of them.
    • CNAME record of _acme-challenge.example.com points to cert.snikket.example.com
    • NS record of snikket.example.com points to ns-snikket.example.com
    • The A/AAAA record(s) of ns-snikket.example.com point to the server on which you are working.

Make a new folder and cd into it

mkdir snikket-custom
cd snikket-custom

In the folder, add your snikket.conf with the original configuration + these options. SNIKKET_CERTBOT_OPTIONS will be removed later, but best to keep it on until you know that this configuration works and you don't get stuck with LetsEncrypt's rate limits.

SNIKKET_TWEAK_XMPP_DOMAIN=example.com
SNIKKET_CERTBOT_OPTIONS=--staging

Clone the respective repositories with branches in the same folder:

git clone -b split-xmpp-domain https://github.com/Rijul-A/snikket-server
git clone -b dns-challenge https://github.com/Rijul-A/snikket-cert-manager
git clone -b split-xmpp-domain https://github.com/Rijul-A/snikket-web-proxy

Add the following as docker-compose.yml

version: "3.3"

services:
  snikket_proxy:
    container_name: snikket-proxy-custom
    build: ./snikket-web-proxy
    env_file: snikket.conf
    network_mode: host
    volumes:
      - snikket_data_custom:/snikket
      - acme_challenges_custom:/var/www/html/.well-known/acme-challenge
    restart: "unless-stopped"
  snikket_certs:
    container_name: snikket-certs-custom
    build: ./snikket-cert-manager
    env_file: snikket.conf
    volumes:
      - snikket_data_custom:/snikket
      - acme_challenges_custom:/var/www/.well-known/acme-challenge
    restart: "unless-stopped"
    ports:
      - "53:53/udp"
  snikket_portal:
    container_name: snikket-portal-custom
    image: snikket/snikket-web-portal:beta
    network_mode: host
    env_file: snikket.conf
    restart: "unless-stopped"
  snikket_server:
    container_name: snikket-custom
    build: ./snikket-server
    network_mode: host
    volumes:
      - snikket_data_custom:/snikket
    env_file: snikket.conf
    restart: "unless-stopped"

volumes:
  acme_challenges_custom:
  snikket_data_custom:

Build the docker images, run and follow the logs:

docker-compose build
docker-compose up -d
docker-compose logs --follow

Wait for the certificates to be obtained from LE (takes some time, around 3 minutes). If it fails, the logs will indicate why.

snikket-certs-custom | IMPORTANT NOTES:
snikket-certs-custom |  - Congratulations! Your certificate and chain have been saved at:

Once this is done, you can remove SNIKKET_CERTBOT_OPTIONS from snikket.conf, clear the data, and restart the process.

docker-compose down
docker volume rm snikket-custom_acme_challenges_custom
docker volume rm snikket-custom_snikket_data_custom
docker-compose up -d

The certificates obtained are then imported into prosody (the missing certificates can be ignored for now as we have a wildcard).

snikket-custom    | No certificate for host groups.example.com found :(
snikket-custom    | No certificate for host share.example.com found :(
snikket-custom    | Imported certificate and key for hosts example.com

Wait a bit, and make an invite for yourself:

docker exec snikket-custom create-invite --admin --group default

To prevent `assert( token )` from failing for persistent volumes
(certificate renewal or other option changes), remove the file each time
the pre-hook is run.
@hdasch
Copy link

hdasch commented Jun 1, 2022

The instructions in comment above helped me bring up a Snikket server with a tweaked domain. In particular, the DNS error checking caught a couple configuration issues. Very helpful, thanks.

After fixing the configuration issues, I still had difficulty that I resolved by docker volume rm snikket-custom_acme_chalenges_custom and docker volume rm snikket-custom_snikket_data_custom.

This build did not provide the web portal. So login fails with "This Snikket service only hosts addresses ending in chat.example.com".

I, rather blindly, mangled snikket-web-portal enough to get bypass this issue. The changes I made are in two commits available in my fork. I do not know whether that merits a pull request; it would have to be conditional based on this pull request.

The resulting web portal does not currently support account export as this pull request does not have the necessary snikket-server XEP-227 work. But I do get circle management. And that is good enough for me.

Thanks for this work and the instructions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants