linuxserver / docker-swag

Nginx webserver and reverse proxy with php support and a built-in Certbot (Let's Encrypt) client. It also contains fail2ban for intrusion prevention.
https://docs.linuxserver.io/general/swag
GNU General Public License v3.0
2.69k stars 236 forks source link

Can not get Letsencrypt certificate for DuckDNS subdomain (after recent update) #281

Closed peterNordin closed 1 year ago

peterNordin commented 1 year ago

linuxserver.io


Expected Behavior

After the recent update in the way Duckdns is handled I can no longer get a Letsencrypt certificate using Duckdns. It worked fine before that updated. I feel confident that I have been made the necessary configuration changes (According to the updated README)

Current Behavior

I get the following problems reported in the log (docker logs swag)

writing new private key to '/config/keys/cert.key
req: Can't open "/config/keys/cert.key" for writing, No such file or directory

docker exec -it swag ls -l /config/keys

total 2
lrwxrwxrwx 1 root root 27 Oct 16 18:35 cert.crt -> ./letsencrypt/fullchain.pem
lrwxrwxrwx 1 root root 25 Oct 16 18:35 cert.key -> ./letsencrypt/privkey.pem
lrwxrwxrwx 1 root root 49 Oct 16 18:35 letsencrypt -> ../etc/letsencrypt/live/mysubdomain.duckdns.org

But /config/keys/cert.key point to /config/etc/letsencrypt/live/mysubdomain.duckdns.org/privkey.pem which has been deleted (the entire live directory was removed by the updated swag container).

I also see this odd (maybe) behavior. Every time i restart the container, a new key is generated in the /config/etc/letsencrypt/keys directory.
docker exec -it swag ls -l /config/etc/letsencrypt/keys

total 34
-rw------- 1 abc  abc  3276 Oct 16 16:29 0000_key-certbot.pem
-rw------- 1 abc  abc  3272 Oct 16 16:31 0001_key-certbot.pem
-rw------- 1 abc  abc  3272 Oct 16 16:33 0002_key-certbot.pem
-rw------- 1 root root 3272 Oct 16 18:36 0003_key-certbot.pem

Then further down at the end of the log The DNS response does not contain an answer to the question: mysubdomain.duckdns.org. IN TXT

I also found a thread about this: https://community.letsencrypt.org/t/swag-no-answer-from-dns-in-txt-duckdns but with no obvious solution except reverting swag to an older version.

Steps to Reproduce

  1. Try to run the container according to the given instructions. In-place updated container over the old installation.

An alternative approach was to remove (rename) the old host-side swag (/config) directory to let swag generate a new set of files on startup.

  1. In this case the /config/keys/cert.key could be written, but then since I had not added my Duckdns token yet I still could not get a certificate.
  2. After adding the token and restarting the container I was back to the situation described in this issue. The token is accepted but it still does not work

Environment

OS: Ubuntu 22.04.1 LTS CPU architecture: x86_64 How docker service was installed:

From official Ubuntu repo

Command used to create docker container (run/create/compose/screenshot)

---
version: "3.5"
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=113
      - PGID=113
      - TZ=Europe/Stockholm
      - URL=mysubdomain.duckdns.org
      - VALIDATION=dns
      - SUBDOMAINS=wildcard
#      - CERTPROVIDER= #optional
      - DNSPLUGIN=duckdns
#      - PROPAGATION= #optional
#      - EMAIL= #optional
#      - ONLY_SUBDOMAINS=false #optional
#      - EXTRA_DOMAINS= #optional
      - STAGING=false #optional
    volumes:
      - /conf/swag:/config
    ports:
      - 443:443
      - 80:80 #optional
    restart: unless-stopped
    networks:
      - default

I have also verified that /config/dns-conf/duckdns.ini contains my duckdns token

Docker logs

docker logs swag

[custom-init] No custom services found, skipping...
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service 00-legacy: starting
s6-rc: info: service 00-legacy successfully started
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
cont-init: info: running /etc/cont-init.d/01-envfile
cont-init: info: /etc/cont-init.d/01-envfile exited 0
cont-init: info: running /etc/cont-init.d/01-migrations
[migrations] started
[migrations] 01-nginx-site-confs-default: skipped
[migrations] done
cont-init: info: /etc/cont-init.d/01-migrations exited 0
cont-init: info: running /etc/cont-init.d/10-adduser

-------------------------------------
          _         ()
         | |  ___   _    __
         | | / __| | |  /  \
         | | \__ \ | | | () |
         |_| |___/ |_|  \__/

Brought to you by linuxserver.io
-------------------------------------

To support the app dev(s) visit:
Certbot: https://supporters.eff.org/donate/support-work-on-certbot

To support LSIO projects visit:
https://www.linuxserver.io/donate/
-------------------------------------
GID/UID
-------------------------------------

User uid:    113
User gid:    113
-------------------------------------

cont-init: info: /etc/cont-init.d/10-adduser exited 0
cont-init: info: running /etc/cont-init.d/11-folders
cont-init: info: /etc/cont-init.d/11-folders exited 0
cont-init: info: running /etc/cont-init.d/12-samples
cont-init: info: /etc/cont-init.d/12-samples exited 0
cont-init: info: running /etc/cont-init.d/13-nginx
cont-init: info: /etc/cont-init.d/13-nginx exited 0
cont-init: info: running /etc/cont-init.d/14-php
cont-init: info: /etc/cont-init.d/14-php exited 0
cont-init: info: running /etc/cont-init.d/15-keygen
generating self-signed keys in /config/keys, you can replace these with your own keys if required
Generating a RSA private key
..+++++
...............................................................................+++++
writing new private key to '/config/keys/cert.key'
req: Can't open "/config/keys/cert.key" for writing, No such file or directory
cont-init: info: /etc/cont-init.d/15-keygen exited 1
cont-init: info: running /etc/cont-init.d/20-permissions
cont-init: info: /etc/cont-init.d/20-permissions exited 0
cont-init: info: running /etc/cont-init.d/30-test-run
cont-init: info: /etc/cont-init.d/30-test-run exited 0
cont-init: info: running /etc/cont-init.d/31-require-url
cont-init: info: /etc/cont-init.d/31-require-url exited 0
cont-init: info: running /etc/cont-init.d/40-folders
cont-init: info: /etc/cont-init.d/40-folders exited 0
cont-init: info: running /etc/cont-init.d/41-samples
cont-init: info: /etc/cont-init.d/41-samples exited 0
cont-init: info: running /etc/cont-init.d/42-fail2ban
cont-init: info: /etc/cont-init.d/42-fail2ban exited 0
cont-init: info: running /etc/cont-init.d/43-crontabs
cont-init: info: /etc/cont-init.d/43-crontabs exited 0
cont-init: info: running /etc/cont-init.d/45-nginx
cont-init: info: /etc/cont-init.d/45-nginx exited 0
cont-init: info: running /etc/cont-init.d/50-certbot
Variables set:
PUID=113
PGID=113
TZ=Europe/Stockholm
URL=mysubdomain.duckdns.org
SUBDOMAINS=wildcard
EXTRA_DOMAINS=
ONLY_SUBDOMAINS=false
VALIDATION=dns
CERTPROVIDER=
DNSPLUGIN=duckdns
EMAIL=
STAGING=false

the resulting certificate will only cover the subdomains due to a limitation of duckdns, so it is advised to set the root location to use www.subdomain.duckdns.org
Using Let's Encrypt as the cert provider
SUBDOMAINS entered, processing
Wildcard cert for only the subdomains of mysubdomain.duckdns.org will be requested
No e-mail address entered or address invalid
dns validation via duckdns plugin is selected
Generating new certificate
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for *.mysubdomain.duckdns.org
Unsafe permissions on credentials configuration file: /config/dns-conf/duckdns.ini
Unsafe permissions on credentials configuration file: /config/dns-conf/duckdns.ini
The DNS response does not contain an answer to the question: mysubdomain.duckdns.org. IN TXT
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
ERROR: Cert does not exist! Please see the validation error above. Make sure you entered correct credentials into the /config/dns-conf/duckdns.ini file.

docker exec -it swag cat /var/log/letsencrypt/letsencrypt.log

2022-10-16 18:36:03,408:DEBUG:certbot._internal.main:certbot version: 1.31.0
2022-10-16 18:36:03,409:DEBUG:certbot._internal.main:Location of certbot entry point: /usr/bin/certbot
2022-10-16 18:36:03,409:DEBUG:certbot._internal.main:Arguments: ['--non-interactive', '--renew-by-default', '--server', 'https://acme-v02.api.letsencrypt.org/directory', '-a', 'dns-duckdns', '--dns-duckdns-credentials', '/config/dns-conf/duckdns.ini', '--rsa-key-size', '4096', '--register-unsafely-without-email', '--agree-tos', '-d', '*.mysubdomain.duckdns.org']
2022-10-16 18:36:03,409:DEBUG:certbot._internal.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#certbot-dns-acmedns:dns-acmedns,PluginEntryPoint#certbot-dns-aliyun:dns-aliyun,PluginEntryPoint#certbot-dns-azure:dns-azure,PluginEntryPoint#certbot-dns-cpanel:cpanel,PluginEntryPoint#certbot-dns-desec:dns-desec,PluginEntryPoint#certbot-dns-directadmin:directadmin,PluginEntryPoint#certbot-dns-dnspod:dns-dnspod,PluginEntryPoint#certbot-dns-do:dns-do,PluginEntryPoint#certbot-dns-domeneshop:dns-domeneshop,PluginEntryPoint#certbot-dns-duckdns:dns-duckdns,PluginEntryPoint#certbot-dns-dynu:dns-dynu,PluginEntryPoint#certbot-dns-godaddy:dns-godaddy,PluginEntryPoint#certbot-dns-he:dns-he,PluginEntryPoint#certbot-dns-hetzner:dns-hetzner,PluginEntryPoint#certbot-dns-infomaniak:dns-infomaniak,PluginEntryPoint#certbot-dns-inwx:dns-inwx,PluginEntryPoint#certbot-dns-ionos:dns-ionos,PluginEntryPoint#certbot-dns-loopia:dns-loopia,PluginEntryPoint#certbot-dns-netcup:dns-netcup,PluginEntryPoint#certbot-dns-njalla:dns-njalla,PluginEntryPoint#certbot-dns-porkbun:dns-porkbun,PluginEntryPoint#certbot-dns-standalone:dns-standalone,PluginEntryPoint#certbot-dns-transip:dns-transip,PluginEntryPoint#certbot-dns-vultr:dns-vultr,PluginEntryPoint#certbot-plugin-gandi:dns,PluginEntryPoint#certbot-plugin-gandi:dns-gandi,PluginEntryPoint#certbot-route53:auth,PluginEntryPoint#cpanel,PluginEntryPoint#directadmin,PluginEntryPoint#dns,PluginEntryPoint#dns-acmedns,PluginEntryPoint#dns-aliyun,PluginEntryPoint#dns-azure,PluginEntryPoint#dns-cloudflare,PluginEntryPoint#dns-cloudxns,PluginEntryPoint#dns-desec,PluginEntryPoint#dns-digitalocean,PluginEntryPoint#dns-dnsimple,PluginEntryPoint#dns-dnsmadeeasy,PluginEntryPoint#dns-dnspod,PluginEntryPoint#dns-do,PluginEntryPoint#dns-domeneshop,PluginEntryPoint#dns-duckdns,PluginEntryPoint#dns-dynu,PluginEntryPoint#dns-gandi,PluginEntryPoint#dns-gehirn,PluginEntryPoint#dns-godaddy,PluginEntryPoint#dns-google,PluginEntryPoint#dns-he,PluginEntryPoint#dns-hetzner,PluginEntryPoint#dns-infomaniak,PluginEntryPoint#dns-inwx,PluginEntryPoint#dns-ionos,PluginEntryPoint#dns-linode,PluginEntryPoint#dns-loopia,PluginEntryPoint#dns-luadns,PluginEntryPoint#dns-netcup,PluginEntryPoint#dns-njalla,PluginEntryPoint#dns-nsone,PluginEntryPoint#dns-ovh,PluginEntryPoint#dns-porkbun,PluginEntryPoint#dns-rfc2136,PluginEntryPoint#dns-route53,PluginEntryPoint#dns-sakuracloud,PluginEntryPoint#dns-standalone,PluginEntryPoint#dns-transip,PluginEntryPoint#dns-vultr,PluginEntryPoint#manual,PluginEntryPoint#null,PluginEntryPoint#standalone,PluginEntryPoint#webroot)
2022-10-16 18:36:03,440:DEBUG:certbot._internal.log:Root logging level set at 30
2022-10-16 18:36:03,442:DEBUG:certbot._internal.plugins.selection:Requested authenticator dns-duckdns and installer None
2022-10-16 18:36:03,446:DEBUG:certbot._internal.plugins.selection:Single candidate plugin: * dns-duckdns
Description: Obtain certificates using a DNS TXT record for DuckDNS domains
Interfaces: Authenticator, Plugin
Entry point: dns-duckdns = certbot_dns_duckdns.cert.client:Authenticator
Initialized: <certbot_dns_duckdns.cert.client.Authenticator object at 0x7fc74cd4d730>
Prep: True
2022-10-16 18:36:03,446:DEBUG:certbot._internal.plugins.selection:Selected authenticator <certbot_dns_duckdns.cert.client.Authenticator object at 0x7fc74cd4d730> and installer None
2022-10-16 18:36:03,446:INFO:certbot._internal.plugins.selection:Plugins selected: Authenticator dns-duckdns, Installer None
2022-10-16 18:36:03,466:DEBUG:certbot._internal.main:Picked account: <Account(RegistrationResource(body=Registration(key=None, contact=(), agreement=None, status=None, terms_of_service_agreed=None, only_return_existing=None, external_account_binding=None), uri='https://acme-v02.api.letsencrypt.org/acme/acct/779240456', new_authzr_uri=None, terms_of_service=None), 6f8012e0bd203c912d6c79950e53665c, Meta(creation_dt=datetime.datetime(2022, 10, 16, 14, 29, 46, tzinfo=<UTC>), creation_host='f53971e4f3b7', register_to_eff=None))>
2022-10-16 18:36:03,466:DEBUG:acme.client:Sending GET request to https://acme-v02.api.letsencrypt.org/directory.
2022-10-16 18:36:04,108:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Date: Sun, 16 Oct 2022 16:36:04 GMT
Content-Type: application/json
Content-Length: 659
Connection: keep-alive
Cache-Control: public, max-age=0, no-cache
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change",
  "meta": {
    "caaIdentities": [
      "letsencrypt.org"
    ],
    "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf",
    "website": "https://letsencrypt.org"
  },
  "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct",
  "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce",
  "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order",
  "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert",
  "vqxLCCJcQ4Q": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417"
}
2022-10-16 18:36:04,110:DEBUG:certbot._internal.display.obj:Notifying user: Requesting a certificate for *.mysubdomain.duckdns.org
2022-10-16 18:36:04,532:DEBUG:certbot.crypto_util:Generating RSA key (4096 bits): /etc/letsencrypt/keys/0003_key-certbot.pem
2022-10-16 18:36:04,539:DEBUG:certbot.crypto_util:Creating CSR: /etc/letsencrypt/csr/0003_csr-certbot.pem
2022-10-16 18:36:04,539:DEBUG:acme.client:Requesting fresh nonce
2022-10-16 18:36:04,540:DEBUG:acme.client:Sending HEAD request to https://acme-v02.api.letsencrypt.org/acme/new-nonce.
2022-10-16 18:36:04,672:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Date: Sun, 16 Oct 2022 16:36:04 GMT
Connection: keep-alive
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: C878L5KmEo5FskneVjHtXkFyvRfx21vi4iP0WfRwnEUUa8Y
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

2022-10-16 18:36:04,672:DEBUG:acme.client:Storing nonce: C878L5KmEo5FskneVjHtXkFyvRfx21vi4iP0WfRwnEUUa8Y
2022-10-16 18:36:04,673:DEBUG:acme.client:JWS payload:
b'{\n  "identifiers": [\n    {\n      "type": "dns",\n      "value": "*.mysubdomain.duckdns.org"\n    }\n  ]\n}'
2022-10-16 18:36:04,688:DEBUG:acme.client:Sending POST request to https://acme-v02.api.letsencrypt.org/acme/new-order:
{
  "protected": "eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2FjY3QvNzc5MjQwNDU2IiwgIm5vbmNlIjogIkM4NzhMNUttRW81RnNrbmVWakh0WGtGeXZSZngyMXZpNGlQMFdmUnduRVVVYThZIiwgInVybCI6ICJodHRwczovL2FjbWUtdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9uZXctb3JkZXIifQ",
  "signature": "aqnHdne05dnVOSOg5cAy0V7fZ_4zXjaxrYBMykwB3-jDBvo4C9nsB6py4_7Fc4973f5Deu-W86sGJyTU92rAOG_kvSoK7QMIG47YuhVPh6ug3-wrgkrWFa8gPlPn8eTAkrr1GCH53Z5dYFqx2sDkYwTmJMs9rdYLIhZPa9SnJdchTNn_D_moKE0InoomxeHt6kR3vlRsfnUZ4Riy4wwV7A31no4Isk3A1F6lzB-vqz300wKUEMvMSVp7M1iUgItrWxmHh3b2OK8omBPThdvF0dGDVvBczWwVV2YECd8TQWNy1B6dgp_iFWVK7G3A8W-qSmos8LL2zthGn_ma83jnry1sv0cc7ZwCNtD1ZJDVTEhbXnRS8fKifDheVcoApepXaUD8tot5MO6zb-uGOhlqg3yeWbLgtGD8R-8vUg9Nm_w6Ii1_iV8XqJorq46Pt3ETCubKSuEpEsMdfWZa0qqEzEjnTwiSv7g4dyVCFyo8PsCw8MlBm1RVtDzmZ6dRbpCuMhv9_J5-XzNNhlk-1eCo0_2PDWE-wktr2zpy-ddayESCnc1fKrHBptwBEODnhPTvF6GLxC07JFkLpS4o-Ih2dfKSFXYhZ99l6Pwlz1PLfWgXJhyA5BzPnWuVuUMlxtB-sretqTytcdH_8GmFt8odQWAOyN_LjtX6nXO5PZuHenA",
  "payload": "ewogICJpZGVudGlmaWVycyI6IFsKICAgIHsKICAgICAgInR5cGUiOiAiZG5zIiwKICAgICAgInZhbHVlIjogIiouamFtdW1iYWtvLWxhbi5kdWNrZG5zLm9yZyIKICAgIH0KICBdCn0"
}
2022-10-16 18:36:04,977:DEBUG:acme.client:Received response:
HTTP 201
Server: nginx
Date: Sun, 16 Oct 2022 16:36:04 GMT
Content-Type: application/json
Content-Length: 352
Connection: keep-alive
Boulder-Requester: 779240456
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index"
Location: https://acme-v02.api.letsencrypt.org/acme/order/779240456/135141075506
Replay-Nonce: 5CA28-51PzGM9s4x3gjf0GwsVEiMpW8M22D8tAAD8balVhY
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "status": "pending",
  "expires": "2022-10-23T14:29:47Z",
  "identifiers": [
    {
      "type": "dns",
      "value": "*.mysubdomain.duckdns.org"
    }
  ],
  "authorizations": [
    "https://acme-v02.api.letsencrypt.org/acme/authz-v3/165226777726"
  ],
  "finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/779240456/135141075506"
}
2022-10-16 18:36:04,977:DEBUG:acme.client:Storing nonce: 5CA28-51PzGM9s4x3gjf0GwsVEiMpW8M22D8tAAD8balVhY
2022-10-16 18:36:04,978:DEBUG:acme.client:JWS payload:
b''
2022-10-16 18:36:04,992:DEBUG:acme.client:Sending POST request to https://acme-v02.api.letsencrypt.org/acme/authz-v3/165226777726:
{
  "protected": "eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2FjY3QvNzc5MjQwNDU2IiwgIm5vbmNlIjogIjVDQTI4LTUxUHpHTTlzNHgzZ2pmMEd3c1ZFaU1wVzhNMjJEOHRBQUQ4YmFsVmhZIiwgInVybCI6ICJodHRwczovL2FjbWUtdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hdXRoei12My8xNjUyMjY3Nzc3MjYifQ",
  "signature": "i95hdDGBuCShLsgTVWcuP_4wBQsJgKqf5oBQc-dsTrSA2UtYpXicrT5p2zi-DYUsM73s_24Rvyc7cP0G93Qj_HZocDlXqTqImW4rAvAFz03U9yt4eRkiBVfVERIkWWMX9Q4bJ22-l2a2CLmLSwX_ulNrPYbI0Qr61AbO0br6KHzJ4aoFtw1JYHGzztS6gMpDfI9YRl_91geVALEpnRxLr5iyVCVMNTMh82xYUuWBkfg6oPvUNQeh2GAp7GdgFiVR2bsSBVDgFQR8rwOroBk3l9EQmCmYEJT8c09Jou47xx8GUOoTkWqZmCiSUC7W7-BMQf7tqxG0oSP7gCeHIS2kKDwB7ZaB9CSVloSV-ni7j1Wsgv0ZBvlFkAHga6ZVuosQBQJoYvfJ8_GtMXbTO5YGU63SKJ9FD9h_JSiH0u5muNyg1x03quFejjcIrTfUZtlcc5AMwGqv4Lz0aLIb2sYc3uD_OVWR08Kj55Lluh4iKG4ypd_XmJin1OJOv1Mj1lCUl6MFpBFd0j86sZfTvhm4Xdq4EiburNRpxYJAktSSDs6JNqXFw5VxLHNdSvHwK55aEZSzm1Tv8P6EPzoH3ljD-3nUrRvvjM50BoYdJ04zFm3SFF6pl4-ujI145eTlMc6jsrN8Htpx7hIzmQiHeyKZrl6kUd3vyt8bcCJwIPDSbkk",
  "payload": ""
}
2022-10-16 18:36:05,144:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Date: Sun, 16 Oct 2022 16:36:05 GMT
Content-Type: application/json
Content-Length: 399
Connection: keep-alive
Boulder-Requester: 779240456
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: C878hjBP8F5f_WDpIjiIxyhYIFbaL_ztcibacv_kvJdP3JQ
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "identifier": {
    "type": "dns",
    "value": "mysubdomain.duckdns.org"
  },
  "status": "pending",
  "expires": "2022-10-23T14:29:47Z",
  "challenges": [
    {
      "type": "dns-01",
      "status": "pending",
      "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/165226777726/8G1Igw",
      "token": "0s04wNLACb51SY41UElNPTqF7KA7JKVb2hgZSU1xce4"
    }
  ],
  "wildcard": true
}
2022-10-16 18:36:05,145:DEBUG:acme.client:Storing nonce: C878hjBP8F5f_WDpIjiIxyhYIFbaL_ztcibacv_kvJdP3JQ
2022-10-16 18:36:05,146:INFO:certbot._internal.auth_handler:Performing the following challenges:
2022-10-16 18:36:05,146:INFO:certbot._internal.auth_handler:dns-01 challenge for mysubdomain.duckdns.org
2022-10-16 18:36:05,147:WARNING:certbot.plugins.dns_common:Unsafe permissions on credentials configuration file: /config/dns-conf/duckdns.ini
2022-10-16 18:36:05,148:WARNING:certbot.plugins.dns_common:Unsafe permissions on credentials configuration file: /config/dns-conf/duckdns.ini
2022-10-16 18:36:05,164:DEBUG:certbot._internal.error_handler:Encountered exception:
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/certbot_dns_duckdns/cert/client.py", line 82, in _perform
    txt_values = custom_resolver.resolve(duckdns_domain, "TXT")
  File "/usr/lib/python3.9/site-packages/dns/resolver.py", line 1090, in resolve
    (answer, done) = resolution.query_result(response, None)
  File "/usr/lib/python3.9/site-packages/dns/resolver.py", line 696, in query_result
    raise NoAnswer(response=answer.response)
dns.resolver.NoAnswer: The DNS response does not contain an answer to the question: mysubdomain.duckdns.org. IN TXT

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/certbot/_internal/auth_handler.py", line 86, in handle_authorizations
    resps = self.auth.perform(achalls)
  File "/usr/lib/python3.9/site-packages/certbot/plugins/dns_common.py", line 76, in perform
    self._perform(domain, validation_domain_name, validation)
  File "/usr/lib/python3.9/site-packages/certbot_dns_duckdns/cert/client.py", line 84, in _perform
    raise errors.PluginError(e)
certbot.errors.PluginError: The DNS response does not contain an answer to the question: mysubdomain.duckdns.org. IN TXT

2022-10-16 18:36:05,164:DEBUG:certbot._internal.error_handler:Calling registered functions
2022-10-16 18:36:05,164:INFO:certbot._internal.auth_handler:Cleaning up challenges
2022-10-16 18:36:05,843:DEBUG:certbot._internal.log:Exiting abnormally:
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/certbot_dns_duckdns/cert/client.py", line 82, in _perform
    txt_values = custom_resolver.resolve(duckdns_domain, "TXT")
  File "/usr/lib/python3.9/site-packages/dns/resolver.py", line 1090, in resolve
    (answer, done) = resolution.query_result(response, None)
  File "/usr/lib/python3.9/site-packages/dns/resolver.py", line 696, in query_result
    raise NoAnswer(response=answer.response)
dns.resolver.NoAnswer: The DNS response does not contain an answer to the question: mysubdomain.duckdns.org. IN TXT

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/bin/certbot", line 8, in <module>
    sys.exit(main())
  File "/usr/lib/python3.9/site-packages/certbot/main.py", line 19, in main
    return internal_main.main(cli_args)
  File "/usr/lib/python3.9/site-packages/certbot/_internal/main.py", line 1744, in main
    return config.func(config, plugins)
  File "/usr/lib/python3.9/site-packages/certbot/_internal/main.py", line 1591, in certonly
    lineage = _get_and_save_cert(le_client, config, domains, certname, lineage)
  File "/usr/lib/python3.9/site-packages/certbot/_internal/main.py", line 141, in _get_and_save_cert
    lineage = le_client.obtain_and_enroll_certificate(domains, certname)
  File "/usr/lib/python3.9/site-packages/certbot/_internal/client.py", line 530, in obtain_and_enroll_certificate
    cert, chain, key, _ = self.obtain_certificate(domains)
  File "/usr/lib/python3.9/site-packages/certbot/_internal/client.py", line 442, in obtain_certificate
    orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names)
  File "/usr/lib/python3.9/site-packages/certbot/_internal/client.py", line 510, in _get_order_and_authorizations
    authzr = self.auth_handler.handle_authorizations(orderr, self.config, best_effort)
  File "/usr/lib/python3.9/site-packages/certbot/_internal/auth_handler.py", line 86, in handle_authorizations
    resps = self.auth.perform(achalls)
  File "/usr/lib/python3.9/site-packages/certbot/plugins/dns_common.py", line 76, in perform
    self._perform(domain, validation_domain_name, validation)
  File "/usr/lib/python3.9/site-packages/certbot_dns_duckdns/cert/client.py", line 84, in _perform
    raise errors.PluginError(e)
