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 { Instance } from '@/models/entities/instance.js';
import { DAY } from '@/const.js';
import { shouldBlockInstance } from './should-block-instance';
// Threshold from last contact after which an instance will be considered
// "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
* @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
const { blockedHosts } = await fetchMeta();
const skipped = hosts.filter(host => blockedHosts.includes(host));
const meta = await fetchMeta();
const shouldSkip = await Promise.all(hosts.map(host => shouldBlockInstance(host, meta)));
const skipped = hosts.filter((_, i) => shouldSkip[i]);
// if possible return early and skip accessing the database
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
* @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]);
return skipped.length > 0;
}

View file

@ -2,6 +2,7 @@ import { db } from '@/db/postgre.js';
import { Instance } from '@/models/entities/instance.js';
import { Packed } from '@/misc/schema.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { shouldBlockInstance } from '@/misc/should-block-instance';
export const InstanceRepository = db.getRepository(Instance).extend({
async pack(
@ -20,7 +21,7 @@ export const InstanceRepository = db.getRepository(Instance).extend({
lastCommunicatedAt: instance.lastCommunicatedAt.toISOString(),
isNotResponding: instance.isNotResponding,
isSuspended: instance.isSuspended,
isBlocked: meta.blockedHosts.includes(instance.host),
isBlocked: await shouldBlockInstance(instance.host),
softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion,
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 { CacheableRemoteUser } from '@/models/entities/user.js';
import { UserPublickey } from '@/models/entities/user-publickey.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
const logger = new Logger('inbox');
@ -34,7 +35,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
// interrupt if blocked
const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) {
if (await shouldBlockInstance(host, meta)) {
return `Blocked request: ${host}`;
}
@ -123,7 +124,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
// ブロックしてたら中断
const ldHost = extractDbHost(authUser.user.uri);
if (meta.blockedHosts.includes(ldHost)) {
if (await shouldBlockInstance(ldHost, meta)) {
return `Blocked request: ${ldHost}`;
}
} else {

View file

@ -6,6 +6,7 @@ import { URL } from 'url';
import { toPuny } from '@/misc/convert-host.js';
import DbResolver from '@/remote/activitypub/db-resolver.js';
import { getApId } from '@/remote/activitypub/type.js';
import { shouldBlockInstance } from '@/misc/should-block-instance';
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 host = toPuny(keyId.hostname);
if (meta.blockedHosts.includes(host)) {
if (await shouldBlockInstance(host, meta)) {
return 403;
}

View file

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

View file

@ -27,6 +27,7 @@ import { parseAudience } from '../audience.js';
import { extractApMentions } from './mention.js';
import DbResolver from '../db-resolver.js';
import { StatusError } from '@/misc/fetch.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
const logger = apLogger;
@ -275,8 +276,7 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
if (uri == null) throw new Error('missing uri');
// Abort if origin host is blocked
const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`);
if (await shouldBlockInstance(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`);
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 { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderFollow from '@/remote/activitypub/renderer/follow.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
export default class Resolver {
private history: Set<string>;
@ -72,7 +73,7 @@ export default class Resolver {
}
const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) {
if (await shouldBlockInstance(host, meta)) {
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 { SchemaType } from '@/misc/schema.js';
import { HOUR } from '@/const.js';
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
export const meta = {
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> {
// Wait if blocked.
const fetchedMeta = await fetchMeta();
if (fetchedMeta.blockedHosts.includes(extractDbHost(uri))) return null;
if (await shouldBlockInstance(extractDbHost(uri))) return null;
const dbResolver = new DbResolver();