diff --git a/docs/configuration.md b/docs/configuration.md index 5ed64a77..ebc16585 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -57,6 +57,7 @@ The `global` section contains the following fields: | `turn_uris` | `array` | The TURN URIs | `[]` | | `turn_secret` | `string` | The TURN secret | `""` | | `turn_ttl` | `integer` | The TURN TTL in seconds | `86400` | +| `ignored_keys` | `[string]` | Server keys that should be ignored | `["l/O9hxMVKB6Lg+3Hqf0FQQZhVESQcMzbPN1Cz2nM3og"]` ([ESS compromised key](https://github.com/element-hq/ess-helm/security/advisories/GHSA-qwcj-h6m8-vp6q)) | | `media` | `table` | See the [media configuration](#media) | See the [media configuration](#media) | | `emergency_password` | `string` | Set a password to login as the `conduit` user in case of emergency | N/A | | `well_known` | `table` | Used for [delegation](delegation.md) | See [delegation](delegation.md) | diff --git a/src/config/mod.rs b/src/config/mod.rs index 098dc20d..28d6404b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -8,7 +8,7 @@ use std::{ }; use bytesize::ByteSize; -use ruma::{OwnedServerName, RoomVersionId}; +use ruma::{api::federation::discovery::VerifyKey, serde::Base64, OwnedServerName, RoomVersionId}; use serde::{de::IgnoredAny, Deserialize}; use tokio::time::{interval, Interval}; use tracing::warn; @@ -89,6 +89,9 @@ pub struct IncompleteConfig { pub turn: Option, + #[serde(default = "default_ignored_keys")] + pub ignored_keys: Vec, + #[serde(default)] pub media: IncompleteMediaConfig, @@ -136,6 +139,8 @@ pub struct Config { pub turn: Option, + pub ignored_keys: Vec, + pub media: MediaConfig, pub emergency_password: Option, @@ -186,6 +191,7 @@ impl From for Config { media, emergency_password, catchall, + ignored_keys, } = val; let turn = turn.or_else(|| { @@ -204,6 +210,8 @@ impl From for Config { } }); + let ignored_keys = ignored_keys.into_iter().map(|key| key.key).collect(); + let well_known_client = well_known .client .map(String::from) @@ -283,6 +291,7 @@ impl From for Config { media, emergency_password, catchall, + ignored_keys, } } } @@ -786,3 +795,12 @@ pub fn default_default_room_version() -> RoomVersionId { fn default_s3_duration() -> u64 { 30 } + +fn default_ignored_keys() -> Vec { + vec![VerifyKey::new(Base64::new( + // Compromised Element Server Suite (ESS) signing key: + // + // https://github.com/element-hq/ess-helm/security/advisories/GHSA-qwcj-h6m8-vp6q + b"l/O9hxMVKB6Lg+3Hqf0FQQZhVESQcMzbPN1Cz2nM3og".to_vec(), + ))] +} diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index 27d84d9a..9957d88e 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -421,7 +421,7 @@ impl Service { } /// Filters the key map of multiple servers down to keys that should be accepted given the expiry time, - /// room version, and timestamp of the parameters + /// room version, and timestamp of the parameters, as well as ignoring keys that are listed in `ignored_keys`. pub fn filter_keys_server_map( &self, keys: BTreeMap, @@ -437,7 +437,7 @@ impl Service { } /// Filters the keys of a single server down to keys that should be accepted given the expiry time, - /// room version, and timestamp of the parameters + /// room version, and timestamp of the parameters, as well as removing any keys in `ignored_keys`. pub fn filter_keys_single_server( &self, keys: SigningKeys, @@ -449,22 +449,32 @@ impl Service { // https://spec.matrix.org/v1.10/server-server-api/#get_matrixkeyv2server || !rules.enforce_key_validity { + let filter_ignored_keys = |(_, base64): &(_, Base64)| { + !services().globals.config.ignored_keys.contains(base64) + }; + // Given that either the room version allows stale keys, or the valid_until_ts is // in the future, all verify_keys are valid let mut map: BTreeMap<_, _> = keys .verify_keys .into_iter() .map(|(id, key)| (id, key.key)) + .filter(filter_ignored_keys) .collect(); - map.extend(keys.old_verify_keys.into_iter().filter_map(|(id, key)| { - // Even on old room versions, we don't allow old keys if they are expired - if key.expired_ts > timestamp { - Some((id, key.key)) - } else { - None - } - })); + map.extend( + keys.old_verify_keys + .into_iter() + .filter_map(|(id, key)| { + // Even on old room versions, we don't allow old keys if they are expired + if key.expired_ts > timestamp { + Some((id, key.key)) + } else { + None + } + }) + .filter(filter_ignored_keys), + ); Some(map) } else { diff --git a/src/service/rooms/event_handler/mod.rs b/src/service/rooms/event_handler/mod.rs index 4f6b8369..c03583be 100644 --- a/src/service/rooms/event_handler/mod.rs +++ b/src/service/rooms/event_handler/mod.rs @@ -1421,7 +1421,8 @@ impl Service { Ok((sorted, eventid_info)) } - /// Filters down the given signing keys, only keeping those which could be valid for this event. + /// Filters down the given signing keys, only keeping those which could be valid for this event, + /// as well as ignoring those listed in `ignored_keys`. #[tracing::instrument(skip_all)] pub async fn filter_required_signing_keys( &self,