Block subdomains of blocked hosts (#9310)
This commit is contained in:
commit
ebe0abe955
|
@ -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));
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
isCollection,
|
||||
isFlag,
|
||||
isMove,
|
||||
getApId,
|
||||
} from '../type.js';
|
||||
import { apLogger } from '../logger.js';
|
||||
import Resolver from '../resolver.js';
|
||||
|
@ -37,6 +38,8 @@ import block from './block/index.js';
|
|||
import flag from './flag/index.js';
|
||||
import move from './move/index.js';
|
||||
import type { IObject } from '../type.js';
|
||||
import { extractDbHost } from '@/misc/convert-host.js';
|
||||
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||
|
||||
export async function performActivity(actor: CacheableRemoteUser, activity: IObject) {
|
||||
if (isCollectionOrOrderedCollection(activity)) {
|
||||
|
@ -59,6 +62,12 @@ export async function performActivity(actor: CacheableRemoteUser, activity: IObj
|
|||
async function performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise<void> {
|
||||
if (actor.isSuspended) return;
|
||||
|
||||
if (typeof activity.id !== 'undefined') {
|
||||
const host = extractDbHost(getApId(activity));
|
||||
if (await shouldBlockInstance(host)) return;
|
||||
}
|
||||
|
||||
|
||||
if (isCreate(activity)) {
|
||||
await create(actor, activity);
|
||||
} else if (isDelete(activity)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { getInstanceActor } from '@/services/instance-actor.js';
|
|||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||
import { extractDbHost, isSelfHost } from '@/misc/convert-host.js';
|
||||
import { signedGet } from './request.js';
|
||||
import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
|
||||
import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection, getApId } from './type.js';
|
||||
import { FollowRequests, Notes, NoteReactions, Polls, Users } from '@/models/index.js';
|
||||
import { parseUri } from './db-resolver.js';
|
||||
import renderNote from '@/remote/activitypub/renderer/note.js';
|
||||
|
@ -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>;
|
||||
|
@ -31,9 +32,7 @@ export default class Resolver {
|
|||
}
|
||||
|
||||
public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> {
|
||||
const collection = typeof value === 'string'
|
||||
? await this.resolve(value)
|
||||
: value;
|
||||
const collection = await this.resolve(value);
|
||||
|
||||
if (isCollectionOrOrderedCollection(collection)) {
|
||||
return collection;
|
||||
|
@ -48,6 +47,12 @@ export default class Resolver {
|
|||
}
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
if (typeof value.id !== 'undefined') {
|
||||
const host = extractDbHost(getApId(value));
|
||||
if (await shouldBlockInstance(host)) {
|
||||
throw new Error('instance is blocked');
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -72,7 +77,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');
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue