Kozea / Radicale

A simple CalDAV (calendar) and CardDAV (contact) server.
https://radicale.org
GNU General Public License v3.0
3.29k stars 429 forks source link

Following docs does not result in successful installation #1495

Open CarlSinclair opened 4 months ago

CarlSinclair commented 4 months ago

RHEL systemd service never starts successfully, but that's secondary to the fact that the only available instructions are for "simple" installation so you can test it out. From there you're really going blind in the dark.

The ~/.config directory doesn't work and I'm not sure if you want it to exist because apparently /etc/ is an alternative and great I prefer that, but I noticed a lot of ~/.config references coded in that I had to manually change so that's probably where the problem lies.

Oh and I can't get logs because I can't even get it to run. I wish I could get logs.

pbiering commented 3 months ago

Did you install via RPM from EPEL?

If so, try this https://github.com/Kozea/Radicale/wiki/Server-Diagnostics---Troubleshooting#manual-start

CarlSinclair commented 3 months ago

No I actually had no idea there was an rpm. I was following the docs and installing through python. But ultimately it didn't make a difference as I ran into the same issues installing via rpm and running via the command you linked to.

First of all the . in /.var/lib/radicale/collections throws permission errors. I assumed that's a typo and removed it. After that, I was eventually able to set up a config file in /etc/radicale/config and point it to it and set up mainly the SSL cert and key files for it to use. The server launches correctly but only works via localhost, which I understand is the case because it's for testing purposes (and the production section of the docs isn't filled out yet) but I'm using SSH so I have no way of navigating a webpage via localhost, hence my attempts to make it accessible over the internet via reverse proxy.

Here's where I'm currently stuck. curl localhost:5232 returns curl: (52) Empty reply from server, with the server reporting an SSL handshake failure. I've tried both the Cloudflare Universal SSL cert and even a custom Let's Encrypt cert. I've tried using Apache and Cloudflare as reverse proxy. The result is the same.

I've come to the conclusion that the instructions I'm following are for test purposes on localhost without SSL and that there's no way to use SSL for production without additional configuration.

CarlSinclair commented 3 months ago

Update: I accidentally found this vhost in /etc/httpd/conf.d/radicale. I had previously made my own vhost. This must have been generated as part of the rpm installation. Unfortunately the documentation it links to is 404, Sadly it doesn't include a https vhost so I'm afraid it won't be very helpful to me since I'm not familiar enough with WSGI to reverse engineer this.

# http://radicale.org/user_documentation/#idapache-and-mod-wsgi

# WARNING: To use this correctly, you will need to set:
#    "setsebool -P httpd_can_read_write_radicale=1"

#<VirtualHost *:80>
#    ServerName cal.localhost

#    WSGIDaemonProcess radicale user=radicale group=radicale threads=1 umask=0027
#    WSGIScriptAlias / /usr/share/radicale/radicale.wsgi

#    <Location />
#        WSGIProcessGroup radicale
#        WSGIApplicationGroup %{GLOBAL}
#        AllowOverride None
#
#        ## You may want to use apache's authentication
#        AuthBasicProvider file
#        AuthType Basic
#        AuthName "Enter your credentials"
#        AuthUserFile /path/to/httpdfile/
#        AuthGroupFile /dev/null
#        Require valid-user
#        <IfModule rewrite_module>
#            RewriteEngine On
#            RewriteCond %{REMOTE_USER}%{PATH_INFO} !^([^/]+/)\1
#            RewriteRule .* - [Forbidden]
#        </IfModule>
#
#    </Location>
#</VirtualHost>
pbiering commented 3 months ago

First of all the . in /.var/lib/radicale/collections throws permission errors.

You've followed the "per-user-only" setup guide but missed the "~", which is not useful on a system-wide installation (and only more or less for testing purposes only).

I've tried both the Cloudflare Universal SSL cert and even a custom Let's Encrypt cert.

The TLS section was reviewed, but can be that file/directory permissions or SELinux denies or local firewall setup prevent access to the port - can't help here without debug log.

I've tried using Apache and Cloudflare as reverse proxy.

https://github.com/Kozea/Radicale/wiki/Reverse-Proxy-Diagnostics-Troubleshooting#selinux

/etc/httpd/conf.d/radicale

I would assume you mean radicale.conf...this is an example file, similar to radicale-doc.conf somehow. Potentially a review is required.

that there's no way to use SSL for production without additional configuration.

TLS configuration is always custom, if forward proxy config is used without dedicated virtual host, TLS is handled by e.g. by the Apache itself. Dedicated virtual host setup with TLS is possible but somehow always custom.

Looks like the example configs in RPM must be reviewed...will schedule this.

CarlSinclair commented 3 months ago

I have SELinux disabled so definitely not that. I couldn't find much in the reverse proxy troubleshooting documentation beyond SELinux troubleshooting.

And yes, I forgot to include the .conf at the end there. I also forgot the ~ but that's definitely in there too. It's more the . in ~/.var/ that I'm confused about. I removed it because it was throwing permission errors and I obviously wasn't going to grant it write permissions to the /var/ directory which is why I assumed it must have been a typo.

I'm sure I'm missing something fairly obvious. I look forward to following the updated documentation when it's ready, both for reverse proxies and also for the production setup.

pbiering commented 3 months ago

can you try starting from following reviewed example: https://src.fedoraproject.org/rpms/radicale/blob/rawhide/f/radicale-httpd

CarlSinclair commented 3 months ago

Well the regular non-wsgi vhost did not work as it is essentially identical to mine except for the additional headers being passed. But to my surprise, the WSGI vhost worked immediately. But I couldn't get past the login screen without a Error 500 when submitting my credentials. Afterwhich time the page is no longer loading at all simply saying A server error occurred. Please contact the administrator.

The worst part however is that the credentials not only seem to be getting sent in plaintext, they're even being returned in the console log, also in planetext. I took a screenshot:

image

Interestingly, the tab I still have open continues to be calling the backend script fn.js, even though I can no longer open the page in a new tab due to the aforementioned error. There's also nothing in Apache's error logs. Not sure if WSGI logs differently, or how to "restart" or otherwise configure it.

pbiering commented 3 months ago

The worst part however is that the credentials not only seem to be getting sent in plaintext, they're even being returned in the console log, also in planetext. I took a screenshot:

They are not sent in plain text in case of HTTPS is in use. And the browser console for sure knows all what the client is sending, therefore also able to log the credentials.

BTW: have updated https://src.fedoraproject.org/rpms/radicale/blob/rawhide/f/radicale-httpd, it works here now using ReverseProxy or WSGI config, there are now toggles in the config to easy enable related method.

Please try RPM on a fresh test system (VM) with that updated httpd.conf to avoid that any former adjustment will cause errors.

CarlSinclair commented 3 months ago

Unfortunately I don't really have access to a fresh VM to test this and my current one has way too much going on to start over. I just messed with this for over an hour and I can't even get to the login screen. I've tried both the regular reverse proxy and WSGI, and even both.

curl localhost:5232 returns curl: (52) Empty reply from server. curl publicIP:5232 returns the same. Doing the same but over HTTPS returns curl: (56) OpenSSL SSL_read: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0 both with Cloudflare's Universal SSL certificate as well as a dedicated Let's Encrypt certificate. Trying to access it over the domain name returns 403, with and without HTTPS. Obviously I've changed require local to require all granted to be able to access it over the internet.

Without more documentation and better understanding of the backend and WSGI I'm afraid I can't be of much help.

I wish you had maintained the older version of the WSGI vhost because that was the only one we made some progress with.

pbiering commented 3 months ago

https://src.fedoraproject.org/rpms/radicale/blob/rawhide/f/radicale-httpd extended with a virtual host example, all 4 scenarios tested on Fedora 41 but see no reason why it should not work on EL.

Beside that the 2 scenarios connecting directly to "radicale" (with or without SSL).

Without having a view on debug log of "radicale" and/or "error log" of Apache it's very hard to help...

Please try to create a local reference installation e.g. using a VM in "VirtualBox".

CarlSinclair commented 3 months ago

The syslog only contains SSL handshake errors and Apache logs reflect the errors I've been seeing without much more info. Currently it seems to be querying the web root directory every time I try to connect to it, and failing because it can't find the requested files and because directory listing is disabled. But the fact that it's even trying to fetch a directory instead of proxying the backend is already wrong. The bigger problem is that it's not even working via curl localhost:5232 which tells me the issue is far beyond the reverse proxy. Even if we fixed all of Apache's problems we'd still have to figure out the backend/service problems. All of this behavior is consistent whether or not I use WSGI. And I don't know how WSGI works at all because I've never used it so I can't even troubleshoot that. But it did work that one time as soon as I swapped the virtualhost to use it instead of the traditional reverse proxy.

This is my systemd service:

[Unit]
Description=A simple CalDAV (calendar) and CardDAV (contact) server
After=network.target
Requires=network.target

[Service]
ExecStart=/usr/bin/env python3 -m radicale --config /etc/radicale/config --logging-level info
Restart=on-failure
User=radicale
Group=radicale
# Deny other users access to the calendar data
UMask=0027
# Optional security settings
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
NoNewPrivileges=true
ReadWritePaths=/var/lib/radicale/collections

[Install]
WantedBy=multi-user.target

This is my /etc/radicale/config

[server]
# Bind all addresses
hosts = 0.0.0.0:5232, [::]:5232
ssl = True
certificate = /etc/radicale/fullchain.pem
key = /etc/radicale/privkey.pem
certificate_authority = /etc/radicale/ca.pem

[auth]
type = htpasswd
htpasswd_filename = /etc/radicale/users
htpasswd_encryption = md5

[storage]
filesystem_folder = ~/var/lib/radicale/collections

Syslog:

Jun 09 01:35:08 systemd[1]: Started A simple CalDAV (calendar) and CardDAV (contact) server.
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Loaded default config
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Loaded config file '/etc/radicale/config'
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Loaded command line arguments
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Starting Radicale
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] auth type is 'radicale.auth.htpasswd'
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.md5'
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] storage type is 'radicale.storage.multifilesystem'
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] rights type is 'radicale.rights.owner_only'
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] web type is 'radicale.web.internal'
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] hook type is 'radicale.hook.none'
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] permit delete of collection: True
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Listening on '0.0.0.0:5232' with SSL
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Listening on '[::]:5232' with SSL
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Radicale server ready
Jun 09 01:35:10 radicale[95396]: [95396/Thread-1] [ERROR] An exception occurred during request: SSL handshake failed: [SSL: HTTP_REQUEST] http request (_ssl.c:1129)
Jun 09 01:35:35 radicale[95396]: [95396/Thread-2] [ERROR] An exception occurred during request: SSL handshake failed: [SSL: PEER_DID_NOT_RETURN_A_CERTIFICATE] peer did not return a certificate (_ssl.c:1129)

My Apache reverse proxy:

<VirtualHost *:443>
    ServerName 
    SSLEngine On
    SSLCertificateFile 
    SSLCertificateKeyFile 
    SSLCertificateChainFile 
        <Files /usr/share/radicale/radicale.wsgi>
                SetHandler wsgi-script
                Require all granted
        </Files>
        RequestHeader    set X-Forwarded-Proto "https"
        WSGIDaemonProcess radicale user=radicale group=radicale threads=1 umask=0027
        WSGIProcessGroup radicale
        WSGIApplicationGroup %{GLOBAL}
        WSGIPassAuthorization On
        WSGIScriptAlias / /usr/share/radicale/radicale.wsgi
        <Location /radicale>
                RequestHeader    set X-Script-Name /
                Require all granted
        </Location>
<IfModule !wsgi_module>
        Error "RADICALE_SERVER_WSGI selected but wsgi module not loaded/enabled"
</IfModule>
    ErrorLog /var/log/httpd/radicale.error.ssl
    CustomLog /var/log/httpd/radicale.access.ssl combined
</VirtualHost>
<VirtualHost *:80>
ServerName 
    ErrorLog /var/log/httpd/radicale.error
    CustomLog /var/log/httpd/radicale.access combined
</VirtualHost>

The non-WSGI reverse proxy:

<VirtualHost *:443>
    ServerName
    SSLEngine On
    SSLProxyEngine On
    SSLCertificateFile 
    SSLCertificateKeyFile 
    SSLCertificateChainFile 
#        RewriteEngine On
#        RewriteRule ^/radicale$ /radicale/ [R,L]

        <Location />
                RequestHeader    set X-Script-Name /
                RequestHeader    set X-Forwarded-Port "%{SERVER_PORT}s"
                RequestHeader    unset X-Forwarded-Proto
                RequestHeader    set X-Forwarded-Proto "https"
                ProxyPass        http://localhost:5232/ retry=0
                ProxyPassReverse http://localhost:5232/
                Require all granted
                <IfDefine RADICALE_ENFORCE_SSL>
                        <IfModule !ssl_module>
                                Error "RADICALE_ENFORCE_SSL selected but ssl module not loaded/enabled"
                        </IfModule>
                                SSLRequireSSL
                </IfDefine>
        </Location>
    ErrorLog /var/log/httpd/cal.error.ssl
    CustomLog /var/log/httpd/cal.access.ssl combined
</VirtualHost>
<VirtualHost *:80>
ServerName
    ErrorLog /var/log/httpd/radicale.error
    CustomLog /var/log/httpd/radicale.access combined
</VirtualHost>
pbiering commented 3 months ago
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Listening on '0.0.0.0:5232' with SSL
Jun 09 01:35:09 radicale[95396]: [95396] [INFO] Listening on '[::]:5232' with SSL

