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

Bitwarden Android Beta (2024.12.0 (19597)) fails to sync with SSH keys #5322

Closed
segln opened this issue Dec 23, 2024 · 10 comments · Fixed by #5339
Closed

Bitwarden Android Beta (2024.12.0 (19597)) fails to sync with SSH keys #5322

segln opened this issue Dec 23, 2024 · 10 comments · Fixed by #5339
Labels
bug Something isn't working

Comments

@segln
Copy link

segln commented Dec 23, 2024

Vaultwarden Support String

Your environment (Generated via diagnostics page)

EXPERIMENTAL_CLIENT_FEATURE_FLAGS=ssh-key-vault-item,ssh-agent

  • Vaultwarden version: v1.32.7
  • Web-vault version: v2024.6.2c
  • OS/Arch: linux/x86_64
  • Running within a container: true (Base: Debian)
  • Database type: PostgreSQL
  • Database version: PostgreSQL 16.3 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 14.2.1 20240805, 64-bit
  • Environment settings overridden!: true
  • Uses a reverse proxy: true
  • IP Header check: true (X-Real-IP)
  • Internet access: true
  • Internet access via a proxy: false
  • DNS Check: true
  • Browser/Server Time Check: true
  • Server/NTP Time Check: true
  • Domain Configuration Check: false
  • HTTPS Check: true
  • Websocket Check: true
  • HTTP Response Checks: true

Config & Details (Generated via diagnostics page)

Show Config & Details

Environment settings which are overridden: ADMIN_TOKEN

Config:

{
  "_duo_akey": null,
  "_enable_duo": false,
  "_enable_email_2fa": true,
  "_enable_smtp": true,
  "_enable_yubico": true,
  "_icon_service_csp": "",
  "_icon_service_url": "",
  "_ip_header_enabled": true,
  "_max_note_size": 10000,
  "_smtp_img_src": "***:",
  "admin_ratelimit_max_burst": 3,
  "admin_ratelimit_seconds": 300,
  "admin_session_lifetime": 20,
  "admin_token": "***",
  "allowed_connect_src": "",
  "allowed_iframe_ancestors": "",
  "attachments_folder": "data/attachments",
  "auth_request_purge_schedule": "30 * * * * *",
  "authenticator_disable_time_drift": false,
  "data_folder": "data",
  "database_conn_init": "",
  "database_max_conns": 10,
  "database_timeout": 30,
  "database_url": "**********://********************,*********************************",
  "db_connection_retries": 15,
  "disable_2fa_remember": false,
  "disable_admin_token": false,
  "disable_icon_download": false,
  "domain": "*****://***************************",
  "domain_origin": "*****://***************************",
  "domain_path": "",
  "domain_set": true,
  "duo_context_purge_schedule": "30 * * * * *",
  "duo_host": null,
  "duo_ikey": null,
  "duo_skey": null,
  "duo_use_iframe": false,
  "email_2fa_auto_fallback": false,
  "email_2fa_enforce_on_verified_invite": false,
  "email_attempts_limit": 3,
  "email_change_allowed": true,
  "email_expiration_time": 600,
  "email_token_size": 6,
  "emergency_access_allowed": true,
  "emergency_notification_reminder_schedule": "0 3 * * * *",
  "emergency_request_timeout_schedule": "0 7 * * * *",
  "enable_db_wal": true,
  "enable_websocket": true,
  "enforce_single_org_with_reset_pw_policy": false,
  "event_cleanup_schedule": "0 10 0 * * *",
  "events_days_retain": null,
  "experimental_client_feature_flags": "ssh-key-vault-item,ssh-agent",
  "extended_logging": true,
  "helo_name": null,
  "hibp_api_key": "***",
  "http_request_block_non_global_ips": true,
  "http_request_block_regex": null,
  "icon_blacklist_non_global_ips": true,
  "icon_blacklist_regex": null,
  "icon_cache_folder": "data/icon_cache",
  "icon_cache_negttl": 259200,
  "icon_cache_ttl": 2592000,
  "icon_download_timeout": 10,
  "icon_redirect_code": 302,
  "icon_service": "internal",
  "incomplete_2fa_schedule": "30 * * * * *",
  "incomplete_2fa_time_limit": 3,
  "increase_note_size_limit": false,
  "invitation_expiration_hours": 120,
  "invitation_org_name": "Vaultwarden",
  "invitations_allowed": false,
  "ip_header": "X-Real-IP",
  "job_poll_interval_ms": 30000,
  "log_file": null,
  "log_level": "warn",
  "log_timestamp_format": "%Y-%m-%d %H:%M:%S.%3f",
  "login_ratelimit_max_burst": 10,
  "login_ratelimit_seconds": 60,
  "org_attachment_limit": null,
  "org_creation_users": "",
  "org_events_enabled": false,
  "org_groups_enabled": false,
  "password_hints_allowed": false,
  "password_iterations": 600000,
  "push_enabled": false,
  "push_identity_uri": "https://identity.bitwarden.com",
  "push_installation_id": "***",
  "push_installation_key": "***",
  "push_relay_uri": "https://push.bitwarden.com",
  "reload_templates": false,
  "require_device_email": false,
  "rsa_key_filename": "data/rsa_key",
  "send_purge_schedule": "0 5 * * * *",
  "sendmail_command": null,
  "sends_allowed": true,
  "sends_folder": "data/sends",
  "show_password_hint": false,
  "signups_allowed": false,
  "signups_domains_whitelist": "",
  "signups_verify": false,
  "signups_verify_resend_limit": 6,
  "signups_verify_resend_time": 3600,
  "smtp_accept_invalid_certs": false,
  "smtp_accept_invalid_hostnames": false,
  "smtp_auth_mechanism": null,
  "smtp_debug": false,
  "smtp_embed_images": true,
  "smtp_explicit_tls": null,
  "smtp_from": "*****************",
  "smtp_from_name": "Vaultwarden",
  "smtp_host": "********************",
  "smtp_password": "***",
  "smtp_port": 587,
  "smtp_security": "starttls",
  "smtp_ssl": null,
  "smtp_timeout": 15,
  "smtp_username": "*******",
  "templates_folder": "data/templates",
  "tmp_folder": "data/tmp",
  "trash_auto_delete_days": null,
  "trash_purge_schedule": "0 5 0 * * *",
  "use_sendmail": false,
  "use_syslog": false,
  "user_attachment_limit": null,
  "user_send_limit": null,
  "web_vault_enabled": true,
  "web_vault_folder": "web-vault/",
  "yubico_client_id": null,
  "yubico_secret_key": null,
  "yubico_server": null
}

Vaultwarden Build Version

v1.32.7

Deployment method

Official Container Image

Custom deployment method

No response

Reverse Proxy

nginx/1.27.2

Host/Server Operating System

Linux

Operating System Version

6.12.4-arch1-1 x86_64 GNU/Linux

Clients

Android

Client Version

Android Beta 2024.12.0 (19597)

Steps To Reproduce

  1. Sync the vault

Expected Result

Sync success

Actual Result

Sync failed

Logs

Bitwarden Android Beta: adb logcat log

The log reports Network Error: https://api.bitwarden.com/sync but I'm connecting to Vaultwarden so that can be ignored.

12-23 18:54:56.815 32448 14626 W NetworkResultCall: Network Error: https://api.bitwarden.com/sync
12-23 18:54:56.816 32448 14626 W NetworkResultCall: kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 719619: Expected string literal but 'null' literal was found at path: $.ciphers[234].sshKey.keyFingerprint
12-23 18:54:56.816 32448 14626 W NetworkResultCall: Use 'coerceInputValues = true' in 'Json {}' builder to coerce nulls if property has a default value.
12-23 18:54:56.816 32448 14626 W NetworkResultCall: JSON input: .....ll,"sshKey":{"keyFingerprint":null,"privateKey":null,"public.....
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.AbstractJsonLexer.fail(AbstractJsonLexer.kt:580)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.AbstractJsonLexer.unexpectedToken(AbstractJsonLexer.kt:221)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StringJsonLexer.consumeNextToken(StringJsonLexer.kt:76)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StringJsonLexer.consumeKeyString(StringJsonLexer.kt:88)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.AbstractJsonLexer.consumeString(AbstractJsonLexer.kt:365)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeString(StreamingJsonDecoder.kt:339)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.AbstractDecoder.decodeStringElement(AbstractDecoder.kt:58)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson$Cipher$SshKey$$serializer.deserialize(SyncResponseJson.kt:736)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson$Cipher$SshKey$$serializer.deserialize(SyncResponseJson.kt:736)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.AbstractDecoder.decodeNullableSerializableElement(AbstractDecoder.kt:78)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson$Cipher$$serializer.deserialize(SyncResponseJson.kt:430)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson$Cipher$$serializer.deserialize(SyncResponseJson.kt:430)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:168)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.CompositeDecoder$DefaultImpls.decodeSerializableElement$default(Decoding.kt:538)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.internal.CollectionLikeSerializer.readElement(CollectionSerializers.kt:80)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.internal.AbstractCollectionSerializer.readElement$default(CollectionSerializers.kt:51)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.internal.AbstractCollectionSerializer.merge(CollectionSerializers.kt:36)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.internal.AbstractCollectionSerializer.deserialize(CollectionSerializers.kt:43)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.encoding.AbstractDecoder.decodeNullableSerializableElement(AbstractDecoder.kt:78)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson$$serializer.deserialize(SyncResponseJson.kt:26)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson$$serializer.deserialize(SyncResponseJson.kt:26)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at kotlinx.serialization.json.Json.decodeFromString(Json.kt:165)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at retrofit2.converter.kotlinx.serialization.Serializer$FromString.fromResponseBody(Serializer.kt:26)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at retrofit2.converter.kotlinx.serialization.DeserializationStrategyConverter.convert(DeserializationStrategyConverter.kt:11)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at retrofit2.converter.kotlinx.serialization.DeserializationStrategyConverter.convert(DeserializationStrategyConverter.kt:7)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:246)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:156)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
12-23 18:54:56.816 32448 14626 W NetworkResultCall: 	at java.lang.Thread.run(Thread.java:1012)

