Unverified Commit e48f97c8 authored by Hong Minhee's avatar Hong Minhee
Browse files

CreateFederationOptions.allowPrivateAddress option

parent 8a6643c2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ To be released.
 -  Removed `expand` option of `Object.toJsonLd()` method, which was deprecated
    in version 0.14.0.  Use `format: "expand"` option instead.
 -  Added `Context.lookupObject()` method.
 -  Added `allowPrivateAddress` option to `CreateFederationOptions` interface.
 -  Renamed the short option `-c` for `--compact` of `fedify lookup` command to
    `-C` to avoid conflict with the short option `-c` for `--cache-dir`.
 -  Added `-r`/`--raw` option to `fedify lookup` command to output the raw JSON
+21 −0
Original line number Diff line number Diff line
@@ -181,6 +181,27 @@ load remote JSON-LD contexts. The type of the function is the same as the
[*Document loader vs. context loader*
section](./context.md#document-loader-vs-context-loader)).

### `allowPrivateAddress`

*This API is available since Fedify 0.15.0.*

> [!WARNING]
> Do not turn on this option in production environments.  Disallowing fetching
> private network addresses is a security feature to prevent [SSRF] attacks.

Whether to allow fetching private network addresses in the document loader.

If turned on, [`documentLoader`](#documentloader),
[`contextLoader`](#contextloader),
and [`authenticatedDocumentLoaderFactory`](#authenticateddocumentloaderfactory)
cannot be configured.

Mostly useful for testing purposes.

Turned off by default.

[SSRF]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery

### `outboxRetryPolicy`

*This API is available since Fedify 0.12.0.*
+35 −0
Original line number Diff line number Diff line
@@ -111,3 +111,38 @@ Fedify provides a [CLI toolchain](../cli.md) for testing and debugging.
The [`fedify inbox` command](../cli.md#fedify-inbox-ephemeral-inbox-server) is
a simple tool for spinning up an ephemeral inbox server that receives and
displays incoming ActivityPub messages.


Allowing fetching private network addresses
-------------------------------------------

*This API is available since Fedify 0.15.0.*

By default, Fedify disallows fetching private network addresses
(e.g., localhost) in order to prevent [SSRF] attacks.  However, in some cases,
you may want to allow fetching private network addresses for testing purposes
(e.g., end-to-end testing).  In this case, you can set
the [`allowPrivateAddress`](./federation.md#allowprivateaddress) option to
`true` in the `createFederation()` function:

~~~~ typescript
const federation = createFederation({
  // ... other options
  allowPrivateAddress: true,
});
~~~~

> [!NOTE]
> By turning on the `allowPrivateAddress` option, you cannot configure other
> options related to document loaders including
> [`documentLoader`](./federation.md#documentloader),
> [`contextLoader`](./federation.md#contextloader), and
> [`authenticatedDocumentLoaderFactory`](./federation.md#authenticateddocumentloaderfactory)

> [!WARNING]
> Be careful when you allow fetching private network addresses.  It may cause
> security vulnerabilities such as [SSRF].  Make sure to turn off the option
> when you finish testing, or conditionally turn it on only in the testing
> environment.

[SSRF]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
+22 −0
Original line number Diff line number Diff line
@@ -30,6 +30,28 @@ import { MemoryKvStore } from "./kv.ts";
import { createFederation } from "./middleware.ts";
import { RouterError } from "./router.ts";

test("createFederation()", () => {
  const kv = new MemoryKvStore();
  assertThrows(() =>
    createFederation<number>({
      kv,
      documentLoader: mockDocumentLoader,
      allowPrivateAddress: true,
    }), TypeError);
  assertThrows(() =>
    createFederation<number>({
      kv,
      contextLoader: mockDocumentLoader,
      allowPrivateAddress: true,
    }), TypeError);
  assertThrows(() =>
    createFederation<number>({
      kv,
      authenticatedDocumentLoaderFactory: () => mockDocumentLoader,
      allowPrivateAddress: true,
    }), TypeError);
});

test("Federation.createContext()", async (t) => {
  const kv = new MemoryKvStore();
  const documentLoader = (url: string) => {
+36 −2
Original line number Diff line number Diff line
@@ -121,6 +121,20 @@ export interface CreateFederationOptions {
   */
  authenticatedDocumentLoaderFactory?: AuthenticatedDocumentLoaderFactory;

  /**
   * Whether to allow fetching private network addresses in the document loader.
   *
   * If turned on, {@link CreateFederationOptions.documentLoader},
   * {@link CreateFederationOptions.contextLoader}, and
   * {@link CreateFederationOptions.authenticatedDocumentLoaderFactory}
   * cannot be configured.
   *
   * Mostly useful for testing purposes.  *Do not use in production.*
   *
   * Turned off by default.
   */
  allowPrivateAddress?: boolean;

  /**
   * A callback that handles errors during outbox processing.  Note that this
   * callback can be called multiple times for the same activity, because
@@ -742,15 +756,35 @@ class FederationImpl<TContextData> implements Federation<TContextData> {
    this.router.add("/.well-known/nodeinfo", "nodeInfoJrd");
    this.objectCallbacks = {};
    this.objectTypeIds = {};
    if (options.allowPrivateAddress) {
      if (options.documentLoader != null) {
        throw new TypeError(
          "Cannot set documentLoader with allowPrivateAddress turned on.",
        );
      } else if (options.contextLoader != null) {
        throw new TypeError(
          "Cannot set contextLoader with allowPrivateAddress turned on.",
        );
      } else if (options.authenticatedDocumentLoaderFactory != null) {
        throw new TypeError(
          "Cannot set authenticatedDocumentLoaderFactory with " +
            "allowPrivateAddress turned on.",
        );
      }
    }
    this.documentLoader = options.documentLoader ?? kvCache({
      loader: fetchDocumentLoader,
      loader: options.allowPrivateAddress
        ? (url) => fetchDocumentLoader(url, true)
        : fetchDocumentLoader,
      kv: options.kv,
      prefix: this.kvPrefixes.remoteDocument,
    });
    this.contextLoader = options.contextLoader ?? this.documentLoader;
    this.authenticatedDocumentLoaderFactory =
      options.authenticatedDocumentLoaderFactory ??
        getAuthenticatedDocumentLoader;
        (options.allowPrivateAddress
          ? (identity) => getAuthenticatedDocumentLoader(identity, true)
          : getAuthenticatedDocumentLoader);
    this.onOutboxError = options.onOutboxError;
    this.signatureTimeWindow = options.signatureTimeWindow ?? { minutes: 1 };
    this.skipSignatureVerification = options.skipSignatureVerification ?? false;