Block subdomains of blocked hosts

This commit is contained in:
Francis Dinh 2022-12-24 14:39:54 -05:00
parent afce8f04ab
commit d9b7219404
No known key found for this signature in database
GPG Key ID: 7123E30E441E80DE
9 changed files with 37 additions and 17 deletions

View File

@ -0,0 +1,15 @@
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Instance } from '@/models/entities/instance.js';
import { Meta } from '@/models/entities/meta.js';
/**
* Returns whether a specific host (punycoded) should be blocked.
*
* @param host punycoded instance host
* @param meta a resolved Meta table
* @returns whether the given host should be blocked
*/
export async function shouldBlockInstance(host: Instance['host'], meta?: Meta): Promise<boolean> {
const { blockedHosts } = meta ?? await fetchMeta();
return blockedHosts.some(blockedHost => host === blockedHost || host.endsWith('.' + blockedHost));
}

View File

@ -3,6 +3,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js';
import { Instances } from '@/models/index.js'; import { Instances } from '@/models/index.js';
import { Instance } from '@/models/entities/instance.js'; import { Instance } from '@/models/entities/instance.js';
import { DAY } from '@/const.js'; import { DAY } from '@/const.js';
import { shouldBlockInstance } from './should-block-instance';
// Threshold from last contact after which an instance will be considered // Threshold from last contact after which an instance will be considered
// "dead" and should no longer get activities delivered to it. // "dead" and should no longer get activities delivered to it.
@ -14,11 +15,12 @@ const deadThreshold = 7 * DAY;
* @param hosts array of punycoded instance hosts * @param hosts array of punycoded instance hosts
* @returns array of punycoed instance hosts that should be skipped (subset of hosts parameter) * @returns array of punycoed instance hosts that should be skipped (subset of hosts parameter)
*/ */
export async function skippedInstances(hosts: Array<Instace['host']>): Array<Instance['host']> { export async function skippedInstances(hosts: Instance['host'][]): Promise<Instance['host'][]> {
// first check for blocked instances since that info may already be in memory // first check for blocked instances since that info may already be in memory
const { blockedHosts } = await fetchMeta(); const meta = await fetchMeta();
const shouldSkip = await Promise.all(hosts.map(host => shouldBlockInstance(host, meta)));
const skipped = hosts.filter(host => blockedHosts.includes(host)); const skipped = hosts.filter((_, i) => shouldSkip[i]);
// if possible return early and skip accessing the database // if possible return early and skip accessing the database
if (skipped.length === hosts.length) return hosts; if (skipped.length === hosts.length) return hosts;
@ -47,7 +49,7 @@ export async function skippedInstances(hosts: Array<Instace['host']>): Array<Ins
* @param host punycoded instance host * @param host punycoded instance host
* @returns whether the given host should be skipped * @returns whether the given host should be skipped
*/ */
export async function shouldSkipInstance(host: Instance['host']): boolean { export async function shouldSkipInstance(host: Instance['host']): Promise<boolean> {
const skipped = await skippedInstances([host]); const skipped = await skippedInstances([host]);
return skipped.length > 0; return skipped.length > 0;
} }

View File

@ -2,6 +2,7 @@ import { db } from '@/db/postgre.js';
import { Instance } from '@/models/entities/instance.js'; import { Instance } from '@/models/entities/instance.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { fetchMeta } from '@/misc/fetch-meta.js'; import { fetchMeta } from '@/misc/fetch-meta.js';
import { shouldBlockInstance } from '@/misc/should-block-instance';
export const InstanceRepository = db.getRepository(Instance).extend({ export const InstanceRepository = db.getRepository(Instance).extend({
async pack( async pack(
@ -20,7 +21,7 @@ export const InstanceRepository = db.getRepository(Instance).extend({
lastCommunicatedAt: instance.lastCommunicatedAt.toISOString(), lastCommunicatedAt: instance.lastCommunicatedAt.toISOString(),
isNotResponding: instance.isNotResponding, isNotResponding: instance.isNotResponding,
isSuspended: instance.isSuspended, isSuspended: instance.isSuspended,
isBlocked: meta.blockedHosts.includes(instance.host), isBlocked: await shouldBlockInstance(instance.host),
softwareName: instance.softwareName, softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion, softwareVersion: instance.softwareVersion,
openRegistrations: instance.openRegistrations, openRegistrations: instance.openRegistrations,

View File

@ -17,6 +17,7 @@ import { LdSignature } from '@/remote/activitypub/misc/ld-signature.js';
import { StatusError } from '@/misc/fetch.js'; import { StatusError } from '@/misc/fetch.js';
import { CacheableRemoteUser } from '@/models/entities/user.js'; import { CacheableRemoteUser } from '@/models/entities/user.js';
import { UserPublickey } from '@/models/entities/user-publickey.js'; import { UserPublickey } from '@/models/entities/user-publickey.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
const logger = new Logger('inbox'); const logger = new Logger('inbox');
@ -34,7 +35,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
// interrupt if blocked // interrupt if blocked
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) { if (await shouldBlockInstance(host, meta)) {
return `Blocked request: ${host}`; return `Blocked request: ${host}`;
} }
@ -123,7 +124,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
// ブロックしてたら中断 // ブロックしてたら中断
const ldHost = extractDbHost(authUser.user.uri); const ldHost = extractDbHost(authUser.user.uri);
if (meta.blockedHosts.includes(ldHost)) { if (await shouldBlockInstance(ldHost, meta)) {
return `Blocked request: ${ldHost}`; return `Blocked request: ${ldHost}`;
} }
} else { } else {

View File

@ -6,6 +6,7 @@ import { URL } from 'url';
import { toPuny } from '@/misc/convert-host.js'; import { toPuny } from '@/misc/convert-host.js';
import DbResolver from '@/remote/activitypub/db-resolver.js'; import DbResolver from '@/remote/activitypub/db-resolver.js';
import { getApId } from '@/remote/activitypub/type.js'; import { getApId } from '@/remote/activitypub/type.js';
import { shouldBlockInstance } from '@/misc/should-block-instance';
export default async function checkFetch(req: IncomingMessage): Promise<number> { export default async function checkFetch(req: IncomingMessage): Promise<number> {
@ -22,7 +23,7 @@ export default async function checkFetch(req: IncomingMessage): Promise<number>
const keyId = new URL(signature.keyId); const keyId = new URL(signature.keyId);
const host = toPuny(keyId.hostname); const host = toPuny(keyId.hostname);
if (meta.blockedHosts.includes(host)) { if (await shouldBlockInstance(host, meta)) {
return 403; return 403;
} }

View File

@ -5,11 +5,11 @@ import { IAnnounce, getApId } from '../../type.js';
import { fetchNote, resolveNote } from '../../models/note.js'; import { fetchNote, resolveNote } from '../../models/note.js';
import { apLogger } from '../../logger.js'; import { apLogger } from '../../logger.js';
import { extractDbHost } from '@/misc/convert-host.js'; import { extractDbHost } from '@/misc/convert-host.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { getApLock } from '@/misc/app-lock.js'; import { getApLock } from '@/misc/app-lock.js';
import { parseAudience } from '../../audience.js'; import { parseAudience } from '../../audience.js';
import { StatusError } from '@/misc/fetch.js'; import { StatusError } from '@/misc/fetch.js';
import { Notes } from '@/models/index.js'; import { Notes } from '@/models/index.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
const logger = apLogger; const logger = apLogger;
@ -24,8 +24,7 @@ export default async function(resolver: Resolver, actor: CacheableRemoteUser, ac
} }
// Interrupt if you block the announcement destination // Interrupt if you block the announcement destination
const meta = await fetchMeta(); if (await shouldBlockInstance(extractDbHost(uri))) return;
if (meta.blockedHosts.includes(extractDbHost(uri))) return;
const unlock = await getApLock(uri); const unlock = await getApLock(uri);

View File

@ -27,6 +27,7 @@ import { parseAudience } from '../audience.js';
import { extractApMentions } from './mention.js'; import { extractApMentions } from './mention.js';
import DbResolver from '../db-resolver.js'; import DbResolver from '../db-resolver.js';
import { StatusError } from '@/misc/fetch.js'; import { StatusError } from '@/misc/fetch.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
const logger = apLogger; const logger = apLogger;
@ -275,8 +276,7 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
if (uri == null) throw new Error('missing uri'); if (uri == null) throw new Error('missing uri');
// Abort if origin host is blocked // Abort if origin host is blocked
const meta = await fetchMeta(); if (await shouldBlockInstance(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`);
if (meta.blockedHosts.includes(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`);
const unlock = await getApLock(uri); const unlock = await getApLock(uri);

View File

@ -15,6 +15,7 @@ import renderQuestion from '@/remote/activitypub/renderer/question.js';
import renderCreate from '@/remote/activitypub/renderer/create.js'; import renderCreate from '@/remote/activitypub/renderer/create.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderFollow from '@/remote/activitypub/renderer/follow.js'; import renderFollow from '@/remote/activitypub/renderer/follow.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
export default class Resolver { export default class Resolver {
private history: Set<string>; private history: Set<string>;
@ -72,7 +73,7 @@ export default class Resolver {
} }
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) { if (await shouldBlockInstance(host, meta)) {
throw new Error('Instance is blocked'); throw new Error('Instance is blocked');
} }

View File

@ -13,6 +13,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js';
import { isActor, isPost, getApId } from '@/remote/activitypub/type.js'; import { isActor, isPost, getApId } from '@/remote/activitypub/type.js';
import { SchemaType } from '@/misc/schema.js'; import { SchemaType } from '@/misc/schema.js';
import { HOUR } from '@/const.js'; import { HOUR } from '@/const.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
export const meta = { export const meta = {
tags: ['federation'], tags: ['federation'],
@ -92,8 +93,7 @@ export default define(meta, paramDef, async (ps, me) => {
*/ */
async function fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { async function fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> {
// Wait if blocked. // Wait if blocked.
const fetchedMeta = await fetchMeta(); if (await shouldBlockInstance(extractDbHost(uri))) return null;
if (fetchedMeta.blockedHosts.includes(extractDbHost(uri))) return null;
const dbResolver = new DbResolver(); const dbResolver = new DbResolver();