Skip to content

Generate a random PROXY_API_KEY by default and mask it in startup/UI output #171

Description

@chenshj73

Hi, I noticed two related proxy-key handling issues while reviewing the startup and setup paths.

In the current main branch (b3fecbf), PROXY_API_KEY protects both OpenAI-compatible and Anthropic-compatible endpoints, but the setup tool and reset flow can set it to a fixed public value.

The endpoint checks depend directly on PROXY_API_KEY in src/proxy_app/main.py:

# lines 666-672
async def verify_api_key(auth: str = Depends(api_key_header)):
    if not PROXY_API_KEY:
        return auth
    if not auth or auth != f"Bearer {PROXY_API_KEY}":
        raise HTTPException(status_code=401, detail="Invalid or missing API Key")
    return auth

# lines 680-694
async def verify_anthropic_api_key(...):
    if x_api_key and x_api_key == PROXY_API_KEY:
        return x_api_key
    if auth and auth == f"Bearer {PROXY_API_KEY}":
        return auth
    raise HTTPException(status_code=401, detail="Invalid or missing API Key")

However, first-time setup writes a hard-coded default in src/rotator_library/credential_tool.py:

# lines 1095-1111
if get_key(str(_get_env_file()), "PROXY_API_KEY") is None:
    default_key = "VerysecretKey"
    set_key(str(_get_env_file()), "PROXY_API_KEY", default_key)

The launcher reset flow also resets to the same fixed value in src/proxy_app/launcher_tui.py:

# lines 728-785
default_api_key = "VerysecretKey"
...
LauncherConfig.update_proxy_api_key(default_api_key)
...
self.console.print(f"   Proxy API Key:      {default_api_key}")

The current startup banner also prints the full proxy key in src/proxy_app/main.py:

# lines 87-97
proxy_api_key = os.getenv("PROXY_API_KEY")
if proxy_api_key:
    key_display = f"✓ {proxy_api_key}"
...
print(f"Proxy API Key: {key_display}")

The same header is printed again after clearing the screen:

# lines 248-252
print(f"Starting proxy on {args.host}:{args.port}")
print(f"Proxy API Key: {key_display}")

And the launcher status screen prints the full value in src/proxy_app/launcher_tui.py:

# lines 454-457
proxy_key = os.getenv("PROXY_API_KEY")
if proxy_key:
    self.console.print(f"   Proxy API Key:       {proxy_key}")

Minimal reproduction:

  1. Run first-time setup without an existing PROXY_API_KEY, or use the launcher reset-to-defaults flow.
  2. Observe that .env receives PROXY_API_KEY=VerysecretKey.
  3. Start the proxy and observe that the full proxy key is printed in startup output.
  4. Any client can authenticate with Authorization: Bearer VerysecretKey unless the user manually changes it.

Why this matters:

  • A fixed default key means fresh installs or reset installs can share the same bearer credential.
  • If the proxy is bound beyond localhost, deployed behind a tunnel/reverse proxy, or hosted on a platform, anyone who knows the default can call the relay.
  • The full key in startup output/UI can leak through Docker logs, Render logs, terminal recordings, screenshots, or support bundles.
  • This is especially sensitive for an LLM relay because the proxy key gates access to all configured provider credentials behind the service.

Suggested fix:

  • Generate a random first-boot/default key, for example with secrets.token_urlsafe(32).
  • Do not reset to a hard-coded shared key.
  • Display only a masked value such as sk-...abcd, or show "Set" without revealing the secret.
  • Optionally print the full generated key only once during setup, with an explicit warning that it will not be shown again.
  • Consider masking both startup and launcher status output consistently.

Thanks for maintaining the project.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions