summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/patch-examples/override-relay-ips.json7
-rw-r--r--docs/settings-patch-format.md59
-rw-r--r--mullvad-daemon/src/settings/patch.rs31
-rw-r--r--mullvad-management-interface/proto/management_interface.proto1
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) {}
}