diff options
| author | David Lönnhager <david.l@mullvad.net> | 2024-01-08 16:53:26 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2024-01-08 16:53:26 +0100 |
| commit | 63aa10fefc87e0a37bc482cccaaa56b14a2fbc59 (patch) | |
| tree | 761cc8d732e9d36d623965c215870136a5189526 | |
| parent | 4d33d452feaec4c82f134803c152486282ad36c8 (diff) | |
| parent | 167e9a9a1f4d856e034fee55cad01065e24c427f (diff) | |
| download | mullvadvpn-63aa10fefc87e0a37bc482cccaaa56b14a2fbc59.tar.xz mullvadvpn-63aa10fefc87e0a37bc482cccaaa56b14a2fbc59.zip | |
Merge branch 'doc-settings-patch'
| -rw-r--r-- | docs/patch-examples/override-relay-ips.json | 7 | ||||
| -rw-r--r-- | docs/settings-patch-format.md | 59 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings/patch.rs | 31 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 1 |
4 files changed, 91 insertions, 7 deletions
diff --git a/docs/patch-examples/override-relay-ips.json b/docs/patch-examples/override-relay-ips.json new file mode 100644 index 0000000000..acf5228678 --- /dev/null +++ b/docs/patch-examples/override-relay-ips.json @@ -0,0 +1,7 @@ +{ + "relay_overrides": [ + { "hostname": "test1", "ipv4_addr_in": "1.3.3.7" }, + { "hostname": "test2", "ipv6_addr_in": "::1" }, + { "hostname": "test3", "ipv4_addr_in": "1.2.3.4", "ipv6_addr_in": "::1" } + ] +} diff --git a/docs/settings-patch-format.md b/docs/settings-patch-format.md new file mode 100644 index 0000000000..2feb736db0 --- /dev/null +++ b/docs/settings-patch-format.md @@ -0,0 +1,59 @@ +# Settings patches + +Mullvad settings patch is a JSON format used to apply changes to Mullvad app settings. The purpose +is to make it easy to share or distribute useful configurations of the app, for example to make it +work better in censored locations. + +A patch consists of a JSON object. Each key in the object refers to a setting to be edited by the +patch. The type of the value depends on the setting/key itself. How the setting is updated (merge +strategy) also depends on the setting/key. + +If any part of a patch is not supported on a platform (or any platform), for any reason, the app +must reject the entire patch and not apply any changes to the settings. If possible, the reason for +the rejection should be clear to the user. + +The format is shared by apps on all platforms. Not all platforms are guaranteed to support all +settings at all times, however. + +## Supported settings + +Only a subset of all available app settings are patchable, and all of these settings are described +below. + +### Relay overrides + +The following settings patch sets the *relay IP override* setting for servers `a` and `b`: + +```json +{ + "relay_overrides": [ + { "hostname": "a", "ipv4_addr_in": "1.2.3.4" }, + { "hostname": "b", "ipv4_addr_in": "1.2.3.4", "ipv6_addr_in": "::1" } + ] +} +``` + +The merge strategy for overrides is "append or replace": + +* Overrides for hostnames not present in the array must remain unchanged. For example, in the above + patch, overrides for some hostname `c` are completely unaffected. +* For any given hostname, only specified overrides should change. For example, the above patch has + no effect on the value of `ipv6_addr_in` for the hostname `a`. +* Overrides that *are* specified are added or replaced. For example, `ipv4_addr_in` should be set + to `1.2.3.4`, regardless of what the override was previously set to. + +There is no way to remove an existing override (without replacing it) using a patch. + +## Versioning and backward compatibility + +Patches are not versioned as backward compatibility is not considered important, though +compatibility should not be broken for no good reason. + +## Security + +Patches must not edit any settings that may compromise security. For example, enabling custom DNS +should not be allowed. + +## Examples + +See [patch-examples](./patch-examples) for examples of patch files. diff --git a/mullvad-daemon/src/settings/patch.rs b/mullvad-daemon/src/settings/patch.rs index f3682b4322..50c0da6304 100644 --- a/mullvad-daemon/src/settings/patch.rs +++ b/mullvad-daemon/src/settings/patch.rs @@ -131,8 +131,19 @@ pub async fn merge_validate_patch( settings: &mut SettingsPersister, json_patch: &str, ) -> Result<(), Error> { + let new_settings = merge_validate_patch_inner(settings, json_patch)?; + + settings + .update(move |settings| *settings = new_settings) + .await + .map_err(Error::Settings)?; + + Ok(()) +} + +fn merge_validate_patch_inner(settings: &Settings, json_patch: &str) -> Result<Settings, Error> { let mut settings_value: serde_json::Value = - serde_json::to_value(settings.to_settings()).map_err(Error::SerializeSettings)?; + serde_json::to_value(settings).map_err(Error::SerializeSettings)?; let patch_value: serde_json::Value = serde_json::from_str(json_patch).map_err(Error::ParsePatch)?; @@ -142,12 +153,7 @@ pub async fn merge_validate_patch( let new_settings: Settings = serde_json::from_value(settings_value).map_err(Error::DeserializePatched)?; - settings - .update(move |settings| *settings = new_settings) - .await - .map_err(Error::Settings)?; - - Ok(()) + Ok(new_settings) } /// Replace overrides for existing values in the array if there's a matching hostname. For hostnames @@ -400,6 +406,17 @@ fn test_overflow() { )); } +/// Test whether valid patches from documentation are accepted by the implementation +#[test] +fn test_valid_patch_files() { + const OVERRIDE_PATCH: &str = + include_str!("../../../docs/patch-examples/override-relay-ips.json"); + + let prev_settings = Settings::default(); + let _ = merge_validate_patch_inner(&prev_settings, OVERRIDE_PATCH) + .expect("failed to apply relay overrides"); +} + #[test] fn test_patch_relay_override() { const PERMITTED_SUBKEYS: &PermittedKey = &PermittedKey::object(&[( diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 6978e5a666..e4ef20cbcc 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -100,6 +100,7 @@ service ManagementService { rpc CheckVolumes(google.protobuf.Empty) returns (google.protobuf.Empty) {} // Apply a JSON blob to the settings + // See ../../docs/settings-patch-format.md for a description of the format rpc ApplyJsonSettings(google.protobuf.StringValue) returns (google.protobuf.Empty) {} } |