in conjunction with

                ProxyPass        http://localhost:5232/ retry=0
                ProxyPassReverse http://localhost:5232/

will not work, "apache" is assuming HTTP while "radicale" has only started a SSL socket. Either use "https" in ProxyPass (can require some cert trust tweak extensions in addition<) or disable SSL on "radicale" (and turn back to localhost listen for security reasons).

"WSGI" is a little bit like a "cgid", logging btw. into apache's error log.

Also enabling debug log level for radicale would potentially give more hints.

For tests I would not start "radicale" by systemd, but become the "radicale" user itself and start with same arguments (it stays then in foreground btw.)

CarlSinclair commented 3 months ago

Weird, because I definitely have other services running on my machine that do work using that combination. But I disabled SSL on radicale and now I'm getting actual access logs from it in the syslog:

Jun 09 15:51:53 radicale[111562]: [111562/Thread-1] [INFO] GET request for '/' received from ::1 using 'curl/7.76.1'
Jun 09 15:51:53 radicale[111562]: [111562/Thread-1] [INFO] GET response status for '/' in 0.002 seconds: 302 Found
Jun 09 15:51:57 radicale[111562]: [111562/Thread-2] [INFO] GET request for '/web' received from ::1 using 'curl/7.76.1'
Jun 09 15:51:57 radicale[111562]: [111562/Thread-2] [INFO] Access to '/web' denied for anonymous user
Jun 09 15:51:57 radicale[111562]: [111562/Thread-2] [INFO] GET response status for '/web' in 0.003 seconds: 401 Unauthorized
Jun 09 15:52:27 radicale[111562]: [111562/Thread-3] [INFO] GET request for '/.web' received from ::1 using 'curl/7.76.1'
Jun 09 15:52:27 radicale[111562]: [111562/Thread-3] [INFO] GET response status for '/.web' in 0.006 seconds: 302 Found

My Apache logs however tell me it's still trying to serve the web directory and files at that location instead of proxying radicale.

The biggest progress though is that I'm no longer getting empty response when using curl:

curl localhost:5232
Redirected to /.web

The result is the same using WSGI and traditional reverse proxy, copied directly from the link you provided only changing require local to require all granted and removing the <IfDefine> and <IfModule> checks.

pbiering commented 3 months ago

The redirect on "GET" request to /.web is ok, this is for the WebUI and need to be followed by the browser to /.web/.

Calendar clients using different requests, I've added some examples here:

https://github.com/Kozea/Radicale/wiki/Server-Diagnostics---Troubleshooting

CarlSinclair commented 3 months ago

So it's working with SSL disabled, but not in the browser via reverse proxy. And even via curl I'm unable to access any pages.

But if this is intended functionality then we only need to fix the reverse proxy now, and SSL.

pbiering commented 3 months ago
        <Location />
                ProxyPass        http://localhost:5232/ retry=0
                ProxyPassReverse http://localhost:5232/
                ...
        </Location>

Potentially an overlapped "Location" config, please take the provided example to setup forward proxy on dedicated port (with or without SSL) first.

Added also some hints here: https://github.com/Kozea/Radicale/wiki/Reverse-Proxy-Diagnostics-Troubleshooting#send-request-via-reverse-proxy

Check also the Apache error and access log for any hints.

CarlSinclair commented 3 months ago

It seems that my reverse proxy wasn't working because I was only configuring the 443 vhost and leaving the 80 vhost blank. Odd because this also isn't normally an issue with other vhosts with HTTPS redirection, but HTTPS redirection was leading too too many redirects error in Radicale's case so I had disabled it.

Anyway I got it working but it's crashing at the same spot as before, upon entering credentials. Checking logs, it's #577 which supposedly got fixed back in 2017.

Jun 12 00:40:10 radicale[172205]: [172205/Thread-22] [ERROR] An exception occurred during GET request on '/.web/': Invalid htpasswd file '/etc/radicale/users': not a valid apr_md5_crypt hash

I've updated the file with hashes instead of plain text but now the website won't load at all even after restarting both Apache and Radicale. But it does load in another browser (only for the same thing to happen again), even though there is no cookie being placed. This is all very strange.

The linked issue doesn't offer any solutions, only says it should be fixed.

Edit: I realize now that the documentation covers setting the auth type in the config file and radicale takes this VERY seriously. Since I've been working on this off and on for weeks, I must have forgotten that I set that.

CarlSinclair commented 3 months ago

Commenting out the auth lines in the config and uncommenting the Apache auth lines in the vhost made me finally get in, but I hate that I'm using basic auth over HTTP.

I'll get to know it better over the next few days and hopefully get SSL working. Then I'll be better suited to contribute to the documentation. Hopefully easier adoption would bring more attention to WebDAV.

Thank you for all your help.

jonassmedegaard commented 3 months ago

but I hate that I'm using basic auth over HTTP

I guess that you are here referring to the traffic between the Apache2 proxy and Radicale being unencrypted and containing authentication information.

I am unaware that this is a real cause for concern when that traffic is done only on the loopback interface (i.e. localhost), but anyway you might want to consider connecting through a unix socket instead - e.g. using uWSGI. This is the default setup with the Debian packaging of Radicale - the main config files are here, here and here.

pbiering commented 3 months ago

as stated in https://github.com/Kozea/Radicale/issues/1495#issuecomment-2156373711 one can configure SSL also between "apache" and "radicale" using ProxyPass https://... but the Apache truststore must be proper configured to accept and trust the SSL cert offered by "radicale".

While this blocks e.g. using ngrep -d lo port 5232 to see cleartext, still using strace on "radicale" or "apache" will be able to catch the credentials.