Skip to content

Commit ec9ffaf

Browse files
committed
Merge remote-tracking branch 'origin/main' into hs/update-dependencies
2 parents 45e4724 + 052d42f commit ec9ffaf

File tree

19 files changed

+279
-159
lines changed

19 files changed

+279
-159
lines changed

.github/workflows/main.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ jobs:
9090
homerunnersha: ${{ steps.gitsha.outputs.sha }}
9191
steps:
9292
- name: Checkout matrix-org/complement
93-
uses: actions/checkout@v3
93+
uses: actions/checkout@v4
9494
with:
9595
repository: matrix-org/complement
9696
- name: Get complement git sha
9797
id: gitsha
9898
run: echo sha=`git rev-parse --short HEAD` >> "$GITHUB_OUTPUT"
9999
- name: Cache homerunner
100100
id: cached
101-
uses: actions/cache@v3
101+
uses: actions/cache@v4
102102
with:
103103
path: homerunner
104104
key: ${{ runner.os }}-homerunner-${{ steps.gitsha.outputs.sha }}
@@ -123,23 +123,28 @@ jobs:
123123
needs:
124124
- test
125125
- build-homerunner
126+
services:
127+
redis:
128+
image: redis
129+
ports:
130+
- 6379:6379
126131
steps:
127132
- name: Install Complement Dependencies
128133
run: |
129134
sudo apt-get update && sudo apt-get install -y libolm3
130135
- name: Load cached homerunner bin
131-
uses: actions/cache@v3
136+
uses: actions/cache@v4
132137
with:
133138
path: homerunner
134139
key: ${{ runner.os }}-homerunner-${{ needs.build-synapse.outputs.homerunnersha }}
135140
fail-on-cache-miss: true # Shouldn't happen, we build this in the needs step.
136141
- name: Checkout matrix-hookshot
137-
uses: actions/checkout@v3
142+
uses: actions/checkout@v4
138143
with:
139144
path: matrix-hookshot
140145
# Setup node & run tests
141146
- name: Use Node.js
142-
uses: actions/setup-node@v3
147+
uses: actions/setup-node@v4
143148
with:
144149
node-version-file: matrix-hookshot/.node-version
145150
- uses: Swatinem/rust-cache@v2
@@ -150,8 +155,9 @@ jobs:
150155
timeout-minutes: 10
151156
env:
152157
HOMERUNNER_SPAWN_HS_TIMEOUT_SECS: 100
153-
HOMERUNNER_IMAGE: ghcr.io/element-hq/synapse/complement-synapse:latest
158+
HOMERUNNER_IMAGE: ghcr.io/element-hq/synapse/complement-synapse:nightly
154159
NODE_OPTIONS: --dns-result-order ipv4first
160+
REDIS_DATABASE_URI: "redis://localhost:6379"
155161
run: |
156162
docker pull $HOMERUNNER_IMAGE
157163
cd matrix-hookshot

changelog.d/989.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Support for E2E Encrypted rooms is now considered stable and can be enabled in production. Please see the [documentation](https://matrix-org.github.io/matrix-hookshot/latest/advanced/encryption.html)
2+
on the requirements for enabling support.

config.sample.yml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# This is an example configuration file
22

3+
logging:
4+
# Logging settings. You can have a severity debug,info,warn,error
5+
level: info
6+
colorize: true
7+
json: false
8+
timestampFormat: HH:mm:ss:SSS
39
bridge:
410
# Basic homeserver configuration
511
domain: example.com
@@ -11,12 +17,6 @@ passFile:
1117
# A passkey used to encrypt tokens stored inside the bridge.
1218
# Run openssl genpkey -out passkey.pem -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:4096 to generate
1319
./passkey.pem
14-
logging:
15-
# Logging settings. You can have a severity debug,info,warn,error
16-
level: info
17-
colorize: true
18-
json: false
19-
timestampFormat: HH:mm:ss:SSS
2020
listeners:
2121
# HTTP Listener configuration.
2222
# Bind resource endpoints to ports and addresses.
@@ -143,10 +143,12 @@ listeners:
143143
# # For encryption to work, this must be configured.
144144
# redisUri: redis://localhost:6379
145145

146-
#queue:
147-
# # (Optional) Message queue configuration options for large scale deployments.
148-
# # For encryption to work, this must not be configured.
149-
# redisUri: redis://localhost:6379
146+
#encryption:
147+
# # (Optional) Configuration for encryption support in the bridge.
148+
# # If omitted, encryption support will be disabled.
149+
# storagePath:
150+
# # Path to the directory used to store encryption files. These files must be persist between restarts of the service.
151+
# ./cryptostore
150152

151153
#widgets:
152154
# # (Optional) EXPERIMENTAL support for complimentary widgets

docs/advanced/encryption.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
11
Encryption
22
==========
33

4-
<section class="warning">
5-
Encryption support is <strong>HIGHLY EXPERIMENTAL AND SUBJECT TO CHANGE</strong>. It should not be enabled for production workloads.
6-
For more details, see <a href="https://github.com/matrix-org/matrix-hookshot/issues/594">issue 594</a>.
4+
<section class="notice">
5+
Support for encryption is considered stable, but the underlying specification changes are not yet.
6+
7+
Hookshot supports end-to-bridge encryption via [MSC3202](https://github.com/matrix-org/matrix-spec-proposals/pull/3202), and [MSC4203](https://github.com/matrix-org/matrix-spec-proposals/pull/4203). Hookshot needs to be configured against a a homeserver that supports these features, such as [Synapse](#running-with-synapse).
8+
9+
Please check with your homeserver implementation before reporting bugs against matrix-hookshot.
710
</section>
811

9-
Hookshot supports end-to-bridge encryption via [MSC3202](https://github.com/matrix-org/matrix-spec-proposals/pull/3202). As such, encryption requires Hookshot to be connected to a homeserver that supports that MSC, such as [Synapse](#running-with-synapse).
12+
1013

1114
## Enabling encryption in Hookshot
1215

1316
In order for Hookshot to use encryption, it must be configured as follows:
14-
- The `experimentalEncryption.storagePath` setting must point to a directory that Hookshot has permissions to write files into. If running with Docker, this path should be within a volume (for persistency). Hookshot uses this directory for its crypto store (i.e. long-lived state relating to its encryption keys).
17+
- The `encryption.storagePath` setting must point to a directory that Hookshot has permissions to write files into. If running with Docker, this path should be within a volume (for persistency). Hookshot uses this directory for its crypto store (i.e. long-lived state relating to its encryption keys).
1518
- Once a crypto store has been initialized, its files must not be modified, and Hookshot cannot be configured to use another crypto store of the same type as one it has used before. If a crypto store's files get lost or corrupted, Hookshot may fail to start up, or may be unable to decrypt command messages. To fix such issues, stop Hookshot, then reset its crypto store by running `yarn start:resetcrypto`.
1619
- [Redis](./workers.md) must be enabled. Note that worker mode is not yet supported with encryption, so `queue` MUST **NOT be configured**.
1720

18-
If you ever reset your homeserver's state, ensure you also reset Hookshot's encryption state. This includes clearing the `experimentalEncryption.storagePath` directory and all worker state stored in your redis instance. Otherwise, Hookshot may fail on start up with registration errors.
21+
If you ever reset your homeserver's state, ensure you also reset Hookshot's encryption state. This includes clearing the `storagePath` directory and all worker state stored in your redis instance. Otherwise, Hookshot may fail on start up with registration errors.
1922

2023
Also ensure that Hookshot's appservice registration file contains every line from `registration.sample.yml` that appears after the `If enabling encryption` comment. Note that changing the registration file may require restarting the homeserver that Hookshot is connected to.
2124

2225
## Running with Synapse
2326

24-
[Synapse](https://github.com/matrix-org/synapse/) has functional support for MSC3202 as of [v1.63.0](https://github.com/matrix-org/synapse/releases/tag/v1.63.0). To enable it, add the following section to Synapse's configuration file (typically named `homeserver.yaml`):
27+
[Synapse](https://github.com/matrix-org/synapse/) has functional support for MSC3202 and MSC4203 as of [v1.63.0](https://github.com/matrix-org/synapse/releases/tag/v1.63.0). To enable it, add the following section to Synapse's configuration file (typically named `homeserver.yaml`):
28+
29+
You may notice that MSC2409 is not listed above. Due to the changes being split out from MSC2409, `msc2409_to_device_messages_enabled` refers to MSC4203.
2530

2631
```yaml
2732
experimental_features:
2833
msc3202_device_masquerading: true
2934
msc3202_transaction_extensions: true
3035
msc2409_to_device_messages_enabled: true
3136
```
37+

spec/basic.spec.ts

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,4 @@ describe('Basic test setup', () => {
2626
// Expect help text.
2727
expect((await msg).data.content.body).to.include('!hookshot help` - This help text\n');
2828
});
29-
30-
// TODO: Move test to it's own generic connections file.
31-
it('should be able to setup a webhook', async () => {
32-
const user = testEnv.getUser('user');
33-
const testRoomId = await user.createRoom({ name: 'Test room', invite:[testEnv.botMxid] });
34-
await user.waitForRoomJoin({sender: testEnv.botMxid, roomId: testRoomId });
35-
await user.setUserPowerLevel(testEnv.botMxid, testRoomId, 50);
36-
await user.sendText(testRoomId, "!hookshot webhook test-webhook");
37-
const inviteResponse = await user.waitForRoomInvite({sender: testEnv.botMxid});
38-
await user.waitForRoomEvent<MessageEventContent>({
39-
eventType: 'm.room.message', sender: testEnv.botMxid, roomId: testRoomId,
40-
body: 'Room configured to bridge webhooks. See admin room for secret url.'
41-
});
42-
const webhookUrlMessage = user.waitForRoomEvent<MessageEventContent>({
43-
eventType: 'm.room.message', sender: testEnv.botMxid, roomId: inviteResponse.roomId
44-
});
45-
await user.joinRoom(inviteResponse.roomId);
46-
const msgData = (await webhookUrlMessage).data.content.body;
47-
const webhookUrl = msgData.split('\n')[2];
48-
const webhookNotice = user.waitForRoomEvent<MessageEventContent>({
49-
eventType: 'm.room.message', sender: testEnv.botMxid, roomId: testRoomId, body: 'Hello world!'
50-
});
51-
52-
// Send a webhook
53-
await fetch(webhookUrl, {
54-
method: 'POST',
55-
headers: { 'Content-Type': 'application/json' },
56-
body: JSON.stringify({text: 'Hello world!'})
57-
});
58-
59-
// And await the notice.
60-
await webhookNotice;
61-
});
6229
});

spec/e2ee.spec.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { MessageEventContent } from "matrix-bot-sdk";
2+
import { E2ESetupTestTimeout, E2ETestEnv } from "./util/e2e-test";
3+
import { describe, it, beforeEach, afterEach } from "@jest/globals";
4+
5+
const CryptoRoomState = [{
6+
content: {
7+
"algorithm": "m.megolm.v1.aes-sha2"
8+
},
9+
state_key: "",
10+
type: "m.room.encryption"
11+
}];
12+
13+
describe('End-2-End Encryption support', () => {
14+
let testEnv: E2ETestEnv;
15+
16+
beforeEach(async () => {
17+
testEnv = await E2ETestEnv.createTestEnv({ matrixLocalparts: ['user'], enableE2EE: true });
18+
await testEnv.setUp();
19+
}, E2ESetupTestTimeout);
20+
21+
afterEach(() => {
22+
return testEnv?.tearDown();
23+
});
24+
25+
it('should be able to send the help command', async () => {
26+
const user = testEnv.getUser('user');
27+
const testRoomId = await user.createRoom({ name: 'Test room', invite:[testEnv.botMxid], initial_state: CryptoRoomState});
28+
await user.setUserPowerLevel(testEnv.botMxid, testRoomId, 50);
29+
await user.waitForRoomJoin({sender: testEnv.botMxid, roomId: testRoomId });
30+
await user.sendText(testRoomId, "!hookshot help");
31+
await user.waitForRoomEvent<MessageEventContent>({
32+
eventType: 'm.room.message', sender: testEnv.botMxid, roomId: testRoomId,
33+
});
34+
});
35+
it('should send notices in an encrypted format', async () => {
36+
const user = testEnv.getUser('user');
37+
const testRoomId = await user.createRoom({ name: 'Test room', invite:[testEnv.botMxid], initial_state: CryptoRoomState});
38+
await user.setUserPowerLevel(testEnv.botMxid, testRoomId, 50);
39+
await user.waitForRoomJoin({sender: testEnv.botMxid, roomId: testRoomId });
40+
await user.sendText(testRoomId, "!hookshot webhook test-webhook");
41+
const inviteResponse = await user.waitForRoomInvite({sender: testEnv.botMxid});
42+
await user.waitForEncryptedEvent<MessageEventContent>({
43+
eventType: 'm.room.message', sender: testEnv.botMxid, roomId: testRoomId,
44+
body: 'Room configured to bridge webhooks. See admin room for secret url.'
45+
});
46+
const webhookUrlMessage = user.waitForEncryptedEvent<MessageEventContent>({
47+
eventType: 'm.room.message', sender: testEnv.botMxid, roomId: inviteResponse.roomId
48+
});
49+
await user.joinRoom(inviteResponse.roomId);
50+
const msgData = (await webhookUrlMessage).data.content.body;
51+
const webhookUrl = msgData.split('\n')[2];
52+
const webhookNotice = user.waitForEncryptedEvent<MessageEventContent>({
53+
eventType: 'm.room.message', sender: testEnv.botMxid, roomId: testRoomId, body: 'Hello world!'
54+
});
55+
56+
// Send a webhook
57+
await fetch(webhookUrl, {
58+
method: 'POST',
59+
headers: { 'Content-Type': 'application/json' },
60+
body: JSON.stringify({text: 'Hello world!'})
61+
});
62+
63+
// And await the notice.
64+
await webhookNotice;
65+
});
66+
});

0 commit comments

Comments
 (0)