diff options
| author | MeiMei <30769358+mei23@users.noreply.github.com> | 2020-10-18 01:46:40 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-10-18 01:46:40 +0900 |
| commit | 85a0f696bcea779b02749dae596fff94a1df2467 (patch) | |
| tree | 747ee51f96c7ada22a835dfc7faee0b13cdfd0e4 /src/remote/activitypub | |
| parent | Fix lint (#6732) (diff) | |
| download | sharkey-85a0f696bcea779b02749dae596fff94a1df2467.tar.gz sharkey-85a0f696bcea779b02749dae596fff94a1df2467.tar.bz2 sharkey-85a0f696bcea779b02749dae596fff94a1df2467.zip | |
ActivityPubでリモートのオブジェクトをGETするときのリクエストをHTTP Signatureで署名するオプション (#6731)
* Sign ActivityPub GET
* Fix v12, v12.48.0 UI bug
Diffstat (limited to 'src/remote/activitypub')
| -rw-r--r-- | src/remote/activitypub/request.ts | 97 | ||||
| -rw-r--r-- | src/remote/activitypub/resolver.ts | 13 |
2 files changed, 109 insertions, 1 deletions
diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index ab51fdd93c..0edfcee1e3 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -1,3 +1,4 @@ +import * as http from 'http'; import * as https from 'https'; import { sign } from 'http-signature'; import * as crypto from 'crypto'; @@ -7,6 +8,9 @@ import { ILocalUser } from '../../models/entities/user'; import { UserKeypairs } from '../../models'; import { ensure } from '../../prelude/ensure'; import { getAgentByUrl } from '../../misc/fetch'; +import { URL } from 'url'; +import got from 'got'; +import * as Got from 'got'; export default async (user: ILocalUser, url: string, object: any) => { const timeout = 10 * 1000; @@ -62,3 +66,96 @@ export default async (user: ILocalUser, url: string, object: any) => { req.end(data); }); }; + +/** + * Get AP object with http-signature + * @param user http-signature user + * @param url URL to fetch + */ +export async function signedGet(url: string, user: ILocalUser) { + const timeout = 10 * 1000; + + const keypair = await UserKeypairs.findOne({ + userId: user.id + }).then(ensure); + + const req = got.get<any>(url, { + headers: { + 'Accept': 'application/activity+json, application/ld+json', + 'User-Agent': config.userAgent, + }, + responseType: 'json', + timeout, + hooks: { + beforeRequest: [ + options => { + options.request = (url: URL, opt: http.RequestOptions, callback?: (response: any) => void) => { + // Select custom agent by URL + opt.agent = getAgentByUrl(url, false); + + // Wrap original https?.request + const requestFunc = url.protocol === 'http:' ? http.request : https.request; + const clientRequest = requestFunc(url, opt, callback) as http.ClientRequest; + + // HTTP-Signature + sign(clientRequest, { + authorizationHeaderName: 'Signature', + key: keypair.privateKey, + keyId: `${config.url}/users/${user.id}#main-key`, + headers: ['(request-target)', 'host', 'date', 'accept'] + }); + + return clientRequest; + }; + }, + ], + }, + retry: 0, + }); + + const res = await receiveResponce(req, 10 * 1024 * 1024); + + return res.body; +} + +/** + * Receive response (with size limit) + * @param req Request + * @param maxSize size limit + */ +export async function receiveResponce<T>(req: Got.CancelableRequest<Got.Response<T>>, maxSize: number) { + // 応答ヘッダでサイズチェック + req.on('response', (res: Got.Response) => { + const contentLength = res.headers['content-length']; + if (contentLength != null) { + const size = Number(contentLength); + if (size > maxSize) { + req.cancel(); + } + } + }); + + // 受信中のデータでサイズチェック + req.on('downloadProgress', (progress: Got.Progress) => { + if (progress.transferred > maxSize) { + req.cancel(); + } + }); + + // 応答取得 with ステータスコードエラーの整形 + const res = await req.catch(e => { + if (e.name === 'HTTPError') { + const statusCode = (e as Got.HTTPError).response.statusCode; + const statusMessage = (e as Got.HTTPError).response.statusMessage; + throw { + name: `StatusError`, + statusCode, + message: `${statusCode} ${statusMessage}`, + }; + } else { + throw e; + } + }); + + return res; +} diff --git a/src/remote/activitypub/resolver.ts b/src/remote/activitypub/resolver.ts index f4bf8f94f9..2871c1cb41 100644 --- a/src/remote/activitypub/resolver.ts +++ b/src/remote/activitypub/resolver.ts @@ -1,8 +1,13 @@ +import config from '../../config'; import { getJson } from '../../misc/fetch'; +import { ILocalUser } from '../../models/entities/user'; +import { getInstanceActor } from '../../services/instance-actor'; +import { signedGet } from './request'; import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type'; export default class Resolver { private history: Set<string>; + private user?: ILocalUser; constructor() { this.history = new Set(); @@ -39,7 +44,13 @@ export default class Resolver { this.history.add(value); - const object = await getJson(value, 'application/activity+json, application/ld+json'); + if (config.signToActivityPubGet && !this.user) { + this.user = await getInstanceActor(); + } + + const object = this.user + ? await signedGet(value, this.user) + : await getJson(value, 'application/activity+json, application/ld+json'); if (object == null || ( Array.isArray(object['@context']) ? |