feat: implement `attachLdSignatureForRelays` to control signing of Relayed activities
This commit is contained in:
parent
c344705d67
commit
32872181dd
|
@ -198,8 +198,10 @@ proxyRemoteFiles: true
|
||||||
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
||||||
#videoThumbnailGenerator: https://example.com
|
#videoThumbnailGenerator: https://example.com
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
# Sign outgoing ActivityPub GET request (default: true)
|
||||||
signToActivityPubGet: true
|
signToActivityPubGet: true
|
||||||
|
# Sign outgoing ActivityPub Activities (default: true)
|
||||||
|
attachLdSignatureForRelays: true
|
||||||
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
||||||
checkActivityPubGetSignature: false
|
checkActivityPubGetSignature: false
|
||||||
|
|
||||||
|
|
|
@ -270,8 +270,10 @@ proxyRemoteFiles: true
|
||||||
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
||||||
#videoThumbnailGenerator: https://example.com
|
#videoThumbnailGenerator: https://example.com
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
# Sign outgoing ActivityPub GET request (default: true)
|
||||||
signToActivityPubGet: true
|
signToActivityPubGet: true
|
||||||
|
# Sign outgoing ActivityPub Activities (default: true)
|
||||||
|
attachLdSignatureForRelays: true
|
||||||
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
||||||
checkActivityPubGetSignature: false
|
checkActivityPubGetSignature: false
|
||||||
|
|
||||||
|
|
|
@ -285,8 +285,10 @@ proxyRemoteFiles: true
|
||||||
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
||||||
#videoThumbnailGenerator: https://example.com
|
#videoThumbnailGenerator: https://example.com
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
# Sign outgoing ActivityPub GET request (default: true)
|
||||||
signToActivityPubGet: true
|
signToActivityPubGet: true
|
||||||
|
# Sign outgoing ActivityPub Activities (default: true)
|
||||||
|
attachLdSignatureForRelays: true
|
||||||
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
||||||
checkActivityPubGetSignature: false
|
checkActivityPubGetSignature: false
|
||||||
|
|
||||||
|
|
|
@ -208,8 +208,10 @@ id: "aidx"
|
||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
# Sign outgoing ActivityPub GET request (default: true)
|
||||||
signToActivityPubGet: true
|
signToActivityPubGet: true
|
||||||
|
# Sign outgoing ActivityPub Activities (default: true)
|
||||||
|
attachLdSignatureForRelays: true
|
||||||
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
# check that inbound ActivityPub GET requests are signed ("authorized fetch")
|
||||||
checkActivityPubGetSignature: false
|
checkActivityPubGetSignature: false
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import {fileURLToPath} from 'node:url';
|
||||||
import { dirname, resolve } from 'node:path';
|
import {dirname, resolve} from 'node:path';
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
import { globSync } from 'glob';
|
import {globSync} from 'glob';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import type { RedisOptions } from 'ioredis';
|
import type {RedisOptions} from 'ioredis';
|
||||||
|
|
||||||
type RedisOptionsSource = Partial<RedisOptions> & {
|
type RedisOptionsSource = Partial<RedisOptions> & {
|
||||||
host: string;
|
host: string;
|
||||||
|
@ -95,6 +95,7 @@ type Source = {
|
||||||
customMOTD?: string[];
|
customMOTD?: string[];
|
||||||
|
|
||||||
signToActivityPubGet?: boolean;
|
signToActivityPubGet?: boolean;
|
||||||
|
attachLdSignatureForRelays?: boolean;
|
||||||
checkActivityPubGetSignature?: boolean;
|
checkActivityPubGetSignature?: boolean;
|
||||||
|
|
||||||
perChannelMaxNoteCacheCount?: number;
|
perChannelMaxNoteCacheCount?: number;
|
||||||
|
@ -161,6 +162,7 @@ export type Config = {
|
||||||
proxyRemoteFiles: boolean | undefined;
|
proxyRemoteFiles: boolean | undefined;
|
||||||
customMOTD: string[] | undefined;
|
customMOTD: string[] | undefined;
|
||||||
signToActivityPubGet: boolean;
|
signToActivityPubGet: boolean;
|
||||||
|
attachLdSignatureForRelays: boolean;
|
||||||
checkActivityPubGetSignature: boolean | undefined;
|
checkActivityPubGetSignature: boolean | undefined;
|
||||||
|
|
||||||
version: string;
|
version: string;
|
||||||
|
@ -291,6 +293,7 @@ export function loadConfig(): Config {
|
||||||
proxyRemoteFiles: config.proxyRemoteFiles,
|
proxyRemoteFiles: config.proxyRemoteFiles,
|
||||||
customMOTD: config.customMOTD,
|
customMOTD: config.customMOTD,
|
||||||
signToActivityPubGet: config.signToActivityPubGet ?? true,
|
signToActivityPubGet: config.signToActivityPubGet ?? true,
|
||||||
|
attachLdSignatureForRelays: config.attachLdSignatureForRelays ?? true,
|
||||||
checkActivityPubGetSignature: config.checkActivityPubGetSignature,
|
checkActivityPubGetSignature: config.checkActivityPubGetSignature,
|
||||||
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
||||||
externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
|
externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
|
||||||
|
|
|
@ -3,36 +3,69 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createPublicKey, randomUUID } from 'node:crypto';
|
import {createPublicKey, randomUUID} from 'node:crypto';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import {Inject, Injectable} from '@nestjs/common';
|
||||||
import { In } from 'typeorm';
|
import {In} from 'typeorm';
|
||||||
import * as mfm from '@transfem-org/sfm-js';
|
import * as mfm from '@transfem-org/sfm-js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import {DI} from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type {Config} from '@/config.js';
|
||||||
import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
|
import type {MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser} from '@/models/User.js';
|
||||||
import type { IMentionedRemoteUsers, MiNote } from '@/models/Note.js';
|
import type {IMentionedRemoteUsers, MiNote} from '@/models/Note.js';
|
||||||
import type { MiBlocking } from '@/models/Blocking.js';
|
import type {MiBlocking} from '@/models/Blocking.js';
|
||||||
import type { MiRelay } from '@/models/Relay.js';
|
import type {MiRelay} from '@/models/Relay.js';
|
||||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
import type {MiDriveFile} from '@/models/DriveFile.js';
|
||||||
import type { MiNoteReaction } from '@/models/NoteReaction.js';
|
import type {MiNoteReaction} from '@/models/NoteReaction.js';
|
||||||
import type { MiEmoji } from '@/models/Emoji.js';
|
import type {MiEmoji} from '@/models/Emoji.js';
|
||||||
import type { MiPoll } from '@/models/Poll.js';
|
import type {MiPoll} from '@/models/Poll.js';
|
||||||
import type { MiPollVote } from '@/models/PollVote.js';
|
import type {MiPollVote} from '@/models/PollVote.js';
|
||||||
import { UserKeypairService } from '@/core/UserKeypairService.js';
|
import {UserKeypairService} from '@/core/UserKeypairService.js';
|
||||||
import { MfmService } from '@/core/MfmService.js';
|
import {MfmService} from '@/core/MfmService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import {UserEntityService} from '@/core/entities/UserEntityService.js';
|
||||||
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
import {DriveFileEntityService} from '@/core/entities/DriveFileEntityService.js';
|
||||||
import type { MiUserKeypair } from '@/models/UserKeypair.js';
|
import type {MiUserKeypair} from '@/models/UserKeypair.js';
|
||||||
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository, InstancesRepository } from '@/models/_.js';
|
import type {
|
||||||
import { bindThis } from '@/decorators.js';
|
DriveFilesRepository,
|
||||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
InstancesRepository,
|
||||||
import { isNotNull } from '@/misc/is-not-null.js';
|
NotesRepository,
|
||||||
import { IdService } from '@/core/IdService.js';
|
PollsRepository,
|
||||||
import { MetaService } from '../MetaService.js';
|
UserProfilesRepository,
|
||||||
import { JsonLdService } from './JsonLdService.js';
|
UsersRepository
|
||||||
import { ApMfmService } from './ApMfmService.js';
|
} from '@/models/_.js';
|
||||||
import { CONTEXT } from './misc/contexts.js';
|
import {bindThis} from '@/decorators.js';
|
||||||
import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
|
import {CustomEmojiService} from '@/core/CustomEmojiService.js';
|
||||||
|
import {isNotNull} from '@/misc/is-not-null.js';
|
||||||
|
import {IdService} from '@/core/IdService.js';
|
||||||
|
import {MetaService} from '../MetaService.js';
|
||||||
|
import {JsonLdService} from './JsonLdService.js';
|
||||||
|
import {ApMfmService} from './ApMfmService.js';
|
||||||
|
import {CONTEXT} from './misc/contexts.js';
|
||||||
|
import type {
|
||||||
|
IAccept,
|
||||||
|
IActivity,
|
||||||
|
IAdd,
|
||||||
|
IAnnounce,
|
||||||
|
IApDocument,
|
||||||
|
IApEmoji,
|
||||||
|
IApHashtag,
|
||||||
|
IApImage,
|
||||||
|
IApMention,
|
||||||
|
IBlock,
|
||||||
|
ICreate,
|
||||||
|
IDelete,
|
||||||
|
IFlag,
|
||||||
|
IFollow,
|
||||||
|
IKey,
|
||||||
|
ILike,
|
||||||
|
IMove,
|
||||||
|
IObject,
|
||||||
|
IPost,
|
||||||
|
IQuestion,
|
||||||
|
IReject,
|
||||||
|
IRemove,
|
||||||
|
ITombstone,
|
||||||
|
IUndo,
|
||||||
|
IUpdate
|
||||||
|
} from './type.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApRendererService {
|
export class ApRendererService {
|
||||||
|
@ -793,6 +826,12 @@ export class ApRendererService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> {
|
public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> {
|
||||||
|
// When using authorized fetch, Linked Data signatures are often undesired (as it can allow blocked instances to bypass the check).
|
||||||
|
// We allow admins to disable LD signatures for increased privacy, at the expense of increased incoming fetch (GET) requests.
|
||||||
|
if (!this.config.attachLdSignatureForRelays) {
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
|
||||||
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
||||||
|
|
||||||
const jsonLd = this.jsonLdService.use();
|
const jsonLd = this.jsonLdService.use();
|
||||||
|
|
Loading…
Reference in New Issue