Screenshots or Videos

No response

Additional Context

Details

Bitwarden Android Beta requires ciphers.sshKey.keyFingerprint, but Vaultwarden's /api/sync does not return this property.

This bug does not occur if the SSH key is not in the vault.

Vaultwarden's SSH key feature works well with the Desktop client.

@segln segln added the bug Something isn't working label Dec 23, 2024
@BlackDex
Copy link
Collaborator

BlackDex commented Dec 23, 2024

Vaultwarden doesn't have anything special for this except that it returns the json blob exactly as received. It probably means the stored data is not valid i think. But i haven't checked it.

Does the fingerprint show in the desktop client?

@segln
Copy link
Author

segln commented Dec 23, 2024

Yes, on desktop client with Linux and Windows SSH key features (add, list, edit, etc...) works well.

@BlackDex
Copy link
Collaborator

Could you check the database for this specific key and what is in the data column?

@segln
Copy link
Author

segln commented Dec 23, 2024

Here is the data for the SSH key row.

INSERT INTO "ciphers" ("uuid", "created_at", "updated_at", "user_uuid", "organization_uuid", "atype", "name", "notes", "fields", "data", "password_history", "deleted_at", "reprompt", "key") VALUES
('065d4800-610f-48a9-958d-6ba584489ddf',	'2024-12-23 10:16:13.321893',	'2024-12-23 10:16:13.325395',	'ef669930-9712-4522-95b6-b7dc72c1ae8a',	NULL,	5,	'2.SmP/Qsnb3g+SYNH3tiJLRA==|2H/qXArvh7UuD7OyyVzqeA==|/BTVGv4O4xCbpleOhh1P3x9LYFa1YAGiGkibcaCAkx4=',	NULL,	NULL,	'{"keyFingerprint":"2.TTuJoqtXsm6ATGoog3LHrA==|58fTr0btpcL6qrwLHJQsXyHSRqsmOESGuuWm3QwTgMOXXh76+fw0Z+8uDVta7eSnXHdNOQVcM8lEJ75LPY4qlw==|kXXFZo56gVRc3yJW5J+QebNSRTncr6Wehc/rprDg57k=","privateKey":"2.L+x00lseczoU+vK395tESQ==|ZlsXRL3+6UBwxrHgzJCPp5YRQKlu4XI1oX8uhZO+/8V0sxgPtREyPpfTMujLcnzfhtGaF3I++S+NTGP9vlom4EXM7SQTJDfRTs1tpBEVSciruT334w7FBHfEq1AytHa+P/UlrhTo5lIY7jvag4HvnwdUPt+Yar9ERjEdkSdJZ/VaD3D+Bul0o5ESotehLX3gcn0Tz4sCTZtY8IJ4xe4oOezNxVibVYk+lHvOVxrtsICY2qDHpNXM9McQfWGzgBXfy56Yg+jxdNJLOHvLZZjZ2YdqZJZoCRb9DMs7wQW28Why6NbEtKnU/N9FJGTAVaC2vTZIXaHRnx5AQ7wTka2rn7jPndoRL2M3qPFUPC4Qs7UK7C7bu5mIM6nwCgPwAPSB/V48iIXcWEXYhRGiaAJMKQ5tolD1z/XqZnZgW1dJVwNcTseYYIlu6v3xn3za0yBmdJ5q8awYzokyf3hk7No1ukayo9f1WDODIVA6fx5BJYIy8NErH6nhISKvW8+6yVpJ2HZpyyxVdE4TcMhz40tOMA==|bzgdJAn0pPbWVEvK9k8jUuJKPrizaW/ILCjCbSGezgE=","publicKey":"2.Va0MLFITWE64r4m9Ao/YFA==|VMrFscfr/v+SLkSv4cbYbOR8UxODO6UkpL8t+7eiH2EMUvye6XchBFwMSsAwbiF+ZeWL1fpTiDZ4gIV5R86rY8vtkuDgl3tnZukO48EBhlhim/dxeGLh2CWZ7pR5YDbN|f9QsxBJI24jZIvs7cgiHM6MkBEq0UygY3JJMpBDZ+9k="}',	NULL,	NULL,	0,	NULL);

