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

failed to parse fullchain into cert and chain: less than 2 certificates in chain #83

Closed
jacekjaros opened this issue Jan 17, 2022 · 4 comments

Comments

@jacekjaros
Copy link

hi,
i get following error when try to request cert:

# certbot certonly --standalone --server http://ca.dmz:8000/django_ca/acme/directory/ -d 'web1.dmz'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Requesting a certificate for web1.dmz
Performing the following challenges:
http-01 challenge for web1.dmz
Waiting for verification...
Cleaning up challenges
failed to parse fullchain into cert and chain: less than 2 certificates in chain

less /var/log/letsencrypt/letsencrypt.log

---
2022-01-17 19:17:27,183:DEBUG:certbot._internal.log:Exiting abnormally:
Traceback (most recent call last):
  File "/usr/bin/certbot", line 33, in <module>
    sys.exit(load_entry_point('certbot==1.12.0', 'console_scripts', 'certbot')())
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 15, in main
    return internal_main.main(cli_args)
  File "/usr/lib/python3/dist-packages/certbot/_internal/main.py", line 1413, in main
    return config.func(config, plugins)
  File "/usr/lib/python3/dist-packages/certbot/_internal/main.py", line 1293, in certonly
    lineage = _get_and_save_cert(le_client, config, domains, certname, lineage)
  File "/usr/lib/python3/dist-packages/certbot/_internal/main.py", line 134, in _get_and_save_cert
    lineage = le_client.obtain_and_enroll_certificate(domains, certname)
  File "/usr/lib/python3/dist-packages/certbot/_internal/client.py", line 441, in obtain_and_enroll_certificate
    cert, chain, key, _ = self.obtain_certificate(domains)
  File "/usr/lib/python3/dist-packages/certbot/_internal/client.py", line 390, in obtain_certificate
    cert, chain = self.obtain_certificate_from_csr(csr, orderr)
  File "/usr/lib/python3/dist-packages/certbot/_internal/client.py", line 299, in obtain_certificate_from_csr
    cert, chain = crypto_util.cert_and_chain_from_fullchain(fullchain)
  File "/usr/lib/python3/dist-packages/certbot/crypto_util.py", line 549, in cert_and_chain_from_fullchain
    raise errors.Error("failed to parse fullchain into cert and chain: " +
certbot.errors.Error: failed to parse fullchain into cert and chain: less than 2 certificates in chain
2022-01-17 19:17:27,190:ERROR:certbot._internal.log:failed to parse fullchain into cert and chain: less than 2 certificates in chain

my setup:

root@WEB1:~# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 11 (bullseye)
Release:        11
Codename:       bullseye
root@WEB1:~# dpkg -l certbot
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version      Architecture Description
+++-==============-============-============-=================================================
ii  certbot        1.12.0-2     all          automatically configure HTTPS using Let's Encrypt

CA server is latest django-ca running on top of Python 3.9.
in my CA i use only root ca (there are no intermediate ca)

@mathiasertl
Copy link
Owner

Hi @jacekjaros,

Thanks for your report!

in my CA i use only root ca (there are no intermediate ca)

Hmm! I must say this is something that has never occurred to me to try. I can't be sure without checking of course, but this might also be an issue in certbot itself.

I will definitely try to reproduce this right away. I would appreciate the following additional info:

  • certbot, Django and cryptography version?
  • Do webserver or Celery broker throw any errors anywhere?
  • The request log of the webserver - it's sufficient to get the request URL paths (so everything like /django_ca/acme/...), request body should not be needed.
  • The output of manage.py list_cas and manage.py view_ca your-serial-here?

Thanks, Mat

@mathiasertl
Copy link
Owner

FWIW, I'm not aware of any restriction in RFC 8555 that would forbid such a setup. So it's definitely a bug in either django-ca or certbot.

@jacekjaros
Copy link
Author

hi @mathiasertl

CA:

root@CA:~# /opt/CA/bin/python --version
Python 3.9.8

root@CA:~# /opt/CA/bin/python -m pip list
Package               Version
--------------------- ---------
acme                  1.22.0
asgiref               3.4.1
asn1crypto            1.4.0
certifi               2021.10.8
cffi                  1.15.0
charset-normalizer    2.0.10
configparser          5.2.0
cryptography          36.0.1
Django                4.0.1
django-ca             1.19.1
django-object-actions 3.1.0
dnspython             2.1.0
idna                  3.3
josepy                1.11.0
packaging             21.3
pip                   21.3.1
psycopg2-binary       2.9.3
pycparser             2.21
pyOpenSSL             21.0.0
pyparsing             3.0.6
pyRFC3339             1.1
pytz                  2021.3
requests              2.27.1
requests-toolbelt     0.9.1
setuptools            60.5.0
six                   1.16.0
sqlparse              0.4.2
urllib3               1.26.8

client:

root@WEB1:~# python3 --version
Python 3.9.2
root@WEB1:~# certbot --version
certbot 1.12.0
root@WEB1:~# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 11 (bullseye)
Release:        11
Codename:       bullseye

Clients are talking directly to Django - i have no middle ware.

django access log:

Jan 17 18:10:25 CA python3[15720]: [17/Jan/2022 18:10:25] "GET /django_ca/acme/directory/ HTTP/1.1" 200 557
Jan 17 18:10:25 CA python3[15720]: [17/Jan/2022 18:10:25] "HEAD /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/new-nonce/ HTTP/1.1" 200 0
Jan 17 18:10:25 CA python3[15720]: [17/Jan/2022 18:10:25] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/new-order/ HTTP/1.1" 201 348
Jan 17 18:10:25 CA python3[15720]: [17/Jan/2022 18:10:25] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/authz/m1evIN3NEwVo/ HTTP/1.1" 200 615
Jan 17 18:10:25 CA python3[15720]: [17/Jan/2022 18:10:25] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/chall/P1LYlR36BN6E/ HTTP/1.1" 200 247
Jan 17 18:10:27 CA python3[15720]: [17/Jan/2022 18:10:27] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/authz/m1evIN3NEwVo/ HTTP/1.1" 200 403
Jan 17 18:10:27 CA python3[15720]: [17/Jan/2022 18:10:27] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/order/T05UZP89lCIR/finalize/ HTTP/1.1" 200 232
Jan 17 18:10:28 CA python3[15720]: [17/Jan/2022 18:10:28] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/order/T05UZP89lCIR/ HTTP/1.1" 200 339
Jan 17 18:10:28 CA python3[15720]: [17/Jan/2022 18:10:28] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/cert/zrdBXVX3y14q/ HTTP/1.1" 200 3687
Jan 17 18:11:29 CA python3[15720]: [17/Jan/2022 18:11:29] "GET /django_ca/acme/directory/ HTTP/1.1" 200 557
Jan 17 18:11:29 CA python3[15720]: [17/Jan/2022 18:11:29] "HEAD /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/new-nonce/ HTTP/1.1" 200 0
Jan 17 18:11:29 CA python3[15720]: [17/Jan/2022 18:11:29] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/new-order/ HTTP/1.1" 201 348
Jan 17 18:11:29 CA python3[15720]: [17/Jan/2022 18:11:29] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/authz/cD3GIXySGHXa/ HTTP/1.1" 200 615
Jan 17 18:11:30 CA python3[15720]: [17/Jan/2022 18:11:30] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/chall/r94ZVVDWdnIC/ HTTP/1.1" 200 247
Jan 17 18:11:31 CA python3[15720]: [17/Jan/2022 18:11:31] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/authz/cD3GIXySGHXa/ HTTP/1.1" 200 403
Jan 17 18:11:31 CA python3[15720]: [17/Jan/2022 18:11:31] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/order/RWflL9KLvdVJ/finalize/ HTTP/1.1" 200 232
Jan 17 18:11:32 CA python3[15720]: [17/Jan/2022 18:11:32] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/order/RWflL9KLvdVJ/ HTTP/1.1" 200 339
Jan 17 18:11:32 CA python3[15720]: [17/Jan/2022 18:11:32] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/cert/8D5yq5mACeYk/ HTTP/1.1" 200 3687
Jan 17 18:17:23 CA python3[15720]: [17/Jan/2022 18:17:23] "GET /django_ca/acme/directory/ HTTP/1.1" 200 557
Jan 17 18:17:23 CA python3[15720]: [17/Jan/2022 18:17:23] "HEAD /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/new-nonce/ HTTP/1.1" 200 0
Jan 17 18:17:23 CA python3[15720]: [17/Jan/2022 18:17:23] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/new-order/ HTTP/1.1" 201 348
Jan 17 18:17:23 CA python3[15720]: [17/Jan/2022 18:17:23] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/authz/ssEFImQGhnGo/ HTTP/1.1" 200 615
Jan 17 18:17:23 CA python3[15720]: [17/Jan/2022 18:17:23] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/chall/zqQkf4L2NBif/ HTTP/1.1" 200 247
Jan 17 18:17:25 CA python3[15720]: [17/Jan/2022 18:17:25] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/authz/ssEFImQGhnGo/ HTTP/1.1" 200 403
Jan 17 18:17:25 CA python3[15720]: [17/Jan/2022 18:17:25] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/order/3qijN1JTiCRA/finalize/ HTTP/1.1" 200 232
Jan 17 18:17:26 CA python3[15720]: [17/Jan/2022 18:17:26] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/order/3qijN1JTiCRA/ HTTP/1.1" 200 339
Jan 17 18:17:26 CA python3[15720]: [17/Jan/2022 18:17:26] "POST /django_ca/acme/24C2611A2189967175A7C04E9D098BFF3082BAEE/cert/sehjVrW90Lui/ HTTP/1.1" 200 3687
root@CA:~# /opt/CA/bin/python /opt/CA/ca/manage.py list_cas
24:C2:61:1A:21:89:96:71:75:A7:C0:4E:9D:09:8B:FF:30:82:BA:EE - JarosOrgPlInfraCA
root@CA:~# /opt/CA/bin/python /opt/CA/ca/manage.py view_ca 24:C2:61:1A:21:89:96:71:75:A7:C0:4E:9D:09:8B:FF:30:82:BA:EE
JarosOrgPlInfraCA (enabled):
* Serial: 24:C2:61:1A:21:89:96:71:75:A7:C0:4E:9D:09:8B:FF:30:82:BA:EE
* Path to private key:
  /opt/CA/ca/files/ca/24C2611A2189967175A7C04E9D098BFF3082BAEE.key
* Is a root CA.
* Has no children.
* Distinguished Name: /C=PL/L=Krakow/O=jaros.org.pl/OU=infra/CN=ca.jaros.org.pl
* Maximum levels of sub-CAs (pathlen): 0
* HPKP pin: kHNjkK+bk9Eh1mTs3IjW9uq6RAUbU2JyYNZyieAf4fo=

ACMEv2 support:
* Enabled: True
* Requires contact: True

X509 v3 certificate extensions for CA:
AuthorityKeyIdentifier:
    * KeyID: 28:03:D1:73:06:51:D1:60:BF:D5:07:F4:AE:9D:9D:9A:77:B4:82:69
BasicConstraints (critical):
    CA:TRUE, pathlen:0
KeyUsage (critical):
    * cRLSign
    * keyCertSign
SubjectKeyIdentifier:
    28:03:D1:73:06:51:D1:60:BF:D5:07:F4:AE:9D:9D:9A:77:B4:82:69

X509 v3 certificate extensions for signed certificates:
* Certificate Revokation List (CRL): None
* Issuer URL: None
* OCSP URL: None
* Issuer Alternative Name: None

-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----

i have other box based on Ubuntu 20.04 and Certbot 0.40.0-1ubuntu0.1 - on that box everything is working fine.

@mathiasertl
Copy link
Owner

Hi @jacekjaros,

The issue can easily be reproduced by following Testing ACMEv2, but instead of creating an intermediate CA for ACME, use the root CA instead.

Turns out that this is actually somewhat a bug in certbot, which expects a PEM certificate chain to end with a newline. That is probably commonly the case, but is not strictly required in RFC 7468. ef3e53d appends a newline to the chain, thus fixing the issue on our side.

kr, Mat

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

No branches or pull requests

2 participants