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

Client certificate authentication (mTLS) #344

Merged
merged 15 commits into from
Jun 13, 2024

Conversation

cyb3rko
Copy link
Contributor

@cyb3rko cyb3rko commented Apr 19, 2024

Continuation of #230
Closes #85
Related to gotify/server#416

I've adopted some of the code and translated it to Kotlin, but most parts are rewritten.
Here's a summary:

  • You can now configure a client certificate (PKCS#12) including password in the advanced settings (on the login screen)
  • The certificate contents are no longer stored in the shared preferences but the certificates are copied as a whole over to the app's file directory; the helper functions all just require input streams, so we cut the additional loading from shared preferences into another local bytestream beforehand, which then was forwared to the helper functions
  • Because of the new cert retrieval logic, upgrades from previous app versions will lead to ignored ca certs
  • We can not preview the CN of the client cert as with the CA cert as it's encrypted
  • I just know login and basic messages work when configuring no CA but a client cert

Testing the following cases is still to be done:

  • Fully functional when not configuring anything
  • Fully functional when only configuring CA cert
  • Fully functional when only configuring client cert (fixed in 60946e4)
  • Fully functional when configuring CA cert and client cert

Maybe taking a look at image loading is important as well.

And I will add some kind of hint that you have to give a password, it seems like (according to my trial and error) the Java client key implementation requires one.


For reference my reverse proxy settings (Caddy, docs for client_authentication):

(require_client_cert) {
    tls {
        client_auth {
            mode require
            trusted_leaf_cert_file client.crt
        }
    }
}

my.domain:1234 {
    import require_client_cert
    # gotify on port 8080
    reverse_proxy localhost:8080
    tls internal
}

@cyb3rko cyb3rko marked this pull request as draft April 19, 2024 00:16
@cyb3rko
Copy link
Contributor Author

cyb3rko commented Apr 21, 2024

@jmattheis I've just wondered what the real advantage of the import of a CA cert inside the app is over the system-wide native CA import.
Or do some older Android versions not have this feature?

@jmattheis
Copy link
Member

I don't know. This feature was added added more than 5 years ago. I could imagine that some users may not want to globally trust the CA cert and what to only configure this for certain apps.

@cyb3rko cyb3rko marked this pull request as ready for review April 23, 2024 20:46
@cyb3rko cyb3rko marked this pull request as draft April 23, 2024 21:02
@cyb3rko
Copy link
Contributor Author

cyb3rko commented Apr 23, 2024

Oh wait, I wanted to implement some kind of hint to add a password when selecting a client cert.
The crypto library apparently enforces a non-empty password.

And btw, while merging the master another problem occured:
The refactor to coil also updated the okhttp version.
And this code

@Suppress("DEPRECATION")
builder.sslSocketFactory(context.socketFactory)

can not be suppressed anymore, it's now an error. So we can't just leave the trust manager empty anymore.

PS: Fixed it by inserting default trust managers

@cyb3rko cyb3rko marked this pull request as ready for review April 23, 2024 22:23
Copy link
Member

@jmattheis jmattheis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't tested the new feature yet.

@desbma
Copy link

desbma commented May 24, 2024

Any update on this?
I would love to get this feature merged, and since there has already been some significant discussion in the review, it would be a shame to see this stall.

@jmattheis
Copy link
Member

FYI: I'll review this pr on the weekend.

@cyb3rko
Copy link
Contributor Author

cyb3rko commented Jun 5, 2024

And FYI: The implementation of the changes mentioned in the two remaining PR comments is still pending.
Atm struggling again with creating working certificates. I should have documented the steps last time...

@cyb3rko
Copy link
Contributor Author

cyb3rko commented Jun 6, 2024

That should be it for now. The mentioned improvements are all implemented.

Copy link
Member

@jmattheis jmattheis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for applying my many requests (:, I'm now pretty satisfied with the solution.

@jmattheis jmattheis merged commit b9b767f into gotify:master Jun 13, 2024
1 check passed
@olegbilovus
Copy link

olegbilovus commented Jun 17, 2024

I tested this with Cloudflare's mTLS and it works.

If anyone in the future would like to use this feature with Cloudflare, here are the steps:

  1. Create a CF Tunnel for gotify to a subdomain like gotify.example.com
    1.1. Check from a browser that you can connect to the instance
  2. Create a Client Certificate:
    2.1. In the CF Dashboard go to SSL/TLS -> Client Certificates
    2.2. In Hosts click Edit, add your subdomain for gotify and save
    2.3. In the same page click Create Certificate
    2.4. Leave the default parameters and click on Create. Keep the page with the Certificate and Private Key open
  3. Convert PEM to PKCS#12. Gotify requires PKCS#12:
    3.1 Copy the Certificate and save it in a file cert.pem
    3.2 Copy the Private Key and save it in a file key.pem
    3.3 Run the following command to generate a PKCS#12. It will ask you for a password and save to a file cert.p12
    openssl pkcs12 -export -out cert.p12 -in cert.pem -inkey key.pem
    3.4 Copy the file cert.p12 to your phone
  4. Enforce mTLS with WAF:
    4.1 In the Dashboard go to Security -> WAF
    4.2 Create a new rule and set the following conditions:
    4.2.1 Field = Hostname, Operator = is in, Value = gotify.example.com
    4.2.2 Add another condition with AND
    4.2.3 Field = Client Certificate Verified, equals = disabled
    4.2.4 This should be your Expression Preview:
    (http.host in {"gotify.example.com"} and not cf.tls_client_auth.cert_verified)
    4.3 Choose action: Block
    4.4 Save and wait a few seconds
    4.5 Try again from a browser to go to your subdomain and you should see that CF blocked the request

If you try to connect to the gotify server in the app after setting the certificate, it will still not work because CF by default will block any non interactive request. For this reason a new WAF rule has to be added to bypass this check for gotify.

  1. Bypass CF checks for gotify:
    5.1 Create a new rule in the WAF page and set the following condition:
    5.1.1 Field = Hostname, Operator = is in, Value = gotify.example.com
    5.2 Choose action: Skip
    5.3 Click on More components to skip and select Security Level
    5.4 Save
  2. Go to the WAF page and make sure that the mTLS rule is before the Bypass rule
  3. Setup gotify app:
    7.1 Open the gotify app and input your subdomain as the URL, you may need to add https://
    7.2 Near the Check URL button, press on the settings icon
    7.3 Press on Select Client Certificate (PKCS#12) and select the cert.p12 file
    7.4 Insert the password you added when creating the PKCS#12
    7.5 Press on Done and Check URL

You should now be able to login with your gotify's credentials.

@cyb3rko
Copy link
Contributor Author

cyb3rko commented Jun 17, 2024

@olegbilovus Awesome, thanks for testing and for posting your tips.
Maybe we should add some hint on how to generate that pkcs12 cert in the documentation.

@cyb3rko cyb3rko deleted the client-certificate-auth branch June 18, 2024 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

feature request: option to send certificate for NGINX ssl client verification
4 participants