forked from FoundKeyGang/FoundKey
BREAKING: server: remove wildcard blocking and instead block subdomains (#269)
Co-authored-by: Francis Dinh <normandy@biribiri.dev> Reviewed-on: FoundKeyGang/FoundKey#269 Changelog: Changed
This commit is contained in:
parent
4e74d26e45
commit
c1a51547a9
9 changed files with 30 additions and 44 deletions
|
@ -187,7 +187,7 @@ clearCachedFiles: "Clear cache"
|
||||||
clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?"
|
clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?"
|
||||||
blockedInstances: "Blocked Instances"
|
blockedInstances: "Blocked Instances"
|
||||||
blockedInstancesDescription: "List the hostnames of the instances that you want to\
|
blockedInstancesDescription: "List the hostnames of the instances that you want to\
|
||||||
\ block. Listed instances will no longer be able to communicate with this instance. Non-ASCII domain names must be encoded in punycode. You can use an asterisk (*) as a placeholder for zero or more character(s)."
|
\ block. Listed instances will no longer be able to communicate with this instance. Non-ASCII domain names must be encoded in punycode. Subdomains of the listed instances will also be blocked."
|
||||||
muteAndBlock: "Mutes and Blocks"
|
muteAndBlock: "Mutes and Blocks"
|
||||||
mutedUsers: "Muted users"
|
mutedUsers: "Muted users"
|
||||||
blockedUsers: "Blocked users"
|
blockedUsers: "Blocked users"
|
||||||
|
|
16
packages/backend/src/misc/should-block-instance.ts
Normal file
16
packages/backend/src/misc/should-block-instance.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
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 Promise contatining the information from the meta table (optional)
|
||||||
|
* @returns whether the given host should be blocked
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function shouldBlockInstance(host: Instance['host'], meta: Promise<Meta> = fetchMeta()): Promise<boolean> {
|
||||||
|
const { blockedHosts } = await meta;
|
||||||
|
return blockedHosts.some(blockedHost => host === blockedHost || host.endsWith('.' + blockedHost));
|
||||||
|
}
|
|
@ -2,39 +2,12 @@ import { db } from '@/db/postgre.js';
|
||||||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
import { fetchMeta } from '@/misc/fetch-meta.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 { Meta } from '@/models/entities/meta.js';
|
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||||
|
|
||||||
// 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.
|
||||||
const deadThreshold = 7 * DAY;
|
const deadThreshold = 7 * DAY;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a given host matches a wildcard pattern.
|
|
||||||
* @param host punycoded instance host
|
|
||||||
* @param pattern wildcard pattern containing a punycoded instance host
|
|
||||||
* @returns whether the post matches the pattern
|
|
||||||
*/
|
|
||||||
function matchHost(host: Instance['host'], pattern: string): boolean {
|
|
||||||
// Escape all of the regex special characters. Pattern from:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
|
||||||
const escape = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
||||||
const re = new RegExp('^' + pattern.split('*').map(escape).join('.*') + '$');
|
|
||||||
|
|
||||||
return re.test(host);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a specific host (punycoded) should be blocked.
|
|
||||||
*
|
|
||||||
* @param host punycoded instance host
|
|
||||||
* @param meta a Promise contatining the information from the meta table (oprional)
|
|
||||||
* @returns whether the given host should be blocked
|
|
||||||
*/
|
|
||||||
export async function shouldBlockInstance(host: string, meta: Promise<Meta> = fetchMeta()): Promise<boolean> {
|
|
||||||
const { blockedHosts } = await meta;
|
|
||||||
return blockedHosts.some(blockedHost => matchHost(host, blockedHost));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the subset of hosts which should be skipped.
|
* Returns the subset of hosts which should be skipped.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { db } from '@/db/postgre.js';
|
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 { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||||
|
|
||||||
export const InstanceRepository = db.getRepository(Instance).extend({
|
export const InstanceRepository = db.getRepository(Instance).extend({
|
||||||
async pack(
|
async pack(
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
): Promise<Packed<'FederationInstance'>> {
|
): Promise<Packed<'FederationInstance'>> {
|
||||||
const meta = await fetchMeta();
|
|
||||||
return {
|
return {
|
||||||
id: instance.id,
|
id: instance.id,
|
||||||
caughtAt: instance.caughtAt.toISOString(),
|
caughtAt: instance.caughtAt.toISOString(),
|
||||||
|
@ -20,7 +19,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,
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { LdSignature } from '@/remote/activitypub/misc/ld-signature.js';
|
||||||
import { getAuthUser } from '@/remote/activitypub/misc/auth-user.js';
|
import { getAuthUser } from '@/remote/activitypub/misc/auth-user.js';
|
||||||
import { StatusError } from '@/misc/fetch.js';
|
import { StatusError } from '@/misc/fetch.js';
|
||||||
import { InboxJobData } from '@/queue/types.js';
|
import { InboxJobData } from '@/queue/types.js';
|
||||||
import { shouldBlockInstance } from '@/misc/skipped-instances.js';
|
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||||
|
|
||||||
const logger = new Logger('inbox');
|
const logger = new Logger('inbox');
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import post from '@/services/note/create.js';
|
import post from '@/services/note/create.js';
|
||||||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
import { CacheableRemoteUser } from '@/models/entities/user.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 { StatusError } from '@/misc/fetch.js';
|
import { StatusError } from '@/misc/fetch.js';
|
||||||
import { Notes } from '@/models/index.js';
|
import { Notes } from '@/models/index.js';
|
||||||
|
@ -10,6 +9,7 @@ import { apLogger } from '@/remote/activitypub/logger.js';
|
||||||
import { fetchNote, resolveNote } from '@/remote/activitypub/models/note.js';
|
import { fetchNote, resolveNote } from '@/remote/activitypub/models/note.js';
|
||||||
import Resolver from '@/remote/activitypub/resolver.js';
|
import Resolver from '@/remote/activitypub/resolver.js';
|
||||||
import { IAnnounce, getApId } from '@/remote/activitypub/type.js';
|
import { IAnnounce, getApId } from '@/remote/activitypub/type.js';
|
||||||
|
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||||
|
|
||||||
export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
|
export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
@ -19,8 +19,7 @@ export default async function(resolver: Resolver, actor: CacheableRemoteUser, ac
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel if the announced from host is blocked.
|
// Cancel if the announced from host is blocked.
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,11 @@ import { Emojis, Polls, MessagingMessages } from '@/models/index.js';
|
||||||
import { Note } from '@/models/entities/note.js';
|
import { Note } from '@/models/entities/note.js';
|
||||||
import { Emoji } from '@/models/entities/emoji.js';
|
import { Emoji } from '@/models/entities/emoji.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
|
||||||
import { getApLock } from '@/misc/app-lock.js';
|
import { getApLock } from '@/misc/app-lock.js';
|
||||||
import { createMessage } from '@/services/messages/create.js';
|
import { createMessage } from '@/services/messages/create.js';
|
||||||
import { StatusError } from '@/misc/fetch.js';
|
import { StatusError } from '@/misc/fetch.js';
|
||||||
import { fromHtml } from '@/mfm/from-html.js';
|
import { fromHtml } from '@/mfm/from-html.js';
|
||||||
|
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||||
import { parseAudience } from '../audience.js';
|
import { parseAudience } from '../audience.js';
|
||||||
import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type.js';
|
import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type.js';
|
||||||
import DbResolver from '../db-resolver.js';
|
import DbResolver from '../db-resolver.js';
|
||||||
|
@ -28,7 +28,7 @@ import { extractApHashtags } from './tag.js';
|
||||||
import { extractPollFromQuestion } from './question.js';
|
import { extractPollFromQuestion } from './question.js';
|
||||||
import { extractApMentions } from './mention.js';
|
import { extractApMentions } from './mention.js';
|
||||||
|
|
||||||
export function validateNote(object: any, uri: string) {
|
export function validateNote(object: any, uri: string): Error | null {
|
||||||
const expectHost = extractDbHost(uri);
|
const expectHost = extractDbHost(uri);
|
||||||
|
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
|
@ -270,14 +270,13 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
|
||||||
const uri = typeof value === 'string' ? value : value.id;
|
const uri = typeof value === 'string' ? value : value.id;
|
||||||
if (uri == null) throw new Error('missing uri');
|
if (uri == null) throw new Error('missing uri');
|
||||||
|
|
||||||
// ブロックしてたら中断
|
// Interrupt if 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);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region If already registered on this server, return it.
|
||||||
const exist = await fetchNote(uri);
|
const exist = await fetchNote(uri);
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
|
|
|
@ -11,7 +11,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/skipped-instances.js';
|
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||||
import { signedGet } from './request.js';
|
import { signedGet } from './request.js';
|
||||||
import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
|
import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
|
||||||
import { parseUri } from './db-resolver.js';
|
import { parseUri } from './db-resolver.js';
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { CacheableLocalUser, User } from '@/models/entities/user.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/skipped-instances.js';
|
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue