Compare commits

...

23 Commits

Author SHA1 Message Date
strawberry 4e197315d2 experiment with coroutines/folly https://github.com/girlbossceo/rust-rocksdb-zaidoon1/commit/9ecb597d966efb37ed96f4cfe3f09165013fd14a
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-11 03:27:43 -04:00
strawberry 18e43e1d35 Reapply "bump various deps"
This reverts commit 6b918966d4.
2024-05-10 22:56:44 -04:00
strawberry 09fca89ac5 Revert "rocksdb: enable async_io if using io_uring feature"
This reverts commit 6266e0ab5e.
2024-05-10 22:56:44 -04:00
morguldir 9f19a2025d Revert "feat(membership): check if user already has the membership that is requested to be set"
This reverts commit 321a6ca0fe.

These checks were not working as intended, resulting in the unban button not working

The join check gets kept since it slightly reduces the amount of sent joins in some cases
This check will probably be replaced soon for a more universal solution to the "made no change" issue

Signed-off-by: morguldir <morguldir@protonmail.com>
2024-05-10 22:52:44 -04:00
strawberry 6b918966d4 Revert "bump various deps"
This reverts commit 653ec3799e.
2024-05-09 22:38:05 -04:00
strawberry 328502c1cd dont send avatar url or display name for ban membership events
the display name or avatar may be offensive

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:44:15 -04:00
strawberry d15e461303 config option to auto-remediate bad users joining bad rooms or servers
also forgets all rooms upon leave_all_rooms

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:44:15 -04:00
strawberry 6946eead28 pin rust-rocksdb to before snappy update
it seems to break nix builds

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:42:05 -04:00
strawberry 09d3240365 bump conduwuit version to 0.3.3
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:42:05 -04:00
strawberry 653ec3799e bump various deps
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 17:42:05 -04:00
strawberry 6de9f52d5a docs: update differences.md
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry 484e7d1d2a docs: add my selfhosted forgejo mirror
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry dfa01541b3 docs: transfem.dev has rules
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry adbe9268ce docs: add troubleshooting, maintenance, various improvements and fixes
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:34:50 -04:00
strawberry 3504e6e724 fix broken reports
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 12:33:46 -04:00
strawberry 154b2ab490 media: additional sanitisation on the Content-Disposition filename
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 09:53:04 -04:00
strawberry 2231ccf118 return inline Content-Disposition based on the detected file type (e.g. image/video)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 09:53:04 -04:00
strawberry d4d9f92ade add security response HTTP headers if not present
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-09 09:53:04 -04:00
renovate[bot] e4e1636da8 chore(deps): update aquasecurity/trivy-action action to v0.20.0 2024-05-08 15:06:45 -04:00
strawberry e99aac9550 ci: fix gitlab container registry destination
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-08 15:04:59 -04:00
strawberry ddb87168ed update gitlab repo link
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-08 15:04:59 -04:00
strawberry 245c34e659 ci: dont run docker publishing if none of the usernames are set
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-07 23:59:05 -04:00
strawberry 43b07be3fc ci: use PR author instead of branch name for docker image publishing
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-07 02:44:55 -04:00
28 changed files with 672 additions and 445 deletions
+13 -13
View File
@@ -235,20 +235,20 @@ jobs:
name: Docker publish name: Docker publish
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build needs: build
if: startsWith('refs/tags/v', github.ref) || github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false) if: (startsWith('refs/tags/v', github.ref) || github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false)) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '')
env: env:
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }}-arm64v8 DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }}-amd64 DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }} DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith('refs/tags/v', github.ref) && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }} DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith('refs/tags/v', github.ref) && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }}-arm64v8 GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }}-amd64 GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }} GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith('refs/tags/v', github.ref) && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }} GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith('refs/tags/v', github.ref) && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GLCR_ARM64: registry.gitlab.com/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }}-arm64v8 GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
GLCR_AMD64: registry.gitlab.com/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }}-amd64 GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
GLCR_TAG: registry.gitlab.com/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }}-${{ github.sha }} GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
GLCR_BRANCH: registry.gitlab.com/${{ github.repository }}:${{ (startsWith('refs/tags/v', github.ref) && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.head_ref)) || github.ref_name }} GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith('refs/tags/v', github.ref) && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run Trivy code and vulnerability scanner on repo - name: Run Trivy code and vulnerability scanner on repo
uses: aquasecurity/trivy-action@0.19.0 uses: aquasecurity/trivy-action@0.20.0
with: with:
scan-type: repo scan-type: repo
format: sarif format: sarif
@@ -34,7 +34,7 @@ jobs:
severity: CRITICAL,HIGH,MEDIUM,LOW severity: CRITICAL,HIGH,MEDIUM,LOW
- name: Run Trivy code and vulnerability scanner on filesystem - name: Run Trivy code and vulnerability scanner on filesystem
uses: aquasecurity/trivy-action@0.19.0 uses: aquasecurity/trivy-action@0.20.0
with: with:
scan-type: fs scan-type: fs
format: sarif format: sarif
+3 -1
View File
@@ -52,6 +52,8 @@ conduwuit's website uses [`mdbook`][mdbook] and deployed via CI using GitHub Pag
To build the documentation using Nix, run: `bin/nix-build-and-cache just .#book` To build the documentation using Nix, run: `bin/nix-build-and-cache just .#book`
The output of the mdbook generation is in `result/`. mdbooks can be opened in your browser from the individual HTML files without any web server needed.
### Inclusivity and Diversity ### Inclusivity and Diversity
All **MUST** code and write with inclusivity and diversity in mind. See the [following page by Google on writing inclusive code and documentation](https://developers.google.com/style/inclusive-documentation). All **MUST** code and write with inclusivity and diversity in mind. See the [following page by Google on writing inclusive code and documentation](https://developers.google.com/style/inclusive-documentation).
@@ -68,7 +70,7 @@ Rust's default style and standards with regards to [function names, variable nam
### Creating pull requests ### Creating pull requests
Please try to keep contributions to the GitHub. While the mirrors of conduwuit allow for pull/merge requests, there is no guarantee I will see them in a timely manner. Please try to keep contributions to the GitHub. While the mirrors of conduwuit allow for pull/merge requests, there is no guarantee I will see them in a timely manner. Additionally, please mark WIP or unfinished or incomplete PRs as drafts. This prevents me from having to ping once in a while to double check the status of it, especially when the CI completed successfully and everything so it *looks* done.
If you open a pull request on one of the mirrors, it is your responsibility to inform me about its existence. In the future I may try to solve this with more repo bots in the conduwuit Matrix room. There is no mailing list or email-patch support on the sr.ht mirror, but if you'd like to email me a git patch you can do so at `strawberry@puppygock.gay`. If you open a pull request on one of the mirrors, it is your responsibility to inform me about its existence. In the future I may try to solve this with more repo bots in the conduwuit Matrix room. There is no mailing list or email-patch support on the sr.ht mirror, but if you'd like to email me a git patch you can do so at `strawberry@puppygock.gay`.
Generated
+84 -35
View File
@@ -568,7 +568,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]] [[package]]
name = "conduit" name = "conduit"
version = "0.3.2" version = "0.3.3"
dependencies = [ dependencies = [
"argon2", "argon2",
"async-trait", "async-trait",
@@ -594,6 +594,7 @@ dependencies = [
"hyper 1.3.1", "hyper 1.3.1",
"hyper-util", "hyper-util",
"image", "image",
"infer",
"ipaddress", "ipaddress",
"itertools", "itertools",
"jsonwebtoken", "jsonwebtoken",
@@ -614,6 +615,7 @@ dependencies = [
"ruma-identifiers-validation", "ruma-identifiers-validation",
"rusqlite", "rusqlite",
"rust-rocksdb", "rust-rocksdb",
"sanitize-filename",
"sd-notify", "sd-notify",
"sentry", "sentry",
"sentry-tower", "sentry-tower",
@@ -949,6 +951,17 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "find-folly"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d8bf7ad55e1e290ef089eaaa62c5ac3b86c5c6279c37fa67943937d108b01ef"
dependencies = [
"pkg-config",
"shlex",
"thiserror",
]
[[package]] [[package]]
name = "findshlibs" name = "findshlibs"
version = "0.10.2" version = "0.10.2"
@@ -1357,16 +1370,16 @@ dependencies = [
[[package]] [[package]]
name = "html5ever" name = "html5ever"
version = "0.26.0" version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
dependencies = [ dependencies = [
"log", "log",
"mac", "mac",
"markup5ever", "markup5ever",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.60",
] ]
[[package]] [[package]]
@@ -1605,6 +1618,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "infer"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199"
[[package]] [[package]]
name = "inlinable_string" name = "inlinable_string"
version = "0.1.15" version = "0.1.15"
@@ -1885,9 +1904,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]] [[package]]
name = "markup5ever" name = "markup5ever"
version = "0.11.0" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
dependencies = [ dependencies = [
"log", "log",
"phf", "phf",
@@ -1899,9 +1918,9 @@ dependencies = [
[[package]] [[package]]
name = "markup5ever_rcdom" name = "markup5ever_rcdom"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2" checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18"
dependencies = [ dependencies = [
"html5ever", "html5ever",
"markup5ever", "markup5ever",
@@ -2332,21 +2351,21 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.10.1" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [ dependencies = [
"phf_shared", "phf_shared 0.11.2",
] ]
[[package]] [[package]]
name = "phf_codegen" name = "phf_codegen"
version = "0.10.0" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [ dependencies = [
"phf_generator", "phf_generator 0.11.2",
"phf_shared", "phf_shared 0.11.2",
] ]
[[package]] [[package]]
@@ -2355,7 +2374,17 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [ dependencies = [
"phf_shared", "phf_shared 0.10.0",
"rand",
]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared 0.11.2",
"rand", "rand",
] ]
@@ -2368,6 +2397,15 @@ dependencies = [
"siphasher", "siphasher",
] ]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.5" version = "1.1.5"
@@ -2906,11 +2944,12 @@ dependencies = [
[[package]] [[package]]
name = "rust-librocksdb-sys" name = "rust-librocksdb-sys"
version = "0.21.0+9.1.1" version = "0.21.0+9.1.1"
source = "git+https://github.com/zaidoon1/rust-rocksdb?branch=master#c5cd6bd25152ef1f8a488761351da0c3d29ed93a" source = "git+https://github.com/girlbossceo/rust-rocksdb-zaidoon1?rev=9ecb597d966efb37ed96f4cfe3f09165013fd14a#9ecb597d966efb37ed96f4cfe3f09165013fd14a"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"bzip2-sys", "bzip2-sys",
"cc", "cc",
"find-folly",
"glob", "glob",
"libc", "libc",
"libz-sys", "libz-sys",
@@ -2923,7 +2962,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-rocksdb" name = "rust-rocksdb"
version = "0.25.0" version = "0.25.0"
source = "git+https://github.com/zaidoon1/rust-rocksdb?branch=master#c5cd6bd25152ef1f8a488761351da0c3d29ed93a" source = "git+https://github.com/girlbossceo/rust-rocksdb-zaidoon1?rev=9ecb597d966efb37ed96f4cfe3f09165013fd14a#9ecb597d966efb37ed96f4cfe3f09165013fd14a"
dependencies = [ dependencies = [
"libc", "libc",
"rust-librocksdb-sys", "rust-librocksdb-sys",
@@ -3047,6 +3086,16 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "sanitize-filename"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603"
dependencies = [
"lazy_static",
"regex",
]
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.23" version = "0.1.23"
@@ -3244,18 +3293,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.200" version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.200" version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3277,9 +3326,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.116" version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -3480,7 +3529,7 @@ dependencies = [
"new_debug_unreachable", "new_debug_unreachable",
"once_cell", "once_cell",
"parking_lot", "parking_lot",
"phf_shared", "phf_shared 0.10.0",
"precomputed-hash", "precomputed-hash",
"serde", "serde",
] ]
@@ -3491,8 +3540,8 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
dependencies = [ dependencies = [
"phf_generator", "phf_generator 0.10.0",
"phf_shared", "phf_shared 0.10.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
] ]
@@ -3559,18 +3608,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.59" version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.59" version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -4277,9 +4326,9 @@ dependencies = [
[[package]] [[package]]
name = "webpage" name = "webpage"
version = "2.0.0" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb86b12e58d490a99867f561ce8466ffa7b73e24d015a8e7f5bc111d4424ba2" checksum = "70862efc041d46e6bbaa82bb9c34ae0596d090e86cbd14bd9e93b36ee6802eac"
dependencies = [ dependencies = [
"html5ever", "html5ever",
"markup5ever_rcdom", "markup5ever_rcdom",
@@ -4549,9 +4598,9 @@ dependencies = [
[[package]] [[package]]
name = "xml5ever" name = "xml5ever"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650" checksum = "7c376f76ed09df711203e20c3ef5ce556f0166fa03d39590016c0fd625437fad"
dependencies = [ dependencies = [
"log", "log",
"mac", "mac",
+12 -7
View File
@@ -10,7 +10,7 @@ authors = [
homepage = "https://conduwuit.puppyirl.gay/" homepage = "https://conduwuit.puppyirl.gay/"
repository = "https://github.com/girlbossceo/conduwuit" repository = "https://github.com/girlbossceo/conduwuit"
readme = "README.md" readme = "README.md"
version = "0.3.2" version = "0.3.3"
edition = "2021" edition = "2021"
# See also `rust-toolchain.toml` # See also `rust-toolchain.toml`
@@ -19,6 +19,8 @@ rust-version = "1.77.0"
[dependencies] [dependencies]
console-subscriber = { version = "0.2", optional = true } console-subscriber = { version = "0.2", optional = true }
infer = { version = "0.15", default-features = false }
# for hot lib reload # for hot lib reload
hot-lib-reloader = { version = "^0.7", optional = true } hot-lib-reloader = { version = "^0.7", optional = true }
@@ -26,7 +28,7 @@ hot-lib-reloader = { version = "^0.7", optional = true }
rand = "0.8.5" rand = "0.8.5"
# Used for conduit::Error type # Used for conduit::Error type
thiserror = "1.0.59" thiserror = "1.0.60"
# Used to encode server public key # Used to encode server public key
base64 = "0.22.1" base64 = "0.22.1"
@@ -77,6 +79,7 @@ url = { version = "2.5.0", features = ["serde"] }
async-trait = "0.1.80" async-trait = "0.1.80"
lru-cache = "0.1.2" lru-cache = "0.1.2"
sanitize-filename = "0.5.0"
# standard date and time tools # standard date and time tools
[dependencies.chrono] [dependencies.chrono]
@@ -109,6 +112,7 @@ features = [
"add-extension", "add-extension",
"cors", "cors",
"sensitive-headers", "sensitive-headers",
"set-header",
"trace", "trace",
"util", "util",
"catch-panic", "catch-panic",
@@ -129,14 +133,14 @@ features = ["rustls-tls-native-roots", "socks", "hickory-dns"]
# all the serde stuff # all the serde stuff
# Used for pdu definition # Used for pdu definition
[dependencies.serde] [dependencies.serde]
version = "1.0.200" version = "1.0.201"
features = ["rc"] features = ["rc"]
# Used for appservice registration files # Used for appservice registration files
[dependencies.serde_yaml] [dependencies.serde_yaml]
version = "0.9.34" version = "0.9.34"
# Used for ruma wrapper # Used for ruma wrapper
[dependencies.serde_json] [dependencies.serde_json]
version = "1.0.116" version = "1.0.117"
features = ["raw_value"] features = ["raw_value"]
@@ -230,7 +234,7 @@ features = ["use_std"]
# for URL previews # for URL previews
[dependencies.webpage] [dependencies.webpage]
version = "2.0" version = "2.0.1"
default-features = false default-features = false
# to support multiple variations of setting a config option # to support multiple variations of setting a config option
@@ -294,8 +298,9 @@ version = "0.24.1"
default-features = false default-features = false
[dependencies.rust-rocksdb] [dependencies.rust-rocksdb]
git = "https://github.com/zaidoon1/rust-rocksdb" git = "https://github.com/girlbossceo/rust-rocksdb-zaidoon1"
branch = "master" rev = "9ecb597d966efb37ed96f4cfe3f09165013fd14a"
#branch = "master"
optional = true optional = true
default-features = true default-features = true
features = ["multi-threaded-cf", "zstd"] features = ["multi-threaded-cf", "zstd"]
+6 -1
View File
@@ -26,6 +26,10 @@ friends or company.
An official conduwuit server ran by me is available at transfem.dev ([element.transfem.dev](https://element.transfem.dev) / [cinny.transfem.dev](https://cinny.transfem.dev)) An official conduwuit server ran by me is available at transfem.dev ([element.transfem.dev](https://element.transfem.dev) / [cinny.transfem.dev](https://cinny.transfem.dev))
transfem.dev is a public homeserver that can be used, it is not a "test only homeserver". This means there are rules, so please read the rules: [https://transfem.dev/homeserver_rules.txt](https://transfem.dev/homeserver_rules.txt)
transfem.dev is also listed at [servers.joinmatrix.org](https://servers.joinmatrix.org/)
#### What is the current status? #### What is the current status?
conduwuit is a hard fork of Conduit which is in beta, meaning you can join and participate in most conduwuit is a hard fork of Conduit which is in beta, meaning you can join and participate in most
@@ -59,7 +63,8 @@ Both, but I prefer conduwuit.
#### Mirrors of conduwuit #### Mirrors of conduwuit
- GitHub: <https://github.com/girlbossceo/conduwuit> - GitHub: <https://github.com/girlbossceo/conduwuit>
- GitLab: <https://gitlab.com/girlbossceo/conduwuit> - GitLab: <https://gitlab.com/conduwuit/conduwuit>
- git.girlcock.ceo: <https://git.girlcock.ceo/strawberry/conduwuit>
- git.gay: <https://git.gay/june/conduwuit> - git.gay: <https://git.gay/june/conduwuit>
- Codeberg: <https://codeberg.org/girlbossceo/conduwuit> - Codeberg: <https://codeberg.org/girlbossceo/conduwuit>
- sourcehut: <https://git.sr.ht/~girlbossceo/conduwuit> - sourcehut: <https://git.sr.ht/~girlbossceo/conduwuit>
+13
View File
@@ -269,6 +269,19 @@ url_preview_check_root_domain = false
# Defaults to true # Defaults to true
allow_profile_lookup_federation_requests = true allow_profile_lookup_federation_requests = true
# Config option to automatically deactivate the account of any user who attempts to join a:
# - banned room
# - forbidden room alias
# - room alias or ID with a forbidden server name
#
# This may be useful if all your banned lists consist of toxic rooms or servers that no good faith user would ever attempt to join, and
# to automatically remediate the problem without any admin user intervention.
#
# This will also make the user leave all rooms. Federation (e.g. remote room invites) are ignored here.
#
# Defaults to false as rooms can be banned for non-moderation-related reasons
#auto_deactivate_banned_room_attempts = false
### Misc ### Misc
+1 -2
View File
@@ -1,5 +1,4 @@
conduwuit for Debian # conduwuit for Debian
==================
Installation Installation
------------ ------------
+8 -6
View File
@@ -1,13 +1,18 @@
[Unit] [Unit]
Description=conduwuit Matrix homeserver Description=conduwuit Matrix homeserver
Documentation=https://conduwuit.puppyirl.gay/
After=network-online.target After=network-online.target
[Service] [Service]
DynamicUser=yes DynamicUser=yes
User=_conduwuit User=conduwuit
Group=_conduwuit Group=conduwuit
Type=notify Type=notify
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
ExecStart=/usr/sbin/conduwuit
AmbientCapabilities= AmbientCapabilities=
CapabilityBoundingSet= CapabilityBoundingSet=
@@ -39,14 +44,11 @@ SystemCallArchitectures=native
SystemCallFilter=@system-service @resources SystemCallFilter=@system-service @resources
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc
SystemCallErrorNumber=EPERM SystemCallErrorNumber=EPERM
StateDirectory=matrix-conduit StateDirectory=conduwuit
RuntimeDirectory=conduit RuntimeDirectory=conduit
RuntimeDirectoryMode=0750 RuntimeDirectoryMode=0750
Environment="CONDUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
ExecStart=/usr/sbin/conduwuit
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5
+4 -4
View File
@@ -7,21 +7,21 @@ CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit/
case "$1" in case "$1" in
configure) configure)
# Create the `_conduwuit` user if it does not exist yet. # Create the `conduwuit` user if it does not exist yet.
if ! getent passwd _conduwuit > /dev/null ; then if ! getent passwd conduwuit > /dev/null ; then
echo 'Adding system user for the conduwuit Matrix homeserver' 1>&2 echo 'Adding system user for the conduwuit Matrix homeserver' 1>&2
adduser --system --group --quiet \ adduser --system --group --quiet \
--home "$CONDUWUIT_DATABASE_PATH" \ --home "$CONDUWUIT_DATABASE_PATH" \
--disabled-login \ --disabled-login \
--shell "/usr/sbin/nologin" \ --shell "/usr/sbin/nologin" \
--force-badname \ --force-badname \
_conduwuit conduwuit
fi fi
# Create the database path if it does not exist yet and fix up ownership # Create the database path if it does not exist yet and fix up ownership
# and permissions. # and permissions.
mkdir -p "$CONDUWUIT_DATABASE_PATH" mkdir -p "$CONDUWUIT_DATABASE_PATH"
chown _conduwuit:_conduwuit -R "$CONDUWUIT_DATABASE_PATH" chown conduwuit:conduwuit -R "$CONDUWUIT_DATABASE_PATH"
chmod 700 "$CONDUWUIT_DATABASE_PATH" chmod 700 "$CONDUWUIT_DATABASE_PATH"
;; ;;
esac esac
+6 -4
View File
@@ -2,15 +2,17 @@
- [Introduction](introduction.md) - [Introduction](introduction.md)
- [Differences from upstream Conduit](differences.md) - [Differences from upstream Conduit](differences.md)
- [Example configuration](configuration.md) - [Example configuration](configuration.md)
- [Deploying](deploying.md) - [Deploying](deploying.md)
- [Generic](deploying/generic.md) - [Generic](deploying/generic.md)
- [Debian](deploying/debian.md)
- [Docker](deploying/docker.md)
- [NixOS](deploying/nixos.md) - [NixOS](deploying/nixos.md)
- [Docker](deploying/docker.md)
- [Arch Linux](deploying/arch-linux.md)
- [Debian](deploying/debian.md)
- [TURN](turn.md) - [TURN](turn.md)
- [Appservices](appservices.md) - [Appservices](appservices.md)
- [Maintenance](maintenance.md)
- [Troubleshooting](troubleshooting.md)
- [Development](development.md) - [Development](development.md)
- [Testing](development/testing.md)
- [Contributing](contributing.md) - [Contributing](contributing.md)
- [Testing](development/testing.md)
+8
View File
@@ -0,0 +1,8 @@
# conduwuit for Arch Linux
Currently conduwuit is only on the Arch User Repository (AUR).
The conduwuit AUR packages are community maintained and are not maintained by conduwuit development team, but the AUR package maintainers are in the Matrix room. Please attempt to verify your AUR package's PKGBUILD file looks fine before asking for support.
- [conduwuit](https://aur.archlinux.org/packages/conduwuit) - latest tagged conduwuit
- [conduwuit-git](https://aur.archlinux.org/packages/conduwuit-git) - latest git conduwuit from `main` branch
+6 -91
View File
@@ -12,15 +12,15 @@ OCI images for conduwuit are available in the registries listed below.
| Registry | Image | Size | Notes | | Registry | Image | Size | Notes |
| --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- | | --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- |
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:latest][gh] | ![Image Size][shield-latest] | Stable tagged image. | | GitHub Registry | [ghcr.io/girlbossceo/conduwuit:latest][gh] | ![Image Size][shield-latest] | Stable tagged image. |
| GitLab Registry | [registry.gitlab.com/girlbossceo/conduwuit:latest][gl] | ![Image Size][shield-latest] | Stable tagged image. | | GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:latest][gl] | ![Image Size][shield-latest] | Stable tagged image. |
| Docker Hub | [docker.io/girlbossceo/conduwuit:latest][dh] | ![Image Size][shield-latest] | Stable tagged image. | | Docker Hub | [docker.io/girlbossceo/conduwuit:latest][dh] | ![Image Size][shield-latest] | Stable tagged image. |
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Stable main branch. | | GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Stable main branch. |
| GitLab Registry | [registry.gitlab.com/girlbossceo/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. | | GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. | | Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. |
[dh]: https://hub.docker.com/repository/docker/girlbossceo/conduwuit [dh]: https://hub.docker.com/repository/docker/girlbossceo/conduwuit
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit [gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
[gl]: https://gitlab.com/girlbossceo/conduwuit/container_registry/6351657 [gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6351657
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest [shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main [shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
@@ -51,9 +51,9 @@ docker run -d -p 8448:6167 \
or you can use [docker compose](#docker-compose). or you can use [docker compose](#docker-compose).
The `-d` flag lets the container run in detached mode. You may supply an optional `conduwuit.toml` config file, an example can be found [here](../configuration.md). The `-d` flag lets the container run in detached mode. You may supply an optional `conduwuit.toml` config file, the example config can be found [here](../configuration.md).
You can pass in different env vars to change config values on the fly. You can even configure conduwuit completely by using env vars. For an overview of possible You can pass in different env vars to change config values on the fly. You can even configure conduwuit completely by using env vars. For an overview of possible
values, please take a look at the `docker-compose.yml` file. values, please take a look at the [`docker-compose.yml`](docker-compose.yml) file.
If you just want to test conduwuit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it. If you just want to test conduwuit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
@@ -107,92 +107,7 @@ either expose ports `443` and `8448` or serve two endpoints `.well-known/matrix/
With the service `well-known` we use a single `nginx` container that will serve those two files. With the service `well-known` we use a single `nginx` container that will serve those two files.
So...step by step:
1. Copy [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and [`docker-compose.override.yml`](docker-compose.override.yml) from the repository and remove `.for-traefik` (or `.with-traefik`) from the filename.
2. Open both files and modify/adjust them to your needs. Meaning, change the `CONDUIT_SERVER_NAME` and the volume host mappings according to your needs.
3. Create the `conduwuit.toml` config file, an example can be found [here](../configuration.md), or set `CONDUIT_CONFIG=""` and configure conduwuit per env vars.
4. Uncomment the `element-web` service if you want to host your own Element Web Client and create a `element_config.json`.
5. Create the files needed by the `well-known` service.
- `./nginx/matrix.conf` (relative to the compose file, you can change this, but then also need to change the volume mapping)
```nginx
server {
server_name <SUBDOMAIN>.<DOMAIN>;
listen 80 default_server;
location /.well-known/matrix/server {
return 200 '{"m.server": "<SUBDOMAIN>.<DOMAIN>:443"}';
types { } default_type "application/json; charset=utf-8";
}
location /.well-known/matrix/client {
return 200 '{"m.homeserver": {"base_url": "https://<SUBDOMAIN>.<DOMAIN>"}}';
types { } default_type "application/json; charset=utf-8";
add_header "Access-Control-Allow-Origin" *;
}
location / {
return 404;
}
}
```
6. Run `docker compose up -d`
7. Connect to your homeserver with your preferred client and create a user. You should do this immediately after starting Conduit, because the first created user is the admin.
## Voice communication ## Voice communication
In order to make or receive calls, a TURN server is required. conduwuit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image. Before proceeding with the software installation, it is essential to have the necessary configurations in place. See the [TURN](../turn.md) page.
### Configuration
Create a configuration file called `coturn.conf` containing:
```conf
use-auth-secret
static-auth-secret=<a secret key>
realm=<your server domain>
```
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
These same values need to be set in conduwuit. You can either modify conduwuit.toml to include these lines:
```
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
turn_secret = "<secret key from coturn configuration>"
```
or append the following to the docker environment variables dependig on which configuration method you used earlier:
```yml
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
```
Restart Conduit to apply these changes.
### Run
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
```bash
docker run -d --network=host -v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn
```
or docker-compose. For the latter, paste the following section into a file called `docker-compose.yml`
and run `docker compose up -d` in the same directory.
```yml
version: 3
services:
turn:
container_name: coturn-server
image: docker.io/coturn/coturn
restart: unless-stopped
network_mode: "host"
volumes:
- ./coturn.conf:/etc/coturn/turnserver.conf
```
To understand why the host networking mode is used and explore alternative configuration options, please visit the following link: https://github.com/coturn/coturn/blob/master/docker/coturn/README.md.
For security recommendations see Synapse's [Coturn documentation](https://github.com/matrix-org/synapse/blob/develop/docs/setup/turn/coturn.md#configuration).
+19 -49
View File
@@ -1,7 +1,5 @@
# Generic deployment documentation # Generic deployment documentation
### Please note that this documentation is not fully representative of conduwuit at the moment. Assume majority of it is outdated.
> ## Getting help > ## Getting help
> >
> If you run into any problems while setting up conduwuit, ask us > If you run into any problems while setting up conduwuit, ask us
@@ -13,23 +11,16 @@ You may simply download the binary that fits your machine. Run `uname -m` to see
Prebuilt binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest). Prebuilt binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest).
Alternatively, you may compile the binary yourself. First, install any dependencies: The latest tagged release also includes the Debian packages.
```bash Alternatively, you may compile the binary yourself. We recommend using [Lix](https://lix.systems) to build conduwuit as this has the most guaranteed
# Debian reproducibiltiy and easiest to get a build environment and output going.
$ sudo apt install libclang-dev build-essential
# RHEL Otherwise, follow standard Rust project build guides (installing git and cloning the repo, getting the Rust toolchain via rustup, installing LLVM toolchain + libclang, installing liburing for io_uring and RocksDB, etc).
$ sudo dnf install clang
```
Then, `cd` into the source tree of conduwuit and run:
```bash
$ cargo build --release
```
## Adding a conduwuit user ## Adding a conduwuit user
While conduwuit can run as any user it is usually better to use dedicated users for different services. This also allows While conduwuit can run as any user it is better to use dedicated users for different services. This also allows
you to make sure that the file permissions are correctly set up. you to make sure that the file permissions are correctly set up.
In Debian or RHEL, you can use this command to create a conduwuit user: In Debian or RHEL, you can use this command to create a conduwuit user:
@@ -38,6 +29,12 @@ In Debian or RHEL, you can use this command to create a conduwuit user:
sudo adduser --system conduwuit --group --disabled-login --no-create-home sudo adduser --system conduwuit --group --disabled-login --no-create-home
``` ```
For distros without `adduser`:
```bash
sudo useradd -r --shell /usr/bin/nologin --no-create-home conduwuit
```
## Forwarding ports in the firewall or the router ## Forwarding ports in the firewall or the router
conduwuit uses the ports 443 and 8448 both of which need to be open in the firewall. conduwuit uses the ports 443 and 8448 both of which need to be open in the firewall.
@@ -46,45 +43,18 @@ If conduwuit runs behind a router or in a container and has a different public I
## Setting up a systemd service ## Setting up a systemd service
Now we'll set up a systemd service for conduwuit, so it's easy to start/stop conduwuit and set it to autostart when your The systemd unit for conduwuit can be found [here](../../debian/conduwuit.service). You may need to change the `ExecStart=` path to where you placed the conduwuit binary.
server reboots. Simply paste the default systemd service you can find below into
`/etc/systemd/system/conduwuit.service`.
```systemd
[Unit]
Description=conduwuit Matrix Server
After=network.target
[Service]
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
User=conduwuit
Group=conduwuit
RuntimeDirectory=conduwuit
RuntimeDirectoryMode=0750
Restart=always
ExecStart=/usr/local/bin/conduwuit
[Install]
WantedBy=multi-user.target
```
Finally, run
```bash
$ sudo systemctl daemon-reload
```
## Creating the conduwuit configuration file ## Creating the conduwuit configuration file
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. Paste this in **and take a moment Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. The example config can be found at [conduwuit-example.toml](../configuration.md).**Please take a moment to read it. You need to change at least the server name.**
to read it. You need to change at least the server name.**
RocksDB (`rocksdb`) is the only supported database backend. SQLite only exists for historical reasons and is not recommended. Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
See the following example config at [conduwuit-example.toml](../configuration.md) RocksDB (`rocksdb`) is the only supported database backend. SQLite only exists for historical reasons and is not recommended. Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
## Setting the correct file permissions ## Setting the correct file permissions
As we are using a conduwuit specific user we need to allow it to read the config. To do that you can run this command on If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this command on
Debian or RHEL: Debian or RHEL:
```bash ```bash
@@ -102,7 +72,7 @@ sudo chmod 700 /var/lib/conduwuit/
## Setting up the Reverse Proxy ## Setting up the Reverse Proxy
Refer to the documentation or various guides online of your chosen reverse proxy software. A Caddy example will be provided as this is the recommended reverse proxy for new users and is very trivial. Refer to the documentation or various guides online of your chosen reverse proxy software. A [Caddy](https://caddyserver.com/) example will be provided as this is the recommended reverse proxy for new users and is very trivial to use (handles TLS, reverse proxy headers, etc transparently with proper defaults).
### Caddy ### Caddy
@@ -118,10 +88,10 @@ your.server.name, your.server.name:8448 {
} }
``` ```
That's it! Just start or enable the service and you're set. That's it! Just start and enable the service and you're set.
```bash ```bash
$ sudo systemctl enable caddy $ sudo systemctl enable --now caddy
``` ```
## You're done! ## You're done!
+11 -4
View File
@@ -6,7 +6,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
## Performance: ## Performance:
- Concurrency support for key fetching for faster remote room joins and room joins that will error less frequently - Concurrency support for key fetching for faster remote room joins and room joins that will error less frequently
- Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached - Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests (download and thumbnail) to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
- Add feature flags and config options to enable/build with zstd, brotli, and/or gzip HTTP body compression (response and request) - Add feature flags and config options to enable/build with zstd, brotli, and/or gzip HTTP body compression (response and request)
- Eliminate all usage of the thread-blocking `getaddrinfo(3)` call upon DNS queries, significantly improving federation latency/ping and cache DNS results (NXDOMAINs, successful queries, etc) using hickory-dns / hickory-resolver - Eliminate all usage of the thread-blocking `getaddrinfo(3)` call upon DNS queries, significantly improving federation latency/ping and cache DNS results (NXDOMAINs, successful queries, etc) using hickory-dns / hickory-resolver
- Vastly improve RocksDB default settings to use new features that help with performance significantly, uses settings tailored to SSDs, various ways to tweak RocksDB, and a conduwuit setting to tell RocksDB to use settings that are tailored to HDDs or slow spinning rust storage or buggy filesystems. - Vastly improve RocksDB default settings to use new features that help with performance significantly, uses settings tailored to SSDs, various ways to tweak RocksDB, and a conduwuit setting to tell RocksDB to use settings that are tailored to HDDs or slow spinning rust storage or buggy filesystems.
@@ -22,6 +22,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Add config options for RocksDB compression and bottommost compression, including choosing the algorithm and compression level - Add config options for RocksDB compression and bottommost compression, including choosing the algorithm and compression level
- Use [loole](https://github.com/mahdi-shojaee/loole) MPSC channels instead of tokio MPSC channels for huge performance boosts in sending channels (mainly relevant for federation) and presence channels - Use [loole](https://github.com/mahdi-shojaee/loole) MPSC channels instead of tokio MPSC channels for huge performance boosts in sending channels (mainly relevant for federation) and presence channels
- Use `tracing`/`log`'s `release_max_level_info` feature to improve performance, build speeds, binary size, and CPU usage in release builds by avoid compiling debug/trace log level macros that users will generally never use (can be disabled with a build-time feature flag) - Use `tracing`/`log`'s `release_max_level_info` feature to improve performance, build speeds, binary size, and CPU usage in release builds by avoid compiling debug/trace log level macros that users will generally never use (can be disabled with a build-time feature flag)
- Enable RocksDB async read I/O via `io_uring` by default
## General Fixes: ## General Fixes:
@@ -35,7 +36,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Increased graceful shutdown timeout from a low 60 seconds to 180 seconds to avoid killing connections and let the remaining ones finish processing - Increased graceful shutdown timeout from a low 60 seconds to 180 seconds to avoid killing connections and let the remaining ones finish processing
- Return joined member count of rooms for push rules/conditions instead of a hardcoded value of 10 - Return joined member count of rooms for push rules/conditions instead of a hardcoded value of 10
- Make `CONDUIT_CONFIG` optional, relevant for container users that configure only by environment variables and no longer need to set `CONDUIT_CONFIG` to an empty string. - Make `CONDUIT_CONFIG` optional, relevant for container users that configure only by environment variables and no longer need to set `CONDUIT_CONFIG` to an empty string.
- Allow HEAD HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests) - Allow HEAD and PATCH (MSC4138) HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
- Resolve and remove some "features" from upstream that result in concurrency hazards, exponential backoff issues, or arbitrary performance limiters - Resolve and remove some "features" from upstream that result in concurrency hazards, exponential backoff issues, or arbitrary performance limiters
- Find more servers for outbound federation `/hierarchy` requests instead of just the room ID server name - Find more servers for outbound federation `/hierarchy` requests instead of just the room ID server name
- Support for suggesting servers to join through at `/_matrix/client/v3/directory/room/{roomAlias}` - Support for suggesting servers to join through at `/_matrix/client/v3/directory/room/{roomAlias}`
@@ -55,6 +56,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events - On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events
- Add support for a "global ACLs" feature (`forbidden_remote_server_names`) that blocks inbound remote room invites, room joins by room ID on server name, room joins by room alias on server name, incoming federated joins, and incoming federated room directory requests. This is very helpful for blocking servers that are purely toxic/bad and serve no value in allowing our users to suffer from things like room invite spam or such. Please note that this is not a substitute for room ACLs. - Add support for a "global ACLs" feature (`forbidden_remote_server_names`) that blocks inbound remote room invites, room joins by room ID on server name, room joins by room alias on server name, incoming federated joins, and incoming federated room directory requests. This is very helpful for blocking servers that are purely toxic/bad and serve no value in allowing our users to suffer from things like room invite spam or such. Please note that this is not a substitute for room ACLs.
- Add support for a config option to forbid our local users from sending federated room directory requests for (`forbidden_remote_room_directory_server_names`). Similar to above, useful for blocking servers that help prevent our users from wandering into bad areas of Matrix via room directories of those malicious servers. - Add support for a config option to forbid our local users from sending federated room directory requests for (`forbidden_remote_room_directory_server_names`). Similar to above, useful for blocking servers that help prevent our users from wandering into bad areas of Matrix via room directories of those malicious servers.
- Add config option for auto remediating/deactivating local non-admin users who attempt to join bad/forbidden rooms (`auto_deactivate_banned_room_attempts`)
## Privacy/Security: ## Privacy/Security:
@@ -68,6 +70,9 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Config option to disable incoming and/or outgoing remote read receipts - Config option to disable incoming and/or outgoing remote read receipts
- Config option to disable incoming and/or outgoing remote typing indicators - Config option to disable incoming and/or outgoing remote typing indicators
- Config option to disable incoming, outgoing, and/or local presence - Config option to disable incoming, outgoing, and/or local presence
- Sanitise file names for the `Content-Disposition` header for all media requests (thumbnails, downloads, uploads)
- Return `inline` or `attachment` based on the detected file MIME type for the `Content-Disposition` and only allow images/videos/text/audio to be `inline`
- Send secure default HTTP headers such as a strong restrictive CSP, deny iframes, disable `X-XSS-Protection`, disable interest cohort in `Permission-Policy`, etc to mitigate any potential attack surface such as from untrusted media
## Administration/Logging: ## Administration/Logging:
@@ -81,7 +86,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Warn on unknown config options specified - Warn on unknown config options specified
- Add `/_conduwuit/server_version` route to return the version of conduwuit without relying on the federation API `/_matrix/federation/v1/version` - Add `/_conduwuit/server_version` route to return the version of conduwuit without relying on the federation API `/_matrix/federation/v1/version`
- Add configurable RocksDB recovery modes to aid in recovering corrupted RocksDB databases - Add configurable RocksDB recovery modes to aid in recovering corrupted RocksDB databases
- Support config options via `CONDUWUIT_` prefix - Support config options via `CONDUWUIT_` prefix and accessing non-global struct config options with the `__` split (e.g. `CONDUWUIT_WELL_KNOWN__SERVER`)
- Add support for listening on multiple TCP ports - Add support for listening on multiple TCP ports
- Disable update check by default as it's not useful for conduwuit - Disable update check by default as it's not useful for conduwuit
- **Opt-in** Sentry.io telemetry and metrics, mainly used for crash reporting - **Opt-in** Sentry.io telemetry and metrics, mainly used for crash reporting
@@ -89,7 +94,8 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
## Maintenance/Stability: ## Maintenance/Stability:
- GitLab CI ported to GitHub Actions - GitLab CI ported to GitHub Actions
- Repo is mirrored to GitHub, GitLab, git.gay, sourcehut, and Codeberg (see README.md for their links) - Repo is mirrored to GitHub, GitLab, git.gay, git.girlcock.ceo, sourcehut, and Codeberg (see README.md for their links)
- Docker container images published to GitLab Container Registry, GitHub Container Registry, and Dockerhub
- Extensively revamp the example config to be extremely helpful and useful to both new users and power users - Extensively revamp the example config to be extremely helpful and useful to both new users and power users
- Fixed every single clippy (default lints) and rustc warnings, including some that were performance related or potential safety issues / unsoundness - Fixed every single clippy (default lints) and rustc warnings, including some that were performance related or potential safety issues / unsoundness
- Add a **lot** of other clippy and rustc lints and a rustfmt.toml file - Add a **lot** of other clippy and rustc lints and a rustfmt.toml file
@@ -146,6 +152,7 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
- Implement running and diff'ing Complement results in CI - Implement running and diff'ing Complement results in CI
- Interest in supporting other operating systems such as macOS, BSDs, and Windows, and getting them added into CI and doing builds for them - Interest in supporting other operating systems such as macOS, BSDs, and Windows, and getting them added into CI and doing builds for them
- Add config option for disabling RocksDB Direct IO if needed - Add config option for disabling RocksDB Direct IO if needed
- Add various documentation on maintaining conduwuit, using RocksDB online backups, some troubleshooting, using admin commands, etc
- (Developers): Add support for tokio-console - (Developers): Add support for tokio-console
- (Developers): Add support for tracing flame graphs - (Developers): Add support for tracing flame graphs
- Add `release-debuginfo` Cargo build profile - Add `release-debuginfo` Cargo build profile
+63
View File
@@ -0,0 +1,63 @@
# Maintaining your conduwuit setup
## Moderation
conduwuit has moderation through admin room commands. "binary commands" (medium priority) and an admin API (low priority) is planned. Some moderation-related config options are available in the example config such as "global ACLs" and blocking media requests to certain servers. See the example config for the moderation config options under the "Moderation / Privacy / Security" section.
conduwuit has moderation admin commands for:
- managing room aliases (`!admin rooms alias`)
- managing room directory (`!admin rooms directory`)
- managing room banning/blocking and user removal (`!admin rooms moderation`)
- managing user accounts (`!admin users`)
- fetching `/.well-known/matrix/support` from servers (`!admin federation`)
- blocking incoming federation for certain rooms (not the same as room banning) (`!admin federation`)
- deleting media (see [the media section](#media))
Any commands with `-list` in them will require a codeblock in the message with each object being newline delimited. An example of doing this is:
````
!admin rooms moderation ban-list-of-rooms
```
!roomid1:server.name
!roomid2:server.name
!roomid3:server.name
```
````
## Database
If using RocksDB, there's very little you need to do. Compaction is ran automatically based on various defined thresholds tuned for conduwuit to be high performance with the least I/O amplifcation or overhead. Manually running compaction is not recommended, or compaction via a timer. RocksDB is built with io_uring support via liburing for async read I/O.
Some RocksDB settings can be adjusted such as the compression method chosen. See the RocksDB section in the [example config](configuration.md). btrfs users may benefit from disabling compression on RocksDB if CoW is in use.
RocksDB troubleshooting can be found [in the RocksDB section of troubleshooting](troubleshooting.md).
## Backups
Currently only RocksDB supports online backups. If you'd like to backup your database online without any downtime, see the `!admin server` command for the backup commands and the `database_backup_path` config options in the example config. Please note that the format of the database backup is not the exact same. This is unfortunately a bad design choice by Facebook as we are using the database backup engine API from RocksDB, however the data is still there and can still be joined together.
To restore a backup from an online RocksDB backup:
- shutdown conduwuit
- create a new directory for merging together the data
- in the online backup created, copy all `.sst` files in `$DATABASE_BACKUP_PATH/shared_checksum` to your new directory
- trim all the strings so instead of `######_sxxxxxxxxx.sst`, it reads `######.sst`. A way of doing this with sed and bash is `for file in *.sst; do mv "$file" "$(echo "$file" | sed 's/_s.*/.sst/')"; done`
- copy all the files in `$DATABASE_BACKUP_PATH/1` to your new directory
- set your `database_path` config option to your new directory, or replace your old one with the new one you crafted
- start up conduwuit again and it should open as normal
If you'd like to do an offline backup, shutdown conduwuit and copy your `database_path` directory elsewhere. This can be restored with no modifications needed.
Backing up media is also just copying the `media/` directory from your database directory.
## Media
Media still needs various work, however conduwuit implements media deletion via:
- MXC URI
- Delete list of MXC URIs
- Delete remote media in the past `N` seconds/minutes
See the `!admin media` command for further information. All media in conduwuit is stored at `$DATABASE_DIR/media`. This will be configurable soon.
If you are finding yourself needing extensive granular control over media, we recommend looking into [Matrix Media Repo](https://github.com/t2bot/matrix-media-repo). conduwuit intends to implement various utilities for media, but MMR is dedicated to extensive media management.
Built-in S3 support is also planned, but for now using a "S3 filesystem" on `media/` works. conduwuit also sends a `Cache-Control` header of 1 year and immutable for all media requests (download and thumbnail) to reduce unnecessary media requests from browsers.
+62
View File
@@ -0,0 +1,62 @@
# Troubleshooting conduwuit
> ## Docker users ⚠️
>
> Docker is extremely UX unfriendly. Because of this, a ton of issues or support is actually Docker support, not conduwuit support. We also cannot document the ever-growing list of Docker issues here.
>
> If you intend on asking for support and you are using Docker, **PLEASE** triple validate your issues are **NOT** because you have a misconfiguration in your Docker setup.
>
> If there are things like Compose file issues or Dockerhub image issues, those can still be mentioned as long as they're something we can fix.
## Rocksdb / database issues
#### Direct IO
Some filesystems may not like RocksDB using [Direct IO](https://github.com/facebook/rocksdb/wiki/Direct-IO). Direct IO is for non-buffered I/O which improves conduwuit performance, but at least FUSE is a filesystem potentially known to not like this. See the [example config](configuration.md) for disabling it if needed. Issues from Direct IO on unsupported filesystems are usually shown as startup errors.
#### Database corruption
If your database is corrupted and is failing to start (e.g. checksum mismatch), it may be recoverable but careful steps must be taken, and there is no guarantee it may be recoverable.
RocksDB has the following recovery modes:
- `TolerateCorruptedTailRecords`
- `AbsoluteConsistency`
- `PointInTime`
- `SkipAnyCorruptedRecord`
By default, conduwuit uses `TolerateCorruptedTailRecords` as generally these may be due to bad federation and we can re-fetch the correct data over federation. The RocksDB default is `PointInTime` which will attempt to restore a "snapshot" of the data when it was last known to be good. This data can be either a few seconds old, or multiple minutes prior. `PointInTime` may not be suitable for default usage due to clients and servers possibly not being able to handle sudden "backwards time travels", and `AbsoluteConsistency` may be too strict.
`AbsoluteConsistency` will fail to start the database if any sign of corruption is detected. `SkipAnyCorruptedRecord` will skip all forms of corruption unless it forbids the database from opening (e.g. too severe). Usage of `SkipAnyCorruptedRecord` voids any support as this may cause more damage and/or leave your database in a permanently inconsistent state, but it may do something if `PointInTime` does not work as a last ditch effort.
With this in mind:
- First start conduwuit with the `PointInTime` recovery method. See the [example config](configuration.md) for how to do this using `rocksdb_recovery_mode`
- If your database successfully opens, clients are recommended to clear their client cache to account for the rollback
- Leave your conduwuit running in `PointInTime` for at least 30-60 minutes so as much possible corruption is restored
- If all goes will, you should be able to restore back to using `TolerateCorruptedTailRecords` and you have successfully recovered your database
## Media
#### "File name too long"
If you are running into the "file name is too long" OS error for media requests, your filesystem cannot handle file name lengths >=255 characters. This is unfortuntely due to Conduit (upstream) using base64 for file name keys which is very problematic for some filesystems as the base64 input is untrusted and long file names or specific inputs can cause this. If you would like to avoid this, you may build conduwuit yourself with the `sha256_media` feature. **This will lose database compatibility with upstream**.
## Debugging
Note that users should not really be debugging things. If you find yourself debugging and find the issue, please let us know and/or how we can fix it. Various debug commands can be found in `!admin debug`.
#### Debug/Trace log level
conduwuit builds without debug or trace log levels by default for at least performance reasons. This may change in the future and/or binaries providing such configurations may be provided. If you need to access debug/trace log levels, you will need to build without the `release_max_log_level` feature.
#### Changing log level dynamically
conduwuit supports changing the tracing log environment filter on-the-fly using the admin command `!admin debug change-log-level`. This accepts a string **without quotes** the same format as the `log` config option.
#### Pinging servers
conduwuit can ping other servers using `!admin debug ping`. This takes a server name and goes through the server discovery process and queries `/_matrix/federation/v1/version`. Errors are outputted.
#### Allocator memory stats
If using jemalloc (for now) and built with jemallocator's `stats` feature, you can see conduwuit's jemalloc memory stats by using `!admin debug memory-stats`
+46 -16
View File
@@ -1,25 +1,55 @@
# Setting up TURN/STURN # Setting up TURN/STURN
## General instructions In order to make or receive calls, a TURN server is required. conduwuit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image.
* It is assumed you have a [Coturn server](https://github.com/coturn/coturn) up and running. See [Synapse reference implementation](https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md). ### Configuration
## Edit/Add a few settings to your existing conduit.toml Create a configuration file called `coturn.conf` containing:
```conf
use-auth-secret
static-auth-secret=<a secret key>
realm=<your server domain>
```
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
These same values need to be set in conduwuit. You can either modify conduwuit.toml to include these lines:
``` ```
# Refer to your Coturn settings. turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
# `your.turn.url` has to match the REALM setting of your Coturn as well as `transport`. turn_secret = "<secret key from coturn configuration>"
turn_uris = ["turn:your.turn.url?transport=udp", "turn:your.turn.url?transport=tcp"]
# static-auth-secret of your turnserver
turn_secret = "ADD SECRET HERE"
# If you have your TURN server configured to use a username and password
# you can provide these information too. In this case comment out `turn_secret above`!
#turn_username = ""
#turn_password = ""
``` ```
## Apply settings or append the following to the docker environment variables dependig on which configuration method you used earlier:
Restart Conduit. ```yml
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
```
Restart conduwuit to apply these changes.
### Run
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
```bash
docker run -d --network=host -v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn
```
or docker-compose. For the latter, paste the following section into a file called `docker-compose.yml`
and run `docker compose up -d` in the same directory.
```yml
version: 3
services:
turn:
container_name: coturn-server
image: docker.io/coturn/coturn
restart: unless-stopped
network_mode: "host"
volumes:
- ./coturn.conf:/etc/coturn/turnserver.conf
```
To understand why the host networking mode is used and explore alternative configuration options, please visit [Coturn's Docker documentation](https://github.com/coturn/coturn/blob/master/docker/coturn/README.md).
For security recommendations see Synapse's [Coturn documentation](https://element-hq.github.io/synapse/latest/turn-howto.html).
+1
View File
@@ -14,6 +14,7 @@ stdenv.mkDerivation {
include = [ include = [
"book.toml" "book.toml"
"conduwuit-example.toml" "conduwuit-example.toml"
"CONTRIBUTING.md"
"README.md" "README.md"
"debian/README.md" "debian/README.md"
"docs" "docs"
+1 -1
View File
@@ -533,7 +533,7 @@ pub(crate) async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Res
} }
// Make the user leave all rooms before deactivation // Make the user leave all rooms before deactivation
client_server::leave_all_rooms(sender_user).await?; client_server::leave_all_rooms(sender_user).await;
// Remove devices and mark account as deactivated // Remove devices and mark account as deactivated
services().users.deactivate_account(sender_user)?; services().users.deactivate_account(sender_user)?;
+65 -16
View File
@@ -17,7 +17,11 @@ use crate::{
debug_warn, debug_warn,
service::media::{FileMeta, UrlPreviewData}, service::media::{FileMeta, UrlPreviewData},
services, services,
utils::{self, server_name::server_is_ours}, utils::{
self,
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
server_name::server_is_ours,
},
Error, Result, Ruma, RumaResponse, Error, Result, Ruma, RumaResponse,
}; };
@@ -130,7 +134,13 @@ pub(crate) async fn create_content_route(
mxc.clone(), mxc.clone(),
body.filename body.filename
.as_ref() .as_ref()
.map(|filename| format!("attachment; filename={filename}")) .map(|filename| {
format!(
"{}; filename={}",
content_disposition_type(&body.file, &body.content_type),
sanitise_filename(filename.to_owned())
)
})
.as_deref(), .as_deref(),
body.content_type.as_deref(), body.content_type.as_deref(),
&body.file, &body.file,
@@ -175,19 +185,20 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
if let Some(FileMeta { if let Some(FileMeta {
content_type, content_type,
file, file,
.. content_disposition,
}) = services().media.get(mxc.clone()).await? }) = services().media.get(mxc.clone()).await?
{ {
// TODO: safely sanitise filename to be included in the content-disposition let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
Ok(get_content::v3::Response { Ok(get_content::v3::Response {
file, file,
content_type, content_type,
content_disposition: Some("attachment".to_owned()), content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
}) })
} else if !server_is_ours(&body.server_name) && body.allow_remote { } else if !server_is_ours(&body.server_name) && body.allow_remote {
get_remote_content( let response = get_remote_content(
&mxc, &mxc,
&body.server_name, &body.server_name,
body.media_id.clone(), body.media_id.clone(),
@@ -198,6 +209,20 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
.map_err(|e| { .map_err(|e| {
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e); debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
Error::BadRequest(ErrorKind::NotFound, "Remote media error.") Error::BadRequest(ErrorKind::NotFound, "Remote media error.")
})?;
let content_disposition = Some(make_content_disposition(
&response.file,
&response.content_type,
response.content_disposition,
));
Ok(get_content::v3::Response {
file: response.file,
content_type: response.content_type,
content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
}) })
} else { } else {
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
@@ -238,13 +263,15 @@ pub(crate) async fn get_content_as_filename_route(
if let Some(FileMeta { if let Some(FileMeta {
content_type, content_type,
file, file,
.. content_disposition,
}) = services().media.get(mxc.clone()).await? }) = services().media.get(mxc.clone()).await?
{ {
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
Ok(get_content_as_filename::v3::Response { Ok(get_content_as_filename::v3::Response {
file, file,
content_type, content_type,
content_disposition: Some("attachment".to_owned()), content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
}) })
@@ -258,13 +285,21 @@ pub(crate) async fn get_content_as_filename_route(
) )
.await .await
{ {
Ok(remote_content_response) => Ok(get_content_as_filename::v3::Response { Ok(remote_content_response) => {
content_disposition: Some("attachment".to_owned()), let content_disposition = Some(make_content_disposition(
&remote_content_response.file,
&remote_content_response.content_type,
remote_content_response.content_disposition,
));
Ok(get_content_as_filename::v3::Response {
content_disposition,
content_type: remote_content_response.content_type, content_type: remote_content_response.content_type,
file: remote_content_response.file, file: remote_content_response.file,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
}), })
},
Err(e) => { Err(e) => {
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e); debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error.")) Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
@@ -309,7 +344,7 @@ pub(crate) async fn get_content_thumbnail_route(
if let Some(FileMeta { if let Some(FileMeta {
content_type, content_type,
file, file,
.. content_disposition,
}) = services() }) = services()
.media .media
.get_thumbnail( .get_thumbnail(
@@ -323,12 +358,14 @@ pub(crate) async fn get_content_thumbnail_route(
) )
.await? .await?
{ {
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
Ok(get_content_thumbnail::v3::Response { Ok(get_content_thumbnail::v3::Response {
file, file,
content_type, content_type,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
content_disposition: Some("attachment".to_owned()), content_disposition,
}) })
} else if !server_is_ours(&body.server_name) && body.allow_remote { } else if !server_is_ours(&body.server_name) && body.allow_remote {
if services() if services()
@@ -373,12 +410,18 @@ pub(crate) async fn get_content_thumbnail_route(
) )
.await?; .await?;
let content_disposition = Some(make_content_disposition(
&get_thumbnail_response.file,
&get_thumbnail_response.content_type,
get_thumbnail_response.content_disposition,
));
Ok(get_content_thumbnail::v3::Response { Ok(get_content_thumbnail::v3::Response {
file: get_thumbnail_response.file, file: get_thumbnail_response.file,
content_type: get_thumbnail_response.content_type, content_type: get_thumbnail_response.content_type,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
content_disposition: Some("attachment".to_owned()), content_disposition,
}) })
}, },
Err(e) => { Err(e) => {
@@ -437,12 +480,18 @@ async fn get_remote_content(
) )
.await?; .await?;
let content_disposition = Some(make_content_disposition(
&content_response.file,
&content_response.content_type,
content_response.content_disposition,
));
services() services()
.media .media
.create( .create(
None, None,
mxc.to_owned(), mxc.to_owned(),
Some("attachment"), content_disposition.as_deref(),
content_response.content_type.as_deref(), content_response.content_type.as_deref(),
&content_response.file, &content_response.file,
) )
@@ -451,7 +500,7 @@ async fn get_remote_content(
Ok(get_content::v3::Response { Ok(get_content::v3::Response {
file: content_response.file, file: content_response.file,
content_type: content_response.content_type, content_type: content_response.content_type,
content_disposition: Some("attachment".to_owned()), content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
}) })
+100 -183
View File
@@ -21,12 +21,13 @@ use ruma::{
room::{ room::{
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent}, join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
member::{MembershipState, RoomMemberEventContent}, member::{MembershipState, RoomMemberEventContent},
message::RoomMessageEventContent,
}, },
StateEventType, TimelineEventType, StateEventType, TimelineEventType,
}, },
serde::Base64, serde::Base64,
state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName, state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
OwnedUserId, RoomId, RoomVersionId, UserId, OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
}; };
use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@@ -40,6 +41,91 @@ use crate::{
Error, PduEvent, Result, Ruma, Error, PduEvent, Result, Ruma,
}; };
/// Checks if the room is banned in any way possible and the sender user is not
/// an admin.
///
/// Performs automatic deactivation if `auto_deactivate_banned_room_attempts` is
/// enabled
#[tracing::instrument]
async fn banned_room_check(user_id: &UserId, room_id: Option<&RoomId>, server_name: Option<&ServerName>) -> Result<()> {
if !services().users.is_admin(user_id)? {
if let Some(room_id) = room_id {
if services().rooms.metadata.is_banned(room_id)?
|| services()
.globals
.config
.forbidden_remote_server_names
.contains(&room_id.server_name().unwrap().to_owned())
{
warn!(
"User {user_id} who is not an admin attempted to send an invite for or attempted to join a banned \
room or banned room server name: {room_id}."
);
if services()
.globals
.config
.auto_deactivate_banned_room_attempts
{
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Automatically deactivating user {user_id} due to attempted banned room join"
)))
.await;
// ignore errors
leave_all_rooms(user_id).await;
_ = services().users.deactivate_account(user_id);
}
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
} else if let Some(server_name) = server_name {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server_name.to_owned())
{
warn!(
"User {user_id} who is not an admin tried joining a room which has the server name {server_name} \
that is globally forbidden. Rejecting.",
);
if services()
.globals
.config
.auto_deactivate_banned_room_attempts
{
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Automatically deactivating user {user_id} due to attempted banned room join"
)))
.await;
// ignore errors
leave_all_rooms(user_id).await;
_ = services().users.deactivate_account(user_id);
}
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
}
Ok(())
}
/// # `POST /_matrix/client/r0/rooms/{roomId}/join` /// # `POST /_matrix/client/r0/rooms/{roomId}/join`
/// ///
/// Tries to join the sender user into a room. /// Tries to join the sender user into a room.
@@ -53,32 +139,7 @@ pub(crate) async fn join_room_by_id_route(
) -> Result<join_room_by_id::v3::Response> { ) -> Result<join_room_by_id::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? { banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if let Some(server) = body.room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room ID {} which has a server name that is globally forbidden. \
Rejecting.",
body.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
// There is no body.server_name for /roomId/join // There is no body.server_name for /roomId/join
let mut servers = services() let mut servers = services()
@@ -131,31 +192,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) { let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
Ok(room_id) => { Ok(room_id) => {
if services().rooms.metadata.is_banned(&room_id)? && !services().users.is_admin(sender_user)? { banned_room_check(sender_user, Some(&room_id), room_id.server_name()).await?;
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if let Some(server) = room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room ID {room_id} which has a server name that is globally \
forbidden. Rejecting.",
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
let mut servers = body.server_name.clone(); let mut servers = body.server_name.clone();
servers.extend( servers.extend(
@@ -186,69 +223,9 @@ pub(crate) async fn join_room_by_id_or_alias_route(
(servers, room_id) (servers, room_id)
}, },
Err(room_alias) => { Err(room_alias) => {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&room_alias.server_name().to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room alias {room_alias} which has a server name that is \
globally forbidden. Rejecting.",
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?; let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
if services().rooms.metadata.is_banned(&response.room_id)? && !services().users.is_admin(sender_user)? { banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name())).await?;
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&room_alias.server_name().to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which the alias has a \
server name that is globally forbidden. Rejecting.",
&response.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
if let Some(server) = response.room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
&& !services().users.is_admin(sender_user)?
{
warn!(
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which has a server \
name that is globally forbidden. Rejecting.",
&response.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This remote server is banned on this homeserver.",
));
}
}
let mut servers = body.server_name; let mut servers = body.server_name;
servers.extend(response.servers); servers.extend(response.servers);
@@ -321,30 +298,7 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
)); ));
} }
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? { banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
info!(
"Local user {} who is not an admin attempted to send an invite for banned room {}.",
&sender_user, &body.room_id
);
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"This room is banned on this homeserver.",
));
}
if let Some(server) = body.room_id.server_name() {
if services()
.globals
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Server is banned on this homeserver.",
));
}
}
if let invite_user::v3::InvitationRecipient::UserId { if let invite_user::v3::InvitationRecipient::UserId {
user_id, user_id,
@@ -363,15 +317,6 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Result<kick_user::v3::Response> { pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Result<kick_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if let Ok(true) = services()
.rooms
.state_cache
.is_left(sender_user, &body.room_id)
{
info!("{} is not in room {}", &body.user_id, &body.room_id);
return Ok(kick_user::v3::Response {});
}
let mut event: RoomMemberEventContent = serde_json::from_str( let mut event: RoomMemberEventContent = serde_json::from_str(
services() services()
.rooms .rooms
@@ -428,17 +373,6 @@ pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Resul
pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> { pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if let Ok(Some(membership_event)) = services()
.rooms
.state_accessor
.get_member(&body.room_id, sender_user)
{
if membership_event.membership == MembershipState::Ban {
info!("{} is already banned in {}", &body.user_id, &body.room_id);
return Ok(ban_user::v3::Response {});
}
}
let event = services() let event = services()
.rooms .rooms
.state_accessor .state_accessor
@@ -446,11 +380,11 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
.map_or( .map_or(
Ok(RoomMemberEventContent { Ok(RoomMemberEventContent {
membership: MembershipState::Ban, membership: MembershipState::Ban,
displayname: services().users.displayname(&body.user_id)?, displayname: None,
avatar_url: services().users.avatar_url(&body.user_id)?, avatar_url: None,
is_direct: None, is_direct: None,
third_party_invite: None, third_party_invite: None,
blurhash: services().users.blurhash(&body.user_id)?, blurhash: services().users.blurhash(&body.user_id).unwrap_or_default(),
reason: body.reason.clone(), reason: body.reason.clone(),
join_authorized_via_users_server: None, join_authorized_via_users_server: None,
}), }),
@@ -458,14 +392,8 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
serde_json::from_str(event.content.get()) serde_json::from_str(event.content.get())
.map(|event: RoomMemberEventContent| RoomMemberEventContent { .map(|event: RoomMemberEventContent| RoomMemberEventContent {
membership: MembershipState::Ban, membership: MembershipState::Ban,
displayname: services() displayname: None,
.users avatar_url: None,
.displayname(&body.user_id)
.unwrap_or_default(),
avatar_url: services()
.users
.avatar_url(&body.user_id)
.unwrap_or_default(),
blurhash: services().users.blurhash(&body.user_id).unwrap_or_default(), blurhash: services().users.blurhash(&body.user_id).unwrap_or_default(),
reason: body.reason.clone(), reason: body.reason.clone(),
join_authorized_via_users_server: None, join_authorized_via_users_server: None,
@@ -514,17 +442,6 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Result<unban_user::v3::Response> { pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Result<unban_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if let Ok(Some(membership_event)) = services()
.rooms
.state_accessor
.get_member(&body.room_id, sender_user)
{
if membership_event.membership != MembershipState::Ban {
info!("{} is already unbanned in {}", &body.user_id, &body.room_id);
return Ok(unban_user::v3::Response {});
}
}
let mut event: RoomMemberEventContent = serde_json::from_str( let mut event: RoomMemberEventContent = serde_json::from_str(
services() services()
.rooms .rooms
@@ -1606,8 +1523,9 @@ pub(crate) async fn invite_helper(
Ok(()) Ok(())
} }
// Make a user leave all their joined rooms // Make a user leave all their joined rooms, forgets all rooms, and ignores
pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> { // errors
pub(crate) async fn leave_all_rooms(user_id: &UserId) {
let all_rooms = services() let all_rooms = services()
.rooms .rooms
.state_cache .state_cache
@@ -1627,10 +1545,9 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
}; };
// ignore errors // ignore errors
_ = services().rooms.state_cache.forget(&room_id, user_id);
_ = leave_room(user_id, &room_id, None).await; _ = leave_room(user_id, &room_id, None).await;
} }
Ok(())
} }
pub(crate) async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> { pub(crate) async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
+2 -2
View File
@@ -91,12 +91,12 @@ fn is_report_valid(
)); ));
} }
if services() if !services()
.rooms .rooms
.state_cache .state_cache
.room_members(&pdu.room_id) .room_members(&pdu.room_id)
.filter_map(Result::ok) .filter_map(Result::ok)
.any(|user_id| user_id != *sender_user) .any(|user_id| user_id == *sender_user)
{ {
return Err(Error::BadRequest( return Err(Error::BadRequest(
ErrorKind::NotFound, ErrorKind::NotFound,
+6
View File
@@ -207,6 +207,8 @@ pub(crate) struct Config {
#[serde(default = "Vec::new")] #[serde(default = "Vec::new")]
pub(crate) auto_join_rooms: Vec<OwnedRoomId>, pub(crate) auto_join_rooms: Vec<OwnedRoomId>,
#[serde(default)]
pub(crate) auto_deactivate_banned_room_attempts: bool,
#[serde(default = "default_rocksdb_log_level")] #[serde(default = "default_rocksdb_log_level")]
pub(crate) rocksdb_log_level: String, pub(crate) rocksdb_log_level: String,
@@ -612,6 +614,10 @@ impl fmt::Display for Config {
"Allow incoming profile lookup federation requests", "Allow incoming profile lookup federation requests",
&self.allow_profile_lookup_federation_requests.to_string(), &self.allow_profile_lookup_federation_requests.to_string(),
), ),
(
"Auto deactivate banned room join attempts",
&self.auto_deactivate_banned_room_attempts.to_string(),
),
("Notification push path", &self.notification_push_path), ("Notification push path", &self.notification_push_path),
("Allow room creation", &self.allow_room_creation.to_string()), ("Allow room creation", &self.allow_room_creation.to_string()),
( (
+34 -1
View File
@@ -6,7 +6,7 @@ use axum::{
Router, Router,
}; };
use http::{ use http::{
header::{self, HeaderName}, header::{self, HeaderName, HeaderValue},
Method, StatusCode, Uri, Method, StatusCode, Uri,
}; };
use ruma::api::client::{ use ruma::api::client::{
@@ -17,6 +17,7 @@ use tower::ServiceBuilder;
use tower_http::{ use tower_http::{
catch_panic::CatchPanicLayer, catch_panic::CatchPanicLayer,
cors::{self, CorsLayer}, cors::{self, CorsLayer},
set_header::SetResponseHeaderLayer,
trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer}, trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer},
ServiceBuilderExt as _, ServiceBuilderExt as _,
}; };
@@ -32,6 +33,9 @@ pub(crate) async fn build(server: &Server) -> io::Result<axum::routing::IntoMake
let base_middlewares = base_middlewares.layer(sentry_tower::NewSentryLayer::<http::Request<_>>::new_from_top()); let base_middlewares = base_middlewares.layer(sentry_tower::NewSentryLayer::<http::Request<_>>::new_from_top());
let x_forwarded_for = HeaderName::from_static("x-forwarded-for"); let x_forwarded_for = HeaderName::from_static("x-forwarded-for");
let permissions_policy = HeaderName::from_static("permissions-policy");
let origin_agent_cluster = HeaderName::from_static("origin-agent-cluster"); // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin-Agent-Cluster
let middlewares = base_middlewares let middlewares = base_middlewares
.sensitive_headers([header::AUTHORIZATION]) .sensitive_headers([header::AUTHORIZATION])
.sensitive_request_headers([x_forwarded_for].into()) .sensitive_request_headers([x_forwarded_for].into())
@@ -44,6 +48,33 @@ pub(crate) async fn build(server: &Server) -> io::Result<axum::routing::IntoMake
.on_response(DefaultOnResponse::new().level(Level::DEBUG)), .on_response(DefaultOnResponse::new().level(Level::DEBUG)),
) )
.layer(axum::middleware::from_fn(request_handle)) .layer(axum::middleware::from_fn(request_handle))
.layer(SetResponseHeaderLayer::if_not_present(
origin_agent_cluster,
HeaderValue::from_static("?1"),
))
.layer(SetResponseHeaderLayer::if_not_present(
header::X_CONTENT_TYPE_OPTIONS,
HeaderValue::from_static("nosniff"),
))
.layer(SetResponseHeaderLayer::if_not_present(
header::X_XSS_PROTECTION,
HeaderValue::from_static("0"),
))
.layer(SetResponseHeaderLayer::if_not_present(
header::X_FRAME_OPTIONS,
HeaderValue::from_static("DENY"),
))
.layer(SetResponseHeaderLayer::if_not_present(
permissions_policy,
HeaderValue::from_static("interest-cohort=(),browsing-topics=()"),
))
.layer(SetResponseHeaderLayer::if_not_present(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static(
"sandbox; default-src 'none'; font-src 'none'; script-src 'none'; plugin-types application/pdf; \
style-src 'unsafe-inline'; object-src 'self'; frame-ancesors 'none';",
),
))
.layer(cors_layer(server)) .layer(cors_layer(server))
.layer(DefaultBodyLimit::max( .layer(DefaultBodyLimit::max(
server server
@@ -130,6 +161,8 @@ fn request_result_log(method: &Method, uri: &Uri, result: &axum::response::Respo
} }
} }
/// Cross-Origin-Resource-Sharing header as defined by spec:
/// <https://spec.matrix.org/latest/client-server-api/#web-browser-clients>
fn cors_layer(_server: &Server) -> CorsLayer { fn cors_layer(_server: &Server) -> CorsLayer {
const METHODS: [Method; 7] = [ const METHODS: [Method; 7] = [
Method::GET, Method::GET,
+2 -2
View File
@@ -167,7 +167,7 @@ pub(crate) async fn deactivate(
services().users.deactivate_account(&user_id)?; services().users.deactivate_account(&user_id)?;
if leave_rooms { if leave_rooms {
leave_all_rooms(&user_id).await?; leave_all_rooms(&user_id).await;
} }
Ok(RoomMessageEventContent::text_plain(format!( Ok(RoomMessageEventContent::text_plain(format!(
@@ -282,7 +282,7 @@ pub(crate) async fn deactivate_all(body: Vec<&str>, leave_rooms: bool, force: bo
if leave_rooms { if leave_rooms {
for &user_id in &user_ids { for &user_id in &user_ids {
_ = leave_all_rooms(user_id).await; leave_all_rooms(user_id).await;
} }
} }
+88
View File
@@ -0,0 +1,88 @@
use infer::MatcherType;
/// Returns a Content-Disposition of `attachment` or `inline`, depending on the
/// *parsed* contents of the file uploaded via format magic keys using `infer`
/// crate (basically libmagic without needing libmagic).
///
/// This forbids trusting what the client or remote server says the file is from
/// their `Content-Type` and we try to detect it ourselves. Also returns
/// `attachment` if the Content-Type does not match what we detected.
///
/// TODO: add a "strict" function for comparing the Content-Type with what we
/// detected: `file_type.mime_type() != content_type`
pub(crate) fn content_disposition_type(buf: &[u8], _content_type: &Option<String>) -> &'static str {
let Some(file_type) = infer::get(buf) else {
return "attachment";
};
match file_type.matcher_type() {
MatcherType::Image | MatcherType::Audio | MatcherType::Text | MatcherType::Video => "inline",
_ => "attachment",
}
}
/// sanitises the file name for the Content-Disposition using
/// `sanitize_filename` crate
#[tracing::instrument]
pub(crate) fn sanitise_filename(filename: String) -> String {
let options = sanitize_filename::Options {
truncate: false,
..Default::default()
};
sanitize_filename::sanitize_with_options(filename, options)
}
/// creates the final Content-Disposition based on whether the filename exists
/// or not.
///
/// if filename exists: `Content-Disposition: attachment/inline;
/// filename=filename.ext` else: `Content-Disposition: attachment/inline`
#[tracing::instrument(skip(file))]
pub(crate) fn make_content_disposition(
file: &[u8], content_type: &Option<String>, content_disposition: Option<String>,
) -> String {
let filename = content_disposition.map_or_else(String::new, |content_disposition| {
let (_, filename) = content_disposition
.split_once("filename=")
.unwrap_or(("", ""));
if filename.is_empty() {
String::new()
} else {
sanitise_filename(filename.to_owned())
}
});
if !filename.is_empty() {
// Content-Disposition: attachment/inline; filename=filename.ext
format!("{}; filename={}", content_disposition_type(file, content_type), filename)
} else {
// Content-Disposition: attachment/inline
String::from(content_disposition_type(file, content_type))
}
}
#[cfg(test)]
mod tests {
#[test]
fn string_sanitisation() {
const SAMPLE: &str =
"🏳️‍⚧️this\\r\\n įs \r\\n ä \\r\nstrïng 🥴that\n\r ../../../../../../../may be\r\n malicious🏳️‍⚧️";
const SANITISED: &str = "🏳️‍⚧️thisrn įs n ä rstrïng 🥴that ..............may be malicious🏳️‍⚧️";
let options = sanitize_filename::Options {
windows: true,
truncate: true,
replacement: "",
};
// cargo test -- --nocapture
println!("{}", SAMPLE);
println!("{}", sanitize_filename::sanitize_with_options(SAMPLE, options.clone()));
println!("{:?}", SAMPLE);
println!("{:?}", sanitize_filename::sanitize_with_options(SAMPLE, options.clone()));
assert_eq!(SANITISED, sanitize_filename::sanitize_with_options(SAMPLE, options.clone()));
}
}
+1
View File
@@ -1,4 +1,5 @@
pub(crate) mod clap; pub(crate) mod clap;
pub(crate) mod content_disposition;
pub(crate) mod debug; pub(crate) mod debug;
pub(crate) mod error; pub(crate) mod error;
pub(crate) mod server_name; pub(crate) mod server_name;