Certbot-auto - IOError: Unable to save to file


#1

Hi everybody,

Currently, I try to create a SSL certificate for one of my website with Let’s Encrypt and Certbot (Debian 8 / Apache). So, I’ve installed certbot with success and I’ve used this command:

./certbot-auto --apache certonly -d example.com

Unfortunately, I’ve got an unexplained error:

  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apac                                                                                                                     he/http_01.py", line 72, in perform
    self.configurator.save("HTTP Challenge", True)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apac                                                                                                                     he/augeas_configurator.py", line 150, in save
    self.aug.save()
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/augeas.py",                                                                                                                      line 488, in save
    raise IOError("Unable to save to file!")
IOError: Unable to save to file!
An unexpected error occurred:

What happened? I already run some tests but without success!

Have you got an idea to help me?

Best regards,


#2

As which user did you run certbot? Root? A ‘normal’ user? Does that user have write access to the webroot of the domains?


#3

I run certbot with root user. This user have write access to the webroot (/var/www/html/...)


#4

Could you post your log file from /var/log/letsencrypt?


#5

Here is my log file:

2018-04-08 08:39:45,715:DEBUG:certbot.main:certbot version: 0.23.0
2018-04-08 08:39:45,717:DEBUG:certbot.main:Arguments: ['--apache', '-d', 'example.com']
2018-04-08 08:39:45,717:DEBUG:certbot.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#apache,PluginEntryPoint#manual,PluginEntryPoint#nginx,PluginEntryPoint#null,PluginEntryPoint#standalo
ne,PluginEntryPoint#webroot)
2018-04-08 08:39:45,808:DEBUG:certbot.log:Root logging level set at 20
2018-04-08 08:39:45,811:INFO:certbot.log:Saving debug log to /var/log/letsencrypt/letsencrypt.log
2018-04-08 08:39:45,816:DEBUG:certbot.plugins.selection:Requested authenticator apache and installer apache
2018-04-08 08:39:46,292:DEBUG:certbot_apache.configurator:Apache version is 2.4.10
2018-04-08 08:39:47,789:DEBUG:certbot.plugins.selection:Single candidate plugin: * apache
Description: Apache Web Server plugin - Beta
Interfaces: IAuthenticator, IInstaller, IPlugin
Entry point: apache = certbot_apache.entrypoint:ENTRYPOINT
Initialized: <certbot_apache.override_debian.DebianConfigurator object at 0x7f1c857a9850>
Prep: True
2018-04-08 08:39:47,795:DEBUG:certbot.plugins.selection:Single candidate plugin: * apache
Description: Apache Web Server plugin - Beta
Interfaces: IAuthenticator, IInstaller, IPlugin
Entry point: apache = certbot_apache.entrypoint:ENTRYPOINT
Initialized: <certbot_apache.override_debian.DebianConfigurator object at 0x7f1c857a9850>
Prep: True
2018-04-08 08:39:47,796:DEBUG:certbot.plugins.selection:Selected authenticator <certbot_apache.override_debian.DebianConfigurator object at 0x7f1c857a9850> and installer <certbot_apache.override_de
bian.DebianConfigurator object at 0x7f1c857a9850>
2018-04-08 08:39:47,796:INFO:certbot.plugins.selection:Plugins selected: Authenticator apache, Installer apache
2018-04-08 08:39:47,819:DEBUG:certbot.main:Picked account: <Account(RegistrationResource(body=Registration(status=None, terms_of_service_agreed=None, contact=(u'mailto:amatukami@hotmail.fr',), agre
ement=u'https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf', key=JWKRSA(key=<ComparableRSAKey(<cryptography.hazmat.backends.openssl.rsa._RSAPublicKey object at 0x7f1c857a4310>)>)), u
ri=u'https://acme-v01.api.letsencrypt.org/acme/reg/32603372', new_authzr_uri=u'https://acme-v01.api.letsencrypt.org/acme/new-authz', terms_of_service=u'https://letsencrypt.org/documents/LE-SA-v1.2-
November-15-2017.pdf'), a156b15c94dfc8c4ced68f21658dcd85, Meta(creation_host=u'89df7a798ebb', creation_dt=datetime.datetime(2018, 4, 7, 10, 58, 3, tzinfo=<UTC>)))>
2018-04-08 08:39:47,824:DEBUG:acme.client:Sending GET request to https://acme-v01.api.letsencrypt.org/directory.
2018-04-08 08:39:47,839:DEBUG:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
2018-04-08 08:39:48,135:DEBUG:requests.packages.urllib3.connectionpool:https://acme-v01.api.letsencrypt.org:443 "GET /directory HTTP/1.1" 200 658
2018-04-08 08:39:48,138:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Content-Type: application/json
Content-Length: 658
Replay-Nonce: ZhT6I1rSDS8vgkYEB_11Ws5pAQ6XY9nlUUsJWhcJxg0
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800
Expires: Sun, 08 Apr 2018 08:39:48 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Sun, 08 Apr 2018 08:39:48 GMT
Connection: keep-alive

{
  "cRm1SaSSzUQ": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
  "key-change": "https://acme-v01.api.letsencrypt.org/acme/key-change",
  "meta": {
    "caaIdentities": [
      "letsencrypt.org"
    ],
    "terms-of-service": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf",
    "website": "https://letsencrypt.org"
  },
  "new-authz": "https://acme-v01.api.letsencrypt.org/acme/new-authz",
  "new-cert": "https://acme-v01.api.letsencrypt.org/acme/new-cert",
  "new-reg": "https://acme-v01.api.letsencrypt.org/acme/new-reg",
  "revoke-cert": "https://acme-v01.api.letsencrypt.org/acme/revoke-cert"
}
2018-04-08 08:39:48,141:INFO:certbot.main:Obtaining a new certificate
2018-04-08 08:39:48,614:DEBUG:certbot.crypto_util:Generating key (2048 bits): /etc/letsencrypt/keys/0012_key-certbot.pem
2018-04-08 08:39:48,624:DEBUG:certbot.crypto_util:Creating CSR: /etc/letsencrypt/csr/0012_csr-certbot.pem
2018-04-08 08:39:48,626:DEBUG:acme.client:Requesting fresh nonce
2018-04-08 08:39:48,626:DEBUG:acme.client:Sending HEAD request to https://acme-v01.api.letsencrypt.org/acme/new-authz.
2018-04-08 08:39:48,858:DEBUG:requests.packages.urllib3.connectionpool:https://acme-v01.api.letsencrypt.org:443 "HEAD /acme/new-authz HTTP/1.1" 405 0
2018-04-08 08:39:48,860:DEBUG:acme.client:Received response:
HTTP 405
Server: nginx
Content-Type: application/problem+json
Content-Length: 91
Allow: POST
Replay-Nonce: x5a1DIYAcqKQaPrSfAGx5Q198KzpeKbqgW_mMcB6U40
Expires: Sun, 08 Apr 2018 08:39:48 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Sun, 08 Apr 2018 08:39:48 GMT
Connection: keep-alive


2018-04-08 08:39:48,860:DEBUG:acme.client:Storing nonce: x5a1DIYAcqKQaPrSfAGx5Q198KzpeKbqgW_mMcB6U40
2018-04-08 08:39:48,861:DEBUG:acme.client:JWS payload:
{
  "identifier": {
    "type": "dns",
    "value": "example.com"
  },
  "resource": "new-authz"
}
2018-04-08 08:39:48,907:DEBUG:acme.client:Sending POST request to https://acme-v01.api.letsencrypt.org/acme/new-authz:
{
  "protected": "eyJub25jZSI6ICJ4NWExRElZQWNxS1FhUHJTZkFHeDVRMTk4S3pwZUticWdXX21NY0I2VTQwIiwgImFsZyI6ICJSUzI1NiIsICJqd2siOiB7ImUiOiAiQVFBQiIsICJrdHkiOiAiUlNBIiwgIm4iOiAibjdOa1pYalhzeTJYc2pTbHliZWpmV
GxwcGlUZEh6bWZPcldZckp1UWxSMEZWWGZ3VG5sd1Z2SnVURC1DeURadDd6amd3bkQxQmgzX0JWaEJWVDRIRURvUDBTNlZLX0dhd1RTZFFsTmpFdGtkMGp6RDBqMFd2bzc0eDdkbzJudkVRanZiRnMxNmZVRVAyWWNRaU1JdEJ6d0J3TmUxMnJmdjVNQy1KVmw3UF
VwbFllMDNRSmltdnFBbjJPeDFnY1R6dWxfbktUOGdvS05YODVPbTVuVFE2akhXR2lXV0FEN2ZJbmNCS0VyVW44NHJaZ24waWdONWtjTGtOZUNxZ1gxSmZqa082VzhyRE1rRmZlNEZrOUxVMzVjdEpkbnMzQU1RbG1hUEpuN1pFOTBJOWEwaXF4NEdqZUhOQXZCZFd
YR1NDaWRoRmJWYTFfWk1tdC12aXk5TF80V3B1czlFTXhQXzUweU9qbFgzamFKa1d5dmhPTVQzWHdmeTM5T2kzYVJYSjJ0WlQtQ1laejliekxybi02MEpjRHlqdGN6VDFNeGwyRmhnWk9fUF9INFlHN3M2dm1rZTA2dEF3bGR3ZnlpX0l2Q3JYREpjelpJS01jejVP
NFh2dk1aNXZDUGpXOHdGMmFqQ0h5TkZHb1c5QmV1U0ZsYmNNOUNJTGhFOEZ6ejhDb25DRjZseGpuZWxpX1M1b3ZxM21CWEpyMS1md3dNdlo1QTVCcHV0WlpVNU1tTnkyb2dGSFNSbDdRelhiTzNHMnFDaFlWU0FPajJwTVB6TGJnbWJXQ0pZa0UwbnJZNUVRMDVxT
3FJMnJmekRqMUFiZ1RTZEduZ3ZQbXRxZ1hSUDViNmVDcEtOc29tNW5zeUV5bkE4cld2ZWM0RTg5OWJnQ3RMOUZ4Z2NxTk0ifX0",
  "payload": "ewogICJpZGVudGlmaWVyIjogewogICAgInR5cGUiOiAiZG5zIiwgCiAgICAidmFsdWUiOiAid2Frb25kYS5ndXJ1IgogIH0sIAogICJyZXNvdXJjZSI6ICJuZXctYXV0aHoiCn0",
  "signature": "iAvZ4kAYLqNUBxYbwaKHsoH5F68XvDQvx3fIWngkXOwmzmnhFPc9hzOG08nI0UxBrLNsLpbV6pRq-2ORO7zrf0RXyAEoK-yViDthbYGCYGB86I7UYy4Z6S0ehUlPzytWOY0Xr2I1PwQs2cB879bNS4jwhrtorATt3N9Hob52ilbEP0cNXhS93
qa1UaWr3-nIQ3L5GBa0eWVphYxT_IJGlDNUcZXT2F_wxxwmZ46vMYLxFdsKIKTTVa4jaYJE3JwVyoTgrRfHmCbFy0aVCKt86pku4jH3U4xv3e5xxvbT08XFemgAioi3uSDU1va5oagcj3RC1qj7hcg-eobP6bz7TuiqfYIG5wlVQU3ZFo7ZsnGly8nRXnELdIJxZk
ysOuxWTpA-EkCfEBUODa6SaH00zSz8CLJludnDlB3LKIS1-lOtM1N_BSFfT4bGnf_EkV0cLgNH_Ef1BCni6buP-IbzUvLwkV-oPPjOE21rnt6W-rHyGbDz7od3QLqhJ1GGmQY8Z-oJL6z9abflq4d32WhCr_EIqKE0214z1s3Cc1GOBfJYVwg0pYmUzsjC17Waq91
J003AGoYjt7hy68MWaacJlm-TqRIMp1UdMpFhdKSv6QuHgL-g-USjdq_1PEn1li0cVmfIQARywXvYtheVRpTfELWD4ohq5YrBgA5lJcIZyZ8"
}
2018-04-08 08:39:49,111:DEBUG:requests.packages.urllib3.connectionpool:https://acme-v01.api.letsencrypt.org:443 "POST /acme/new-authz HTTP/1.1" 201 717
2018-04-08 08:39:49,113:DEBUG:acme.client:Received response:
HTTP 201
Server: nginx
Content-Type: application/json
Content-Length: 717
Boulder-Requester: 32603372
Link: <https://acme-v01.api.letsencrypt.org/acme/new-cert>;rel="next"
Location: https://acme-v01.api.letsencrypt.org/acme/authz/gH6VJV7jdxpFCrm3mjc589wD2kV7jSc_7ywJUnCN6A4
Replay-Nonce: -bY2PFKFBxUaOnaZr-oEzdL3lSjgJ9tP_PxoX4fPtro
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800
Expires: Sun, 08 Apr 2018 08:39:49 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Sun, 08 Apr 2018 08:39:49 GMT
Connection: keep-alive

{
  "identifier": {
    "type": "dns",
    "value": "example.com"
  },
  "status": "pending",
  "expires": "2018-04-14T11:28:54Z",
  "challenges": [
    {
      "type": "dns-01",
      "status": "pending",
      "uri": "https://acme-v01.api.letsencrypt.org/acme/challenge/gH6VJV7jdxpFCrm3mjc589wD2kV7jSc_7ywJUnCN6A4/4124451627",
      "token": "5agBI_CCpt0P6LmIKTXwUNWTGUfPJCZAzJpG9ivNWlk"
    },
    {
      "type": "http-01",
      "status": "pending",
      "uri": "https://acme-v01.api.letsencrypt.org/acme/challenge/gH6VJV7jdxpFCrm3mjc589wD2kV7jSc_7ywJUnCN6A4/4124451628",
      "token": "JWD0kZT9S35bKZXWUls_Srur53W3O8q101kkRBMBUi8"
    }
  ],
  "combinations": [
    [
      1
    ],
    [
      0
    ]
  ]
}
2018-04-08 08:39:49,114:DEBUG:acme.client:Storing nonce: -bY2PFKFBxUaOnaZr-oEzdL3lSjgJ9tP_PxoX4fPtro
2018-04-08 08:39:49,116:INFO:certbot.auth_handler:Performing the following challenges:
2018-04-08 08:39:49,118:INFO:certbot.auth_handler:http-01 challenge for example.com
2018-04-08 08:39:49,271:DEBUG:certbot_apache.http_01:Adding a temporary challenge validation Include for name: example.com in: /etc/apache2/sites-enabled/vh.conf
2018-04-08 08:39:49,272:DEBUG:certbot_apache.http_01:writing a pre config file with text:
         RewriteEngine on
        RewriteRule ^/\.well-known/acme-challenge/([A-Za-z0-9-_=]+)$ /var/lib/letsencrypt/http_challenges/$1 [END]

2018-04-08 08:39:49,273:DEBUG:certbot_apache.http_01:writing a post config file with text:
         <Directory /var/lib/letsencrypt/http_challenges>
            Require all granted
        </Directory>
        <Location /.well-known/acme-challenge>
            Require all granted
        </Location>

2018-04-08 08:39:49,315:DEBUG:certbot.reverter:Creating backup of /etc/apache2/sites-enabled/vh.conf
2018-04-08 08:39:49,369:DEBUG:certbot.error_handler:Encountered exception:
Traceback (most recent call last):
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/auth_handler.py", line 73, in handle_authorizations
    resp = self._solve_challenges(aauthzrs)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/auth_handler.py", line 124, in _solve_challenges
    resp = self.auth.perform(all_achalls)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apache/configurator.py", line 2101, in perform
    http_response = http_doer.perform()
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apache/http_01.py", line 72, in perform
    self.configurator.save("HTTP Challenge", True)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apache/augeas_configurator.py", line 150, in save
    self.aug.save()
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/augeas.py", line 488, in save
    raise IOError("Unable to save to file!")
IOError: Unable to save to file!

2018-04-08 08:39:49,369:DEBUG:certbot.error_handler:Calling registered functions
2018-04-08 08:39:49,370:INFO:certbot.auth_handler:Cleaning up challenges
2018-04-08 08:39:50,590:DEBUG:certbot.log:Exiting abnormally:
Traceback (most recent call last):
  File "/opt/eff.org/certbot/venv/bin/letsencrypt", line 11, in <module>
    sys.exit(main())
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/main.py", line 1266, in main
    return config.func(config, plugins)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/main.py", line 1157, in certonly
    lineage = _get_and_save_cert(le_client, config, domains, certname, lineage)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/main.py", line 118, in _get_and_save_cert
    lineage = le_client.obtain_and_enroll_certificate(domains, certname)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/client.py", line 350, in obtain_and_enroll_certificate
    cert, chain, key, _ = self.obtain_certificate(domains)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/client.py", line 294, in obtain_certificate
    orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/client.py", line 330, in _get_order_and_authorizations
    authzr = self.auth_handler.handle_authorizations(orderr, best_effort)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/auth_handler.py", line 73, in handle_authorizations
    resp = self._solve_challenges(aauthzrs)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot/auth_handler.py", line 124, in _solve_challenges
    resp = self.auth.perform(all_achalls)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apache/configurator.py", line 2101, in perform
    http_response = http_doer.perform()
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apache/http_01.py", line 72, in perform
    self.configurator.save("HTTP Challenge", True)
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/certbot_apache/augeas_configurator.py", line 150, in save
    self.aug.save()
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/site-packages/augeas.py", line 488, in save
    raise IOError("Unable to save to file!")
IOError: Unable to save to file!
2018-04-08 08:39:50,593:ERROR:certbot.log:An unexpected error occurred:

#6

@bmw, can you understand why this could have happened?


#7

Could you verify a few things to help us debug the problem:

  • That you have diskspace available for /var/lib/letsencrypt and that the path is writable by root user. Certbot should create a directory called http_challenges here, and deploy a challenge token file in it.
  • That /etc/apache2/sites-enabled/vh.conf or the file that the possible symlink points to is writable by root.

#8

Thank you for your help.

I check and I’ve enough diskspace available. Further, the path /var/lib/letsencrypt is writable because after launching command line, the directory http_challenges is created.

The file /etc/apache2/sites-enabled/vh.conf seems to have good rights for root.

I also tried to use sudo. But, the result is the same.


#9

You could try record an strace of the entire Certbot session and try locate where the failing I/O is happening.

strace -f -o strace.out ./certbot-auto --apache certonly -d example.com --staging

Careful posting the strace.out, the earlier parts of it may contain your ACME secret key.

In there you should find some calls that look like (except /var/lib/letsencrypt/http_challenges, not /tmp):

50350 18373 stat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=36864, ...}) = 0
50351 18373 mkdir("/", 0755)                  = -1 EEXIST (File exists)
50352 18373 mkdir("/tmp", 0755)               = -1 EEXIST (File exists)
50353 18373 mkdir("/tmp/.well-known", 0755)   = 0
50354 18373 chown("/tmp/.well-known", 0, 0)   = 0
50355 18373 mkdir("/tmp/.well-known/acme-challenge", 0755) = 0
50356 18373 chown("/tmp/.well-known/acme-challenge", 0, 0) = 0

...

50371 18373 openat(AT_FDCWD, "/tmp/.well-known/acme-challenge/4mLVW9emBJwByYjtRcMStVbHNkjUXj9rlFGJco9Es2U", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 8
50372 18373 fstat(8, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
50373 18373 fstat(8, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
50374 18373 write(8, "4mLVW9emBJwByYjt"..., 87) = 87

This would help identify what the exact failure is, since Augeas is swallowing the error.


#10

Finally, I’ve found another solution that works fine!

I used the following command line:

./certbot-auto certonly --manual --preferred-challenges=http -d example.com --manual-auth-hook /path/to/hook/authenticator.sh --manual-cleanup-hook /path/to/hook/cleanup.sh

authenticator.sh

#!/bin/bash

echo $CERTBOT_VALIDATION > /path/to/acme-challenges/$CERTBOT_TOKEN

cleanup.sh

rm -f /path/to/acme-challenges/$CERTBOT_TOKEN

Best regards


#11

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.