Of course, I meant to write “DAV” where I wrote “David.” I suspect the spell-checker got me there.
DAVx5 fails to send server-required SSL cert: "400 No required SSL certificate was sent" ?
-
I’ve a Radicale3 server serving up contacts/calendars.
It sits behind an nginx reverse-proxy front end; auth to Radicale is provided by passthrough basic auth.
With the following nginx config – with NO ssl client cert verification – DavX5 connects without issue:
server { listen 10.0.1.20:10000 ssl http2; server_name front.example.com; root /dev/null; autoindex off; ssl_verify_client off; ssl_verify_depth 2; ssl_client_certificate "/srv/ssl/myCA_CHAIN.crt"; ssl_certificate "/srv/ssl/front.server.EC.crt"; ssl_certificate_key "/srv/ssl/front.server.EC.key"; location / { root /srv/nulldir; allow 10.0.1.100; deny all; try_files $uri $uri/ =404; } location /dav/ { proxy_pass https://radicale.example.com:20000/; proxy_ssl_name radicale.example.com; proxy_set_header X-Script-Name /dav; proxy_ssl_verify on; proxy_ssl_verify_depth 2; proxy_ssl_certificate "/srv/ssl/radicale.client.EC.crt"; proxy_ssl_certificate_key "/srv/ssl/radicale.client.EC.key"; proxy_ssl_trusted_certificate "/srv/ssl/myCA_CHAIN.crt"; auth_basic "DAV - Password Required"; auth_basic_user_file /srv/auth/radicale.auth; include includes/reverse-proxy.inc; }
however,
if I require ssl client verification,
- ssl_verify_client off; + ssl_verify_client on;
ensuring a valid cert on the phone, connection fails, apparently because DavX5 fails to send an SSL cert; debug logs follow.
what additional config/settings are required for DavX5 to correctly serve-up the client-side cert for verification @ the server?
--- BEGIN DEBUG INFO --- SYNCHRONIZATION INFO Account: Account {name=Card.001 (testuser@example.com Ug), type=at.bitfire.davdroid.address_book} Authority: com.android.contacts EXCEPTION at.bitfire.dav4jvm.exception.HttpException: HTTP 400 at at.bitfire.dav4jvm.DavResource.checkStatus(DavResource.kt:6) at at.bitfire.dav4jvm.DavResource.checkStatus(DavResource.kt:3) at at.bitfire.dav4jvm.DavResource.processMultiStatus(DavResource.kt:1) at at.bitfire.dav4jvm.DavResource.propfind(DavResource.kt:11) at at.bitfire.davdroid.syncadapter.ContactsSyncManager$queryCapabilities$1.invoke(ContactsSyncManager.kt:3) at at.bitfire.davdroid.syncadapter.ContactsSyncManager$queryCapabilities$1.invoke(ContactsSyncManager.kt:1) at at.bitfire.davdroid.syncadapter.SyncManager.remoteExceptionContext(SyncManager.kt:1) at at.bitfire.davdroid.syncadapter.SyncManager.remoteExceptionContext(SyncManager.kt:9) at at.bitfire.davdroid.syncadapter.ContactsSyncManager.queryCapabilities(ContactsSyncManager.kt:3) at at.bitfire.davdroid.syncadapter.SyncManager$performSync$1.invoke(SyncManager.kt:6) at at.bitfire.davdroid.syncadapter.SyncManager$performSync$1.invoke(SyncManager.kt:1) at at.bitfire.davdroid.syncadapter.SyncManager.unwrapExceptions(SyncManager.kt:1) at at.bitfire.davdroid.syncadapter.SyncManager.performSync(SyncManager.kt:5) at at.bitfire.davdroid.syncadapter.ContactsSyncAdapterService$ContactsSyncAdapter.sync(ContactsSyncAdapterService.kt:15) at at.bitfire.davdroid.syncadapter.SyncAdapterService$SyncAdapter.onPerformSync(SyncAdapterService.kt:17) at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:334) HTTP REQUEST Request{method=PROPFIND, url=https://front.example.com:10000/dav/testuser%40example.com/33333333-3333-3333-3333-333333333333/, headers=[Depth:0, Accept-Encoding:br,gzip]} <?xml version='1.0' encoding='UTF-8' ?><propfind xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav" xmlns:CARD="urn:ietf:params:xml:ns:carddav"><prop><CARD:supported-address-data /><supported-report-set /><n0:getctag xmlns:n0="http://calendarserver.org/ns/" /><sync-token /></prop></propfind> HTTP RESPONSEResponse{protocol=h2, code=400, message=, url=https://front.example.com:10000/dav/testuser%40example.com/33333333-3333-3333-3333-333333333333/} <html> <head><title>400 No required SSL certificate was sent</title></head> <body> <center><h1>400 Bad Request</h1></center> <center>No required SSL certificate was sent</center> <hr><center>nginx</center> </body> </html> REMOTE RESOURCE https://front.example.com:10000/dav/testuser%40example.com/33333333-3333-3333-3333-333333333333/ SOFTWARE INFORMATION ┌────────────────────────────────┬─────────────┬───────────┬─────────────────────┬───────┐ │ Package │ Version │ Code │ Installer │ Notes │ ├────────────────────────────────┼─────────────┼───────────┼─────────────────────┼───────┤ │ at.bitfire.davdroid │ 3.3.1-gplay │ 303010001 │ com.android.vending │ │ │ org.dmfs.tasks │ 1.2.3 │ 77600 │ com.android.vending │ │ │ com.android.providers.contacts │ 10 │ 29 │ — │ │ │ com.android.providers.calendar │ 10 │ 29 │ — │ │ │ com.android.contacts │ 1.7.31 │ 10731 │ — │ │ │ com.dw.contacts │ 3.1.7.5 │ 3175 │ com.android.vending │ │ │ org.lineageos.etar │ 10 │ 29 │ — │ │ │ com.aboutmycode.betteropenwith │ 1.4.11 │ 53 │ com.android.vending │ │ │ com.appgenix.bizcal │ 2.40.4 │ 240403 │ com.android.vending │ │ └────────────────────────────────┴─────────────┴───────────┴─────────────────────┴───────┘ SYSTEM INFORMATION Android version: 10 (lineage_star2lte-userdebug 10 QQ3A.200605.001 eng.rob.20200712.181146 test-keys) Device: samsung SM-G965F (star2lte) CONNECTVITY ☒ [ Transports: CELLULAR Capabilities: MMS SUPL IA INTERNET NOT_RESTRICTED TRUSTED NOT_VPN VALIDATED NOT_ROAMING FOREGROUND NOT_CONGESTED NOT_SUSPENDED LinkUpBandwidth>=15000Kbps LinkDnBandwidth>=30000Kbps Specifier: <1>] Data saver: disabled CONFIGURATION Power saving disabled: yes System-wide synchronization: automatically Notifications: - sync isBlocked=false * syncProblems: importance=2 * syncIoErrors: importance=1 * syncWarnings: importance=2 - cert4android: importance=2 - general: importance=2 - debug: importance=0 Permissions: - READ_CONTACTS: granted - WRITE_CONTACTS: granted - READ_CALENDAR: granted - WRITE_CALENDAR: granted - READ_TASKS: granted - WRITE_TASKS: granted - ACCESS_COARSE_LOCATION: denied ACCOUNTS - Account: shared@example.com ┌──────────────────────────────────┬────────────┬──────────────────────┬───────────────┐ │ Authority │ isSyncable │ getSyncAutomatically │ Sync interval │ ├──────────────────────────────────┼────────────┼──────────────────────┼───────────────┤ │ at.bitfire.davdroid.addressbooks │ 1 │ true │ 60 min │ │ com.android.calendar │ 1 │ true │ 60 min │ │ com.android.contacts │ 0 │ false │ — │ │ org.dmfs.tasks │ 1 │ true │ 60 min │ └──────────────────────────────────┴────────────┴──────────────────────┴───────────────┘ WiFi only: false Contact group method: CATEGORIES Time range (past days): null Default alarm (min before): null Manage calendar colors: true Use event colors: true * Address book: Card.Shared (shared@example.com eg) ┌────────────┬──────────────────────┬───────────────┐ │ isSyncable │ getSyncAutomatically │ Sync interval │ ├────────────┼──────────────────────┼───────────────┤ │ 1 │ true │ 1440 min │ └────────────┴──────────────────────┴───────────────┘ URL: https://front.example.com:10000/dav/shared%40example.com/22222222-2222-2222-2222-222222222222/ Read-only: 0 - Account: testuser@example.com ┌──────────────────────────────────┬────────────┬──────────────────────┬───────────────┐ │ Authority │ isSyncable │ getSyncAutomatically │ Sync interval │ ├──────────────────────────────────┼────────────┼──────────────────────┼───────────────┤ │ at.bitfire.davdroid.addressbooks │ 1 │ true │ 60 min │ │ com.android.calendar │ 1 │ true │ 60 min │ │ com.android.contacts │ 0 │ false │ — │ │ org.dmfs.tasks │ 1 │ true │ 60 min │ └──────────────────────────────────┴────────────┴──────────────────────┴───────────────┘ WiFi only: false Contact group method: CATEGORIES Time range (past days): null Default alarm (min before): null Manage calendar colors: true Use event colors: true * Address book: Card.001 (testuser@example.com Ug) ┌────────────┬──────────────────────┬───────────────┐ │ isSyncable │ getSyncAutomatically │ Sync interval │ ├────────────┼──────────────────────┼───────────────┤ │ 1 │ true │ — │ └────────────┴──────────────────────┴───────────────┘ URL: https://front.example.com:10000/dav/testuser%40example.com/33333333-3333-3333-3333-333333333333/ Read-only: 0 DATABASE DUMP android_metadata ┌────────┐ │ locale │ ├────────┤ │ en_US │ └────────┘ service ┌────┬────────────────────────────┬─────────┬───────────────────────────────────────────────────────────────────────────────┐ │ id │ accountName │ type │ principal │ ├────┼────────────────────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────┤ │ 7 │ shared@example.com │ carddav │ https://front.example.com:10000/dav/shared%40example.com/ │ │ 8 │ shared@example.com │ caldav │ https://front.example.com:10000/dav/shared%40example.com/ │ │ 9 │ testuser@example.com │ carddav │ https://front.example.com:10000/dav/testuser%40example.com/ │ │ 10 │ testuser@example.com │ caldav │ https://front.example.com:10000/dav/testuser%40example.com/ │ └────┴────────────────────────────┴─────────┴───────────────────────────────────────────────────────────────────────────────┘ sqlite_sequence ┌────────────┬─────┐ │ name │ seq │ ├────────────┼─────┤ │ service │ 10 │ │ collection │ 10 │ │ homeset │ 10 │ └────────────┴─────┘ homeset ┌────┬───────────┬───────────────────────────────────────────────────────────────────────────────┬──────────┬─────────────┐ │ id │ serviceId │ url │ privBind │ displayName │ ├────┼───────────┼───────────────────────────────────────────────────────────────────────────────┼──────────┼─────────────┤ │ 7 │ 8 │ https://front.example.com:10000/dav/shared%40example.com/ │ 1 │ — │ │ 8 │ 7 │ https://front.example.com:10000/dav/shared%40example.com/ │ 1 │ — │ │ 9 │ 9 │ https://front.example.com:10000/dav/testuser%40example.com/ │ 1 │ — │ │ 10 │ 10 │ https://front.example.com:10000/dav/testuser%40example.com/ │ 1 │ — │ └────┴───────────┴───────────────────────────────────────────────────────────────────────────────┴──────────┴─────────────┘ collection ┌────┬───────────┬──────────────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬──────────────────┬────────────┬───────────────┬─────────────────┬─────────────────┬───────────┬──────────┬────────────────┬───────────────┬──────────────────┬────────┬──────┐ │ id │ serviceId │ type │ url │ privWriteContent │ privUnbind │ forceReadOnly │ displayName │ description │ color │ timezone │ supportsVEVENT │ supportsVTODO │ supportsVJOURNAL │ source │ sync │ ├────┼───────────┼──────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────┼────────────┼───────────────┼─────────────────┼─────────────────┼───────────┼──────────┼────────────────┼───────────────┼──────────────────┼────────┼──────┤ │ 7 │ 8 │ CALENDAR │ https://front.example.com:10000/dav/shared%40example.com/11111111-1111-1111-1111-111111111111/ │ 1 │ 1 │ 0 │ Cal.Shared │ Cal.Shared │ -7021909 │ — │ 1 │ 1 │ 1 │ — │ 1 │ │ 8 │ 7 │ ADDRESS_BOOK │ https://front.example.com:10000/dav/shared%40example.com/22222222-2222-2222-2222-222222222222/ │ 1 │ 1 │ 0 │ Card.Shared │ Card.Shared │ — │ — │ — │ — │ — │ — │ 1 │ │ 9 │ 9 │ ADDRESS_BOOK │ https://front.example.com:10000/dav/testuser%40example.com/33333333-3333-3333-3333-333333333333/ │ 1 │ 1 │ 0 │ Card.001 │ Card.001 │ — │ — │ — │ — │ — │ — │ 1 │ │ 10 │ 10 │ CALENDAR │ https://front.example.com:10000/dav/testuser%40example.com/44444444-4444-4444-4444-444444444444/ │ 1 │ 1 │ 0 │ Cal.001 │ Cal.001 │ -10782310 │ — │ 1 │ 1 │ 1 │ — │ 1 │ └────┴───────────┴──────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────┴────────────┴───────────────┴─────────────────┴─────────────────┴───────────┴──────────┴────────────────┴───────────────┴──────────────────┴────────┴──────┘ room_master_table ┌────┬──────────────────────────────────┐ │ id │ identity_hash │ ├────┼──────────────────────────────────┤ │ 42 │ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa │ └────┴──────────────────────────────────┘ APP SETTINGS 1. SharedPreferencesProvider canWrite=true ┌───────────────────────────┬───────────┐ │ Setting │ Value │ ├───────────────────────────┼───────────┤ │ distrust_system_certs │ false │ │ hint_AutostartPermissions │ false │ │ log_to_file │ true │ │ override_proxy │ false │ │ override_proxy_host │ localhost │ │ override_proxy_port │ 8118 │ └───────────────────────────┴───────────┘ 2. DefaultsProvider canWrite=false ┌───────────────────────┬───────────┐ │ Setting │ Value │ ├───────────────────────┼───────────┤ │ distrust_system_certs │ false │ │ override_proxy │ false │ │ override_proxy_host │ localhost │ │ override_proxy_port │ 8118 │ └───────────────────────┴───────────┘ --- END DEBUG INFO ---
-
Hi,
Did you choose “Login with URL and client certificate” when creating the DAVx5 account?
-
I’d attempted that …
However, in that config it no longer passes the password required for Radicale login.
I need both: access control with cert to the Nginx front-end, and auth control with user:pass to the Radicale back-end.
-
@pgnd This is currently not possible with DAVx5, because there is no UI for that. See https://gitlab.com/bitfireAT/davx5-ose/-/wikis/Roadmap “login”
-
so it is either-or, not both at the moment.
that feature will be useful – and necessary for several new/planned organization-wide implementations. here, anyway.
is that feature tagged to a milestone? with any associated timeframe?
-
@pgnd said in DAVx5 fails to send server-required SSL cert: "400 No required SSL certificate was sent" ?:
so it is either-or, not both at the moment.
Yes.
that feature will be useful – and necessary for several new/planned organization-wide implementations. here, anyway.
I see. What is the use case? We have implemented client-side certificate for an organization, too. What is the user name/password needed for? Isn’t the client certificate used to identify the client?
is that feature tagged to a milestone? with any associated timeframe?
No, unfortunately not. There are so many other things at the moment.
-
There’s also already a merge request for that: https://gitlab.com/bitfireAT/davx5-ose/-/merge_requests/13 (as you can see, it’s only an UI thing; because the engine can already do Basic and client certificates)
But there are some unresolved UI implications yet, and UI is always complicated.
-
What is the use case? We have implemented client-side certificate for an organization, too. What is the user name/password needed for? Isn’t the client certificate used to identify the client?
the nginx front-end reverse-proxies many backend apps.
the client cert provides access control for access to the front-end.
auth to the backend apps is provided by their respective, individual auth mechanisms. typically username + strong-pass.
that’ll eventually be additionally wrapped in an SSO portal that’ll add 2FA.
-
No, unfortunately not. There are so many other things at the moment.
understood.
as much as I personally like the davx5 client functionality/stability, it seems like this may not be the solution that fits for broader use here.