@BlackDex
Copy link
Collaborator

Not sure how your export was done because of the double "", but it sure looks like the data is there.

Ill have to check my self how this is returned to the client then.
But as you can see here it should return this as-is.
https://github.com/dani-garcia/vaultwarden/blob/ed4ad67e732c213beaec78970cdb68e48bee3dc1/src/db/models/cipher.rs#L346...L356

@segln
Copy link
Author

segln commented Dec 23, 2024

I've corrected it by changing the export format.
And yes, the data exists. I've reinstalled the Android Bitwarden Beta app multiple times and tried on different devices, but it's still the same. I'm not sure what the problem is.

@BlackDex
Copy link
Collaborator

You could try to install the dev version of the Android client which should output the request and response data of the http call. Which in turn should show if the data is there or not.

@segln
Copy link
Author

segln commented Dec 23, 2024

I completely wiped all SSH key rows from the database and reimported keys, and the symptoms disappeared.
I'm still not sure why the error occurred. For now, the problem has been resolved.

@Dubzer
Copy link

Dubzer commented Jan 3, 2025

hi there. I'm having the same problem and I've found the cause of it

here's a log from the app:

 W  kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 415049: Expected string literal but 'null' literal was found at path: $.ciphers[180].sshKey.keyFingerprint
 W  Use 'coerceInputValues = true' in 'Json {}' builder to coerce nulls if property has a default value.
 W  JSON input: .....ll,"sshKey":{"keyFingerprint":null,"privateKey":null,"public.....

turns out that keyFingerprint is null for an SSH key that I don't even have... or so I thought, until I looked in the Trash from the browser extension and there were some keys with no fields other than the name!
I'm not sure when these keys were created. maybe there was a bug in some client that was fixed. or in the SSH agent

either way, I don't think it makes sense for the backend to allow such keys, since clients will reject anything that appears to be an invalid key

@BlackDex

@BlackDex
Copy link
Collaborator

BlackDex commented Jan 3, 2025

The problem is, that we actually do not care what the data object is and how this is formed.
It does manifest these kind of issue unfortunately though.

The main reason for us to not care on what the clients are sending as data blob, is that new features or additions which are done via that data blob will just work out-of-the-box, without us having to update it every time.

It has the downside of these kind of issue. What we currently do is fix the output of the sync.
But, we have not yet done this for invalid ssh-keys, so those could cause an issue it seems.

But, yes this is because of the clients not sending the correct data back to the server, and could be a faulty client indeed.

BlackDex added a commit to BlackDex/vaultwarden that referenced this issue Jan 4, 2025
If any of the mandatory ssh-key json data values are not a string or are an empty string, this will break the mobile clients.
This commit fixes this by checking if any of the values are missing or invalid and converts the json data to `null`.
It will ensure the clients can sync and show the vault.

Fixes dani-garcia#5343
Fixes dani-garcia#5322

Signed-off-by: BlackDex <black.dex@gmail.com>
dani-garcia pushed a commit that referenced this issue Jan 4, 2025
* Refactor the uri match change

Refactored the uri match fix to also convert numbers within a string to an int.
If it fails it will be null.

Signed-off-by: BlackDex <black.dex@gmail.com>

* Fix ssh-key sync issues

If any of the mandatory ssh-key json data values are not a string or are an empty string, this will break the mobile clients.
This commit fixes this by checking if any of the values are missing or invalid and converts the json data to `null`.
It will ensure the clients can sync and show the vault.

Fixes #5343
Fixes #5322

Signed-off-by: BlackDex <black.dex@gmail.com>

---------

Signed-off-by: BlackDex <black.dex@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants