Acme.sh - chown: changing ownership not permitted

I want to install Nextcloud and OnlyOffice on a home server and secure both with SSL. I want to be able to reach Nextcloud at https://mydomain.de and Onlyoffice at https://office.mydomain.de. I have already applied for, received and installed the certificate for mydomain.de with acme.sh.
Since three days I am trying to get the certificate for the subdomain office.mydomain.de. - Ok, at night I have slept ;-).
I start the script acme.sh with the user account “acmeuser” with the following call

acme.sh --debug --issue -d office.mydomain.de --keylength 4096 -w /var/www/letsencrypt --key-file /etc/letsencrypt/rsa-certs/privkey.pem --ca-file /etc/letsencrypt/rsa-certs/chain.pem --cert-file /etc/letsencrypt/rsa-certs/cert.pem --fullchain-file /etc/letsencrypt/rsa-certs/fullchain.pem

The script is starting, gets a token, and writes it to /var/www/letsencrypt. But when it is trying to change the owner and group of the token, I get the following error message:

Changing owner/group of .well-known to www-data:www-data
chown: changing ownership of '/var/www/letsencrypt/.well-known/acme-challenge/sQmOWXmtDUB3g_klqNxfkIMG5uRrn2QGMurbnHvLc5s': Operation not permitted

The account “acmeuser” is member of the group “www-data” who is owner of the directory /var/www/letsencrypt.

root@mydomain:/var/www/letsencrypt/.well-known/acme-challenge# ls -l
=>
-rw-rw-r-- 1 acmeuser acmeuser 87 Jan 14 17:52 sQmOWXmtDUB3g_klqNxfkIMG5uRrn2QGMurbnHvLc5s

These are the diagnostic informations:

openssl:openssl
OpenSSL 1.1.1c  28 May 2019
apache:
apache doesn't exists.
nginx:
nginx version: nginx/1.16.1 (Ubuntu)
built with OpenSSL 1.1.1c  28 May 2019
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-dvyH9w/nginx-1.16.1=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-compat --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-headers-more-filter --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-cache-purge --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-ndk --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-echo --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-fancyindex --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/nchan --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-lua --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/rtmp --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-uploadprogress --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-subs-filter --add-dynamic-module=/build/nginx-dvyH9w/nginx-1.16.1/debian/modules/http-geoip2
socat:
socat by Gerhard Rieger and contributors - see www.dest-unreach.org
Usage:
socat [options] <bi-address> <bi-address>
   options:
      -V     print version and feature information to stdout, and exit
      -h|-?  print a help text describing command line options and addresses
      -hh    like -h, plus a list of all common address option names
      -hhh   like -hh, plus a list of all available address option names
      -d     increase verbosity (use up to 4 times; 2 are recommended)
      -D     analyze file descriptors before loop
      -ly[facility]  log to syslog, using facility (default is daemon)
      -lf<logfile>   log to file
      -ls            log to stderr (default if no other log)
      -lm[facility]  mixed log mode (stderr during initialization, then syslog)
      -lp<progname>  set the program name used for logging
      -lu            use microseconds for logging timestamps
      -lh            add hostname to log messages
      -v     verbose data traffic, text
      -x     verbose data traffic, hexadecimal
      -b<size_t>     set data buffer size (8192)
      -s     sloppy (continue on error)
      -t<timeout>    wait seconds before closing second channel
      -T<timeout>    total inactivity timeout in seconds
      -u     unidirectional mode (left to right)
      -U     unidirectional mode (right to left)
      -g     do not check option groups
      -L <lockfile>  try to obtain lock, or fail
      -W <lockfile>  try to obtain lock, or wait
      -4     prefer IPv4 if version is not explicitly specified
      -6     prefer IPv6 if version is not explicitly specified
   bi-address:
      pipe[,<opts>]     groups=FD,FIFO
      <single-address>!!<single-address>
      <single-address>
   single-address:
      <address-head>[,<opts>]
   address-head:
      abstract-client:<filename>        groups=FD,SOCKET,RETRY,UNIX
      abstract-connect:<filename>       groups=FD,SOCKET,RETRY,UNIX
      abstract-listen:<filename>        groups=FD,SOCKET,LISTEN,CHILD,RETRY,UNIX
      abstract-recv:<filename>  groups=FD,SOCKET,RETRY,UNIX
      abstract-recvfrom:<filename>      groups=FD,SOCKET,CHILD,RETRY,UNIX
      abstract-sendto:<filename>        groups=FD,SOCKET,RETRY,UNIX
      create:<filename> groups=FD,REG,NAMED
      exec:<command-line>       groups=FD,FIFO,SOCKET,EXEC,FORK,TERMIOS,PTY,PARENT,UNIX
      fd:<num>  groups=FD,FIFO,CHR,BLK,REG,SOCKET,TERMIOS,UNIX,IP4,IP6,UDP,TCP,SCTP
      gopen:<filename>  groups=FD,FIFO,CHR,BLK,REG,SOCKET,NAMED,OPEN,TERMIOS,UNIX
      interface:<interface>     groups=FD,SOCKET
      ip-datagram:<host>:<protocol>     groups=FD,SOCKET,RANGE,IP4,IP6
      ip-recv:<protocol>        groups=FD,SOCKET,RANGE,IP4,IP6
      ip-recvfrom:<protocol>    groups=FD,SOCKET,CHILD,RANGE,IP4,IP6
      ip-sendto:<host>:<protocol>       groups=FD,SOCKET,IP4,IP6
      ip4-datagram:<host>:<protocol>    groups=FD,SOCKET,RANGE,IP4
      ip4-recv:<protocol>       groups=FD,SOCKET,RANGE,IP4
      ip4-recvfrom:<protocol>   groups=FD,SOCKET,CHILD,RANGE,IP4
      ip4-sendto:<host>:<protocol>      groups=FD,SOCKET,IP4
      ip6-datagram:<host>:<protocol>    groups=FD,SOCKET,RANGE,IP6
      ip6-recv:<protocol>       groups=FD,SOCKET,RANGE,IP6
      ip6-recvfrom:<protocol>   groups=FD,SOCKET,CHILD,RANGE,IP6
      ip6-sendto:<host>:<protocol>      groups=FD,SOCKET,IP6
      open:<filename>   groups=FD,FIFO,CHR,BLK,REG,NAMED,OPEN,TERMIOS
      openssl:<host>:<port>     groups=FD,SOCKET,CHILD,RETRY,IP4,IP6,TCP,OPENSSL
      openssl-listen:<port>     groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP4,IP6,TCP,OPENSSL
      pipe:<filename>   groups=FD,FIFO,NAMED,OPEN
      proxy:<proxy-server>:<host>:<port>        groups=FD,SOCKET,CHILD,RETRY,IP4,IP6,TCP,HTTP
      pty       groups=FD,NAMED,TERMIOS,PTY
      sctp-connect:<host>:<port>        groups=FD,SOCKET,CHILD,RETRY,IP4,IP6,SCTP
      sctp-listen:<port>        groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP4,IP6,SCTP
      sctp4-connect:<host>:<port>       groups=FD,SOCKET,CHILD,RETRY,IP4,SCTP
      sctp4-listen:<port>       groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP4,SCTP
      sctp6-connect:<host>:<port>       groups=FD,SOCKET,CHILD,RETRY,IP6,SCTP
      sctp6-listen:<port>       groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP6,SCTP
      socket-connect:<domain>:<protocol>:<remote-address>       groups=FD,SOCKET,CHILD,RETRY
      socket-datagram:<domain>:<type>:<protocol>:<remote-address>       groups=FD,SOCKET,RANGE
      socket-listen:<domain>:<protocol>:<local-address> groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE
      socket-recv:<domain>:<type>:<protocol>:<local-address>    groups=FD,SOCKET,RANGE
      socket-recvfrom:<domain>:<type>:<protocol>:<local-address>        groups=FD,SOCKET,CHILD,RANGE
      socket-sendto:<domain>:<type>:<protocol>:<remote-address> groups=FD,SOCKET
      socks4:<socks-server>:<host>:<port>       groups=FD,SOCKET,CHILD,RETRY,IP4,IP6,TCP,SOCKS4
      socks4a:<socks-server>:<host>:<port>      groups=FD,SOCKET,CHILD,RETRY,IP4,IP6,TCP,SOCKS4
      stderr    groups=FD,FIFO,CHR,BLK,REG,SOCKET,TERMIOS,UNIX,IP4,IP6,UDP,TCP,SCTP
      stdin     groups=FD,FIFO,CHR,BLK,REG,SOCKET,TERMIOS,UNIX,IP4,IP6,UDP,TCP,SCTP
      stdio     groups=FD,FIFO,CHR,BLK,REG,SOCKET,TERMIOS,UNIX,IP4,IP6,UDP,TCP,SCTP
      stdout    groups=FD,FIFO,CHR,BLK,REG,SOCKET,TERMIOS,UNIX,IP4,IP6,UDP,TCP,SCTP
      system:<shell-command>    groups=FD,FIFO,SOCKET,EXEC,FORK,TERMIOS,PTY,PARENT,UNIX
      tcp-connect:<host>:<port> groups=FD,SOCKET,CHILD,RETRY,IP4,IP6,TCP
      tcp-listen:<port> groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP4,IP6,TCP
      tcp4-connect:<host>:<port>        groups=FD,SOCKET,CHILD,RETRY,IP4,TCP
      tcp4-listen:<port>        groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP4,TCP
      tcp6-connect:<host>:<port>        groups=FD,SOCKET,CHILD,RETRY,IP6,TCP
      tcp6-listen:<port>        groups=FD,SOCKET,LISTEN,CHILD,RETRY,RANGE,IP6,TCP
      tun[:<ip-addr>/<bits>]    groups=FD,CHR,NAMED,OPEN,INTERFACE
      udp-connect:<host>:<port> groups=FD,SOCKET,IP4,IP6,UDP
      udp-datagram:<host>:<port>        groups=FD,SOCKET,RANGE,IP4,IP6,UDP
      udp-listen:<port> groups=FD,SOCKET,LISTEN,CHILD,RANGE,IP4,IP6,UDP
      udp-recv:<port>   groups=FD,SOCKET,RANGE,IP4,IP6,UDP
      udp-recvfrom:<port>       groups=FD,SOCKET,CHILD,RANGE,IP4,IP6,UDP
      udp-sendto:<host>:<port>  groups=FD,SOCKET,IP4,IP6,UDP
      udp4-connect:<host>:<port>        groups=FD,SOCKET,IP4,UDP
      udp4-datagram:<remote-address>:<port>     groups=FD,SOCKET,RANGE,IP4,UDP
      udp4-listen:<port>        groups=FD,SOCKET,LISTEN,CHILD,RANGE,IP4,UDP
      udp4-recv:<port>  groups=FD,SOCKET,RANGE,IP4,UDP
      udp4-recvfrom:<host>:<port>       groups=FD,SOCKET,CHILD,RANGE,IP4,UDP
      udp4-sendto:<host>:<port> groups=FD,SOCKET,IP4,UDP
      udp6-connect:<host>:<port>        groups=FD,SOCKET,IP6,UDP
      udp6-datagram:<host>:<port>       groups=FD,SOCKET,RANGE,IP6,UDP
      udp6-listen:<port>        groups=FD,SOCKET,LISTEN,CHILD,RANGE,IP6,UDP
      udp6-recv:<port>  groups=FD,SOCKET,RANGE,IP6,UDP
      udp6-recvfrom:<port>      groups=FD,SOCKET,CHILD,RANGE,IP6,UDP
      udp6-sendto:<host>:<port> groups=FD,SOCKET,IP6,UDP
      unix-client:<filename>    groups=FD,SOCKET,NAMED,RETRY,UNIX
      unix-connect:<filename>   groups=FD,SOCKET,NAMED,RETRY,UNIX
      unix-listen:<filename>    groups=FD,SOCKET,NAMED,LISTEN,CHILD,RETRY,UNIX
      unix-recv:<filename>      groups=FD,SOCKET,NAMED,RETRY,UNIX
      unix-recvfrom:<filename>  groups=FD,SOCKET,NAMED,CHILD,RETRY,UNIX
      unix-sendto:<filename>    groups=FD,SOCKET,NAMED,RETRY,UNIX

I would welcome any advice.
Kind regards,
Michael

1 Like

It looks like there’s some issue history relating to this, but it doesn’t look like anything was done to make this kind of multi-user scenario work with webroot mode.

What webserver do you use? Maybe there’s another mode that would suit you better.

1 Like

I’m using nginx. I have already read the issue you mentioned and also tried to adapt the proposal kevinoid made: https://github.com/kevinoid/acme.sh/commit/6cebfe5c0940076ccaab11d148ebc38a39f273a7 .
But unfortunately I just got another error:
office.mydomain.de:Verify error:Invalid response from http://office.mydomain.de/.well-known/acme-challenge/5f_YE0hrPgUTDiYAMlMa3Wt7N92u_5ozQ-nl8Z7KaVk
Maybe it has something to do with the fact, that the domain office.mydomain.de is just rechable via IPv6?

1 Like

A v6-only domain should not pose any problems. It should just work, but of course it’s hard to conclude anything from a redacted domain.

With acme.sh+nginx there’s also something called stateless mode, which has zero moving parts. If you have concerns about privileges and permissions, it’s pretty neat.

3 Likes