-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
feat: add support for Android user-installed certificates #19830
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
base: main
Are you sure you want to change the base?
Conversation
<base-config> | ||
<trust-anchors> | ||
<certificates src="system"/> | ||
<certificates src="user"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no way to make this opt-in, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, but everything I know about android development I learned in the last week while researching this issue.
mobile/pubspec.yaml
Outdated
flutter_user_certificates_android: | ||
git: | ||
url: https://github.com/jfly/flutter_user_certificates_android.git | ||
ref: "add-trusting" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If (big if, imo) we're keeping this repo reference then it should at least be pinned to a commit hash so we know what we're getting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I totally understand. Happy to do any of the following:
- Pin to a specific commit, as you mention.
- Depend on the unpatched
flutter_user_certificates_android
and vendor just the changes from Add support for trusting android user certificates johnstef99/flutter_user_certificates_android#2. - Vendor the whole thing. We're not talking about a lot of code here.
Just let me know which approach to take.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bo0tzz, friendly ping here. Which approach would you like me to take?
8546274
to
01f0a57
Compare
@jfly Hey, Thanks a lot for working on this. However, if we are to merge this in, rather than deprecating the existing "Allow Self signed certificates" toggle, we could just remove all the custom verification logic all together. We don't need a lot of different codepath to address the same issue. However, this would mean that we would also need to test this exhaustively to make sure it does not break anything from the current setup. So things like:
For all cases, we'd need to test both the dart side of things and the native side of things. Most network calls are from the dart side. Video player, Asset download are made from native side and we should validate these as well. If we are to remove the self signed toggle, we've to make sure everything works on iOS as well. You could just inline all the changes. We are migrating to using pigeon for all message passing between flutter and the platform side, so you can add an API to fetch the certificates and load it in the dart's SecurityContext. We might also need some documentation change to inform users about installing certificates to the OS cert store, rather than loading it in the app like they used to. The core team currently has got it's hands full with working towards the stable release. I know this is a significant undertaking for your free time, but if you can get this properly tested after removing the existing toggle and adding in your changes, we can happily merge this in. If you need help setting up specific test scenarios or access to different certificate setups, let us know - we can coordinate with the community to help test various configurations. |
@@ -0,0 +1,9 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<network-security-config> | |||
<base-config> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<base-config> | |
<base-config cleartextTrafficPermitted="true"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Does my PR change the default behavior somehow? The docs say:
Any values that are not set use the platform default values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The android:usesCleartextTraffic="true"
set in the manifest file still seems to be respected. But it is best to also set it in the network config since the platform default varies between different versions of Android
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it is best to also set it in the network config since the platform default varies between different versions of Android
That makes sense, but I don't see how this is related to my changes here. In the interest of keeping this PR targeted, should I send it a separate PR that adds this network_security_config.xml
file with the explicit cleartextTrafficPermitted="true"
?
Sure, I'm happy to take this on. I've converted my understanding of your requirements into a checklist below. Please let me know if I'm missing anything:
|
This is perfect! Thanks a lot for taking this on. As far the documentation, we can discuss this during the release of this PR. But yes, a callout in the release note might just do. It is also probably worth testing the migration from the older version with the setting on with a certificate to the new one without the toggle. Please feel free to ping me here or over discord if you need any help with the implementation or testing |
5182425
to
c6419f1
Compare
I ran into this while testing out <immich-app#19830>. When I add, change, or remove a client certificate under Immich's advanced settings, the change wouldn't take effect until some mysterious point in the future. For example: 1. Add a client certificate. It doesn't get used. 2. Remove certificate. *Now* the client certificate from step 1) is used. 3. Restart application. Now no client certificate is used. This all boils down to some missing `await`s. The user would change the cert, and we'd start asynchronously saving it to the store, and while the save is still happening, [`HttpSSLOptions` pulls the "old" value out of `SSLClientCertStoreVal`](https://github.com/immich-app/immich/blob/v1.136.0/mobile/lib/utils/http_ssl_options.dart#L30). With the appropriate `await`s, this behaves much more sanely.
0925f1e
to
187a465
Compare
I ran into this while testing out <immich-app#19830>. When I add, change, or remove a client certificate under Immich's advanced settings, the change wouldn't take effect until some mysterious point in the future. For example: 1. Add a client certificate. It doesn't get used. 2. Remove certificate. *Now* the client certificate from step 1) is used. 3. Restart application. Now no client certificate is used. This all boils down to some missing `await`s. The user would change the cert, and we'd start asynchronously saving it to the store, and while the save is still happening, [`HttpSSLOptions` pulls the "old" value out of `SSLClientCertStoreVal`](https://github.com/immich-app/immich/blob/v1.136.0/mobile/lib/utils/http_ssl_options.dart#L30). With the appropriate `await`s, this behaves much more sanely.
187a465
to
813adfc
Compare
I ran into this while testing out <immich-app#19830>. When I add, change, or remove a client certificate under Immich's advanced settings, the change wouldn't take effect until some mysterious point in the future. For example: 1. Add a client certificate. It doesn't get used. 2. Remove certificate. *Now* the client certificate from step 1) is used. 3. Restart application. Now no client certificate is used. This all boils down to some missing `await`s. The user would change the cert, and we'd start asynchronously saving it to the store, and while the save is still happening, [`HttpSSLOptions` pulls the "old" value out of `SSLClientCertStoreVal`](https://github.com/immich-app/immich/blob/v1.136.0/mobile/lib/utils/http_ssl_options.dart#L30). With the appropriate `await`s, this behaves much more sanely.
I ran into this while testing out <immich-app#19830>. When I add, change, or remove a client certificate under Immich's advanced settings, the change wouldn't take effect until some mysterious point in the future. For example: 1. Add a client certificate. It doesn't get used. 2. Remove certificate. *Now* the client certificate from step 1) is used. 3. Restart application. Now no client certificate is used. This all boils down to some missing `await`s. The user would change the cert, and we'd start asynchronously saving it to the store, and while the save is still happening, [`HttpSSLOptions` pulls the "old" value out of `SSLClientCertStoreVal`](https://github.com/immich-app/immich/blob/v1.136.0/mobile/lib/utils/http_ssl_options.dart#L30). With the appropriate `await`s, this behaves much more sanely.
I ran into this while testing out <#19830>. When I add, change, or remove a client certificate under Immich's advanced settings, the change wouldn't take effect until some mysterious point in the future. For example: 1. Add a client certificate. It doesn't get used. 2. Remove certificate. *Now* the client certificate from step 1) is used. 3. Restart application. Now no client certificate is used. This all boils down to some missing `await`s. The user would change the cert, and we'd start asynchronously saving it to the store, and while the save is still happening, [`HttpSSLOptions` pulls the "old" value out of `SSLClientCertStoreVal`](https://github.com/immich-app/immich/blob/v1.136.0/mobile/lib/utils/http_ssl_options.dart#L30). With the appropriate `await`s, this behaves much more sanely.
76f99a8
to
1b7af17
Compare
This is part of immich-app#15230. Frustratingly, Dart/Flutter ignores user-installed certificates. Working around this requires rooting your Android device to install certificates as "system" certs, which isn't an option for everyone. This is a known issue with Dart, see dart-lang/sdk#50435 and flutter/flutter#56607 for details. I have read through <immich-app#15230> and <immich-app#13555>, and I understand that switching to [`cronnet_http`](immich-app#14335 (comment)) would also resolve this. While that may be the correct long-term approach, it looks like there are [a lot of network codepaths](immich-app#15230 (comment)) in Immich, and as I know basically nothing about Dart, nor Flutter, nor Immich's codebase, I thought this would be a better short term approach. For the record: the important code here is copied from my fork of `johnstef99/flutter_user_certificates_android`. See this PR for more context: <johnstef99/flutter_user_certificates_android#2>.
1b7af17
to
db8d15e
Compare
This is unnecessary now that we support user-installed certificates on Android. Users should be adding certs to their system instead, which will work in more places (such as browsers), and won't blindly trust the cert.
db8d15e
to
b396b38
Compare
@shenlong-tanwen, I'm ready for you to take another look at this PR. I've updated my checklist with what I have done and not done. Notably, with these changes, self-signed certificates work, but Android only lets you install them if they have the X.509v3 So, we need to decide what to do here. A couple proposals:
|
Personally I agree with your second point, since this feature here will not strictly replace the existing flag it could be a breaking change for some users to remove it. I'm also not sure about the options for IOS users to install custom CA. |
This works fine. My wife is on iOS and has our custom CA installed on her phone, and immich works great.
Yeah, that would be a ssh-style TOFU scheme. I don't have the same safety concerns, but that's a whole bunch of code that I don't really see the value in anyone writing, nor having the core immich team maintain. IMO, it would be better for immich to take an opinionated stance and say that anyone running their immich server without "universally trusted" certs is required to be have that cert chained off of a root of trust with Of course I am happy to do whatever the core team needs to get this PR landed. IMO, the best solution would be to rebrand the existing option as dangerous and mark it deprecated, with the intent to remove it before the stable release. |
Description
This is part of #15230.
Frustratingly, Dart/Flutter ignores user-installed certificates. Working around this requires rooting your Android device to install certificates as "system" certs, which isn't an option for everyone.
This is a known issue with Dart, see
dart-lang/sdk#50435 and
flutter/flutter#56607 for details.
I have read through
#15230 and #13555, and I understand that switching to
cronnet_http
would also resolve this. While that may be the correct long-term approach, it looks like there are a lot of network codepaths in Immich, and as I know basically nothing about Dart, nor Flutter, nor Immich's codebase, I thought this would be a better short term approach.This depends on my fork of
johnstef99/flutter_user_certificates_android
, which I've sent a PR for herejohnstef99/flutter_user_certificates_android#2. If y'all don't like the supply chain implications of that, I'm happy to inline the implementation here instead.
How Has This Been Tested?
I have step-ca and DNS configured so that immich.mm resolves to an immich server with a certificate issued by my step-ca certificate authority. I've installed that certificate authority as a user-installed certificate on my Android phone.
Screenshots (if appropriate)
The help text on this option is no longer true. I'd suggest that we deprecate it in favor of encouraging people to add certificates to their OS:
Checklist:
src/services/
uses repositories implementations for database calls, filesystem operations, etc.src/repositories/
is pretty basic/simple and does not have any immich specific logic (that belongs insrc/services/
)