certbot.errors.PluginError: The DNS response does not contain an answer to the question: mysubdomain.duckdns.org. IN TXT
2022-10-16 18:36:05,847:ERROR:certbot._internal.log:The DNS response does not contain an answer to the question: mysubdomain.duckdns.org. IN TXT
github-actions[bot] commented 1 year ago

Thanks for opening your first issue here! Be sure to follow the bug or feature issue templates!

nemchik commented 1 year ago

Can you try and set the environment variable:

- PROPAGATION=60

Or maybe 120.

I'm wondering if the default (which I believe is 30 second) isn't long enough.

peterNordin commented 1 year ago

I tried both 60 and 120, and verified that the env. variable was set in the container with docker exec -it swag printenv but sadly it did not help. I get the same errors in the log.

Regarding the /config/keys/cert.key write problem, I manually created /config/etc/letsencrypt/live/mysubdomain.duckdns.org directory, and then a certifiacte was generated and written. But when I visit my page it complains that the certificate is a self-signed one by Linuxserver.io, so it seems the letsencrypt one was not created in this case.

I have a feeling the two problems are related, but I am not sure.

nemchik commented 1 year ago

Ok, I've tried to reproduce this but I can't.

I did not have to manually create any folders. The init scripts do have some strange looking messages in them that I will investigate, but it worked.

peterNordin commented 1 year ago

OK, thank you for testing, I will keep looking at my setup and see if I can find anything that is odd. One thing I forgot to mention is that swag in this case is not reachable from the outside (public internet). It is used for my internal LAN-only containers. But I guess that should not be a problem since I use DNS validation (and it worked before)

nemchik commented 1 year ago

swag in this case is not reachable from the outside (public internet).

I tested without adding any ports to the docker container as well (pretty much self contained). Didn't have issues. I'd like to figure out what's happening and fix it, but I need to find a way to reproduce it.

adgoncal commented 1 year ago

I have the same issue as OP. I don't know if this makes a difference, but duckdns points to my internal LAN IP for the docker host instead of my WAN IP because I have no intention of exposing anything outside my LAN.

version: "3.9"

networks:
  default:
    name: swag
    driver: bridge
    ipam:
      config:
        - subnet: 172.52.0.0/24

services:

  swag:
    image: "linuxserver/swag:latest"
    container_name: "swag"
    cap_add:
      - NET_ADMIN
    restart: unless-stopped
    volumes:
      - ${DOCKERDIR}/swag:/config
      - ${DOCKERDIR}/swag/fail2ban/fail2ban.sqlite3:/dashboard/fail2ban.sqlite3:ro
      - ${DOCKERDIR}/swag/log/nginx:/dashboard/logs:ro
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - URL=mysubdomain.duckdns.org
      - VALIDATION=dns
      - DNSPLUGIN=duckdns
      - SUBDOMAINS=wildcard
      # - STAGING=true
      - DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-auto-reload
    ports:
      - 443:443
      - 80:80
$ docker --version
Docker version 20.10.20, build 9fdeb9c
$ docker-compose --version
docker-compose version 1.28.6, build 5db8d86f
$ uname -a
Linux mynas 4.19.0-21-amd64 #1 SMP Debian 4.19.249-2 (2022-06-30) x86_64 GNU/Linux

Everything works fine on linuxserver/swag:1.31.0-ls153 and the old environment variables, but it broke with linuxserver/swag:1.31.0-ls154.

peterNordin commented 1 year ago

I was finally able to solve the problem.

I turns out that I had set a local override for *.mysubdomain.duckdns.org in my router dns (OPNSens with Unbound) because if I am on my LAN why should I ask duckdns.org about the IP if I already know it. (This is a DNS entry pointing to a private LAN only ip 192.168.1.xxx)

If I disabled that dns override, I am able to get a certificate (I have only tested with a staging certificate, but it will likely work with a proper one to).

peterNordin commented 1 year ago

Now for my guess as to why:

If we look in the letsencrypt logs it tells us there is an exception at line 82 in the duckdns plugin for certbot. This is where the plugin requests the current TXT record before setting the new one for validation. So this happens before the PROPGATION (sleep) takes effect. I guess (I am very unsure) that the python dns package used by the duckdns plugin, ends up getting redirected to my local IP, instead of going to the actual duckdns server to get the TXT record.

adgoncal commented 1 year ago

I was finally able to solve the problem.

I turns out that I had set a local override for *.mysubdomain.dockdns.org in my router dns (OPNSenso with Unbound) because if I am on my LAN why should I ask duckdns.org about the IP if I already know it. (This is dns entry pointing to a private LAN only ip 192.168.1.xxx)

If I disabled that dns override, I am able to get a certificate (I have only tested with a staging certificate, but it will likely work with a proper one to).

That's interesting, I have a similar override in my network. I did not think it would cause any issues because it works fine on 1.31.0-ls153 and earlier.

Why should the DNS challenge require swag to reach that address? I want https in my LAN, but I also want to use my own DNS resolver (bind9 in this case, but it might as well be adguard home or pihole).

peterNordin commented 1 year ago

I have no idea what I am talking about, but it might have something to do with "chaining" whatever that is. I looked in the code here https://dnspython.readthedocs.io/en/latest/_modules/dns/resolver.html Search for: self.rrset = self.chaining_result.answer If rrset is NONE later in the code, the code will throw the exceptions we see in the letsencrypt logs

nemchik commented 1 year ago

@peterNordin excellent find! I don't have any record like this, so I didn't experience the same issue.

This tells me that the issue it's actually something we should open with the plugin to resolve this behavior.

@adgoncal this issue wouldn't have been present in ls153 and below because swag was using a custom implementation for duckdns at that point. ls154 introduced the duckdns plugin in swag.

peterNordin commented 1 year ago

I have been running this now since yesterday and restarted several times. Since I now have a valid certificate the odd problem with keys being re-generated on every restart and failures about writing to disk, no longer happens. I consider the issue solved.

I will consider opening an issue in the duckdns plugin repo.

adgoncal commented 1 year ago

I have been running this now since yesterday and restarted several times. Since I now have a valid certificate the odd problem with keys being re-generated on every restart and failures about writing to disk, no longer happens. I consider the issue solved.

I will consider opening an issue in the duckdns plugin repo.

Let's hope it does not cause any issues when it's time for certificate renewal... 🤞

saxicek commented 1 year ago

I tried both 60 and 120, and verified that the env. variable was set in the container with docker exec -it swag printenv but sadly it did not help. I get the same errors in the log.

Regarding the /config/keys/cert.key write problem, I manually created /config/etc/letsencrypt/live/mysubdomain.duckdns.org directory, and then a certifiacte was generated and written. But when I visit my page it complains that the certificate is a self-signed one by Linuxserver.io, so it seems the letsencrypt one was not created in this case.

I have a feeling the two problems are related, but I am not sure.

I also experienced the error req: Can't open "/config/keys/cert.key" for writing, No such file or directory which was caused by missing folders live/mydomain.com under /etc/letsencrypt. That causes other failures in Certbot, but all went smoothly once I did:

/etc/letsencrypt# mkdir -pv live/mydomain.com

@nemchik Do you know in which script these folders should be created?

nemchik commented 1 year ago

@nemchik Do you know in which script these folders should be created?

https://github.com/linuxserver/docker-baseimage-alpine-nginx/blob/7a87255ea6721832754881f3bca4745dda39654b/root/etc/cont-init.d/11-folders#L5 creates /config/keys https://github.com/linuxserver/docker-swag/blob/d00d2dbe95276f9ea67b4d5d3d6a7b0384538208/root/etc/cont-init.d/40-folders#L6 creates /config/etc/letsencrypt/renewal-hooks which is an adjacent folder to /config/etc/letsencrypt/live (the important thing here is the parent folder exists when it is needed below) https://github.com/linuxserver/docker-swag/blob/d00d2dbe95276f9ea67b4d5d3d6a7b0384538208/root/etc/cont-init.d/50-certbot#L188-L193 symlinks /etc/letsencrypt/live/domain.com to /config/etc/letsencrypt/live/domain.com (at this point, nothing exists at /etc/letsencrypt/live/domain.com, so the links is a dead link until the next part happens) https://github.com/linuxserver/docker-swag/blob/d00d2dbe95276f9ea67b4d5d3d6a7b0384538208/root/etc/cont-init.d/50-certbot#L262 creates /etc/letsencrypt/live/mydomain.com/all_the_cert_files


The root cause of the issue explained above was that both users who reported having a problem have (or had) a local DNS record configured in their router (or however they manage local DNS) pointing theirdomain.duckdns.org to a local IP address. The DNS plugin that is now used by SWAG is (unnecessarily) doing a DNS lookup which causes the request for a cert to fail. When it fails, it appears the folders get messed up. The best option from that point is to remove the local DNS record, delete /config/keys and /config/etc and restart the container. SWAG should get a new cert and recreate all the necessary folders at that point.

The actions happening in the plugin start here https://github.com/infinityofspace/certbot_dns_duckdns/blob/5679d3486a1797bd4f18f786b99756384cc3c80b/certbot_dns_duckdns/cert/client.py#L79 I believe there is an option to bypass it, but not via CLI parameters (which is how SWAG currently interacts with certbot and plugins). I'm still exploring options.

saxicek commented 1 year ago

Ok. My scenario is that I own domain with CloudFlare nameservers (no DuckDNS), but I use it for my home network, so I have DNS A record with IP address like 192.168.0.45. Therefore the issue with DNS lookup still applies.

nemchik commented 1 year ago

Interesting. I have not seen that issue yet with cloudflare. Can you try deleting /config/etc and /config/keys and restarting the container?

P.S. you might want to set STAGING=true while testing things to make sure it looks like a valid staging cert can be obtained, and then remove that to get a real working cert, just so you don't get rate limited while experimenting.

saxicek commented 1 year ago

As I wrote above - my issue was fixed when I created the folders manually with mkdir -pv live/mydomain.com. After that everything worked and Let's Encrypt certificate was issued (well, had to use also the PROPAGATION=60). Maybe if I created just the live dir it would also start working, not sure.

GeoCookie commented 1 year ago

I had the same problem after updating swag to version 1.31. On my side, I have a local DNS server that responds to queries from my LAN. This allows me to get local IPs when I'm at home.

I solved my problem by adding a dumb TXT entry on my DNS zone. With this entry, certbot is now able to pass the DNS challenge and renew my certificate. image

I really don't understand why, but it works 😊

nemchik commented 1 year ago

Please try running lspipepr/swag:1.32.0-pkg-96e270ce-pr-293, it should work with local DNS records.

You may need to delete your /config/keys and /config/etc folders for swag.

Ref: https://github.com/linuxserver/docker-swag/pull/293

GeoCookie commented 1 year ago

Hi @nemchik

I tried what you asked. This works for me (I can get a certificate) BUT the certificate covers my domain and not my subdomain as requested image

Also, I needed to increase the propagation time (30 seconds was not enough).

peterNordin commented 1 year ago

@nemchik I tried the new version and it works for me now with my local DNS override re-enabled in my router. Thanks for the fix. I removed my swag configs and restarted, (setup from scratch) and a cert was now successfully acquired as expected (after adding my duckdns token).