Skip to content

[Bug]: Ollama 403 in proxy mode - wrong signature validation #158

@druppelt

Description

@druppelt

Bug Description

Using this with ollama in local mode works as intended. In proxy mode there is a bug in middleware.ts that removes the first 7 characters of the authorization header before using it in verifySignature. That is probably intended to remove a leading "Bearer ", but that is not present in the actual header.
I propose to replace authorization.substring(7) with authorization.startsWith("Bearer ") ? authorization.substring(7) : authorization.

In context:

if (request.nextUrl.pathname.startsWith("/api/ai/ollama")) {
    const authorization = request.headers.get("authorization") || "";
    const isDisabledModel = await hasDisabledAIModel();
    if (
      !verifySignature(
        authorization.startsWith("Bearer ") ? authorization.substring(7) : authorization,
        accessPassword,
        Date.now()
      ) ||
      disabledAIProviders.includes("ollama") ||
      isDisabledModel
    ) {
      return NextResponse.json(
        { error: ERRORS.NO_PERMISSIONS },
        { status: 403 }
      );
    } else {
      const requestHeaders = new Headers();
      requestHeaders.set(
        "Content-Type",
        request.headers.get("Content-Type") || "application/json"
      );
      return NextResponse.next({
        request: {
          headers: requestHeaders,
        },
      });
    }
  }

This should probably done for all occurences of authorization.substring(7)

While searching for this I also found that verifySignature could cause issues. The timestamp portion of the signature is shortened to get a time window that is valid:

const data = `${key}::${timestamp.toString().substring(0, 8)}`;

The Window is massive yet shifting: By taking the first 8 digits of a millisecond timestamp, the code is effectively grouping time into chunks of 100,000 milliseconds (100 seconds).
If the frontend generated the signature at 1776000099999 and the backend checked it at 1776000100001 (just 2 milliseconds later), the substring changes from 17760000 to 17760001. The hash will be completely different.
An easy fix would be to also generate the signature for the previous time window and check against both.

export function verifySignature(
  signature = "",
  key: string,
  timestamp: number
): boolean {
  const generatedSignature = generateSignature(key, timestamp);
  // Check the current 100s window and the previous 100s window (offset by 10^5 ms)
  const generatedPreviousSignature = generateSignature(key, timestamp - 100000);
  return signature === generatedSignature || signature === generatedPreviousSignature;
}

Steps to Reproduce

Run the app with these env vars properly set:
ACCESS_PASSWORD
OLLAMA_API_BASE_URL

In the settings of the UI, set API mode to proxy and set the access password, save.
Enter a research topic and click "Start Thinking"

Expected Behavior

Ollama responds / the http request to /api/ai/ollama/api/chat returns with 200.

Screenshots

No response

Deployment Method

  • Docker
  • Vercel
  • Server

Desktop OS

Windows 10

Desktop Browser

Firefox

Desktop Browser Version

149.0.2 (64-Bit)

Smartphone Device

No response

Smartphone OS

No response

Smartphone Browser

No response

Smartphone Browser Version

No response

Additional Logs

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions