Skip to content

feat(proxy): route outbound requests through single authenticated http_proxy#12724

Merged
mekarpeles merged 2 commits into
masterfrom
12715/per-service-proxy-params
Jun 27, 2026
Merged

feat(proxy): route outbound requests through single authenticated http_proxy#12724
mekarpeles merged 2 commits into
masterfrom
12715/per-service-proxy-params

Conversation

@mekarpeles

@mekarpeles mekarpeles commented May 12, 2026

Copy link
Copy Markdown
Member

Summary

Routes all outbound service calls (Amazon Creators API, reCAPTCHA) through a single http_proxy URL with embedded credentials — the model ops now supports.

Before: per-service http_proxies.{amazon,recaptcha} entries with separate url, user, password keys and manual URL construction in get_proxy_params().

After: flat http_proxy: http://user:pass@proxy:8080 in config, consumed by setup_requests() (sets HTTP_PROXY/HTTPS_PROXY env vars) and passed directly to the Amazon API client.

Changes

  • openlibrary/core/vendors.pyAmazonCreatorsAPI takes proxy_url: str = "" and passes it to the mekarpeles/python-amazon-paapi@proxy-support fork's proxy= kwarg (covers both urllib3 REST leg and OAuth2 token refresh via requests)
  • scripts/affiliate_server.pyload_config() reads flat config.get("http_proxy") instead of per-service http_proxies.amazon block
  • openlibrary/plugins/recaptcha/recaptcha.py — drops explicit proxies=get_proxy_params("recaptcha"); relies on env vars set by setup_requests() which requests honours automatically
  • openlibrary/plugins/upstream/utils.py — removes get_proxy_params() entirely
  • conf/openlibrary.yml — documents single-URL proxy format
  • requirements.txt — pins to mekarpeles/python-amazon-paapi@proxy-support fork

Testing

docker compose run --no-deps --rm home python -m pytest \
  openlibrary/plugins/upstream/tests/test_utils.py \
  openlibrary/plugins/recaptcha/ -x -v
# 18 passed

Pre-commit clean (mypy/generate-pot skip locally as expected).

Related

Closes #12715
See also: sergioteula/python-amazon-paapi#150

Copilot AI review requested due to automatic review settings May 12, 2026 04:55
@github-actions github-actions Bot added the Priority: 0 Fix now: Issue prevents users from using the site or active data corruption. [managed] label May 12, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-service HTTP proxy configuration to support authenticated squid ACLs (fixing #12715) while keeping existing global http_proxy behavior as the default fallback.

Changes:

  • Introduces get_proxy_params(service_tag) to build a requests-compatible proxies dict from http_proxies.<service> config.
  • Routes reCAPTCHA verification through the per-service proxy configuration when present.
  • Updates the affiliate server to prefer http_proxies.amazon while retaining backward compatibility with legacy proxy config keys.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
scripts/affiliate_server.py Prefer http_proxies.amazon for Amazon proxy settings, with fallback to legacy flat keys.
openlibrary/plugins/upstream/utils.py Add get_proxy_params() helper for service-specific proxy (including auth).
openlibrary/plugins/recaptcha/recaptcha.py Pass proxies=get_proxy_params("recaptcha") to the verification request.
conf/openlibrary.yml Document the new http_proxies config shape (commented examples).

Comment thread openlibrary/plugins/upstream/utils.py Outdated
Comment thread openlibrary/plugins/upstream/utils.py Outdated
@mekarpeles
mekarpeles force-pushed the 12715/per-service-proxy-params branch from bfc7d8c to 940f8f8 Compare May 12, 2026 05:03
Comment thread scripts/affiliate_server.py Outdated
@mekarpeles
mekarpeles force-pushed the 12715/per-service-proxy-params branch from 93e1643 to 7822fec Compare May 12, 2026 15:39
@mekarpeles

Copy link
Copy Markdown
Member Author

Investigation: Amazon 403 on worker restart

Root cause identified. The creatorsapi_python_sdk's OAuth2TokenManager.refresh_token() (in creatorsapi_python_sdk/auth/oauth2_token_manager.py) uses bare requests.post() to fetch OAuth tokens — no proxy configuration of its own, just whatever HTTP_PROXY/HTTPS_PROXY env vars are set.

The flow:

  1. load_config() creates AmazonCreatorsAPI(proxy_url=..., proxy_creds=...), which injects a custom RESTClientObject with proxy+auth into self.api._api_client.rest_client — this covers API calls only.
  2. setup_requests() sets HTTP_PROXY/HTTPS_PROXY to the bare proxy URL (no auth).
  3. OAuth2TokenManager caches the access token in-process memory. On every worker restart, the cache is cold and refresh_token() is called.
  4. refresh_token() does requests.post("https://api.amazon.com/auth/o2/token", ...) — this goes through the bare env-var proxy, which has no auth → 403 under the new squid ACL policy.

Why it worked initially: The token was valid in-process during the session. The failure only surfaces when the token expires (~1hr) or, more visibly, on every worker restart (cold cache → immediate refresh attempt).

Our PR does not fix this. Our change is backward-compatible in production (falls back to config.get("http_proxy") since http_proxies.amazon won't be set yet), but neither the old nor the new code routes the OAuth token fetch through an authenticated proxy.

Fix options (not in this PR — needs a follow-up):

  1. Add token endpoints to no_proxy_addresses (simplest — config-only, no code change):

    no_proxy_addresses:
      - api.amazon.com          # LWA v3.1 token endpoint
      - api.amazon.co.uk        # LWA v3.2
      - creatorsapi.auth.us-east-1.amazoncognito.com  # Cognito v2.1

    This lets the token fetch go direct, bypassing the squid proxy entirely.

  2. Set the auth-bearing proxy in env vars — change setup_requests() to use the authenticated URL for HTTP_PROXY/HTTPS_PROXY. This makes all requests calls (including OAuth refresh) go through the authenticated proxy. Riskier: affects everything globally.

  3. Patch OAuth2TokenManager in our code to inject a proxy-aware requests.Session — surgical but couples us to the SDK's internals.

Recommended: Option 1 (add to no_proxy_addresses in olsystem config) as an immediate ops fix, independent of this PR. This PR can still merge — it doesn't worsen the situation.

@jimchamp jimchamp added the Needs: Special Deploy This PR will need a non-standard deploy to production label May 18, 2026

@jimchamp jimchamp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain that the is_lwa method exists on OAuth2Config objects. Other than that, this looks okay to me.

Configuring the recaptcha URL has caused a merge conflict on this branch.

Comment thread openlibrary/core/vendors.py Outdated
@jimchamp jimchamp added the Needs: Submitter Input Waiting on input from the creator of the issue/pr [managed] label May 20, 2026
@mekarpeles
mekarpeles force-pushed the 12715/per-service-proxy-params branch from 9939df3 to ed7d782 Compare May 20, 2026 23:07
@github-actions github-actions Bot removed the Needs: Submitter Input Waiting on input from the creator of the issue/pr [managed] label May 20, 2026
Comment thread conf/openlibrary.yml Outdated
@mekarpeles

Copy link
Copy Markdown
Member Author

Hi — I'm Pierre, an AI-assisted PR health agent running on behalf of @mekarpeles.

Folded 1 bot commit: The [pre-commit.ci] auto fixes commit (70282ac) was folded into its parent commit fix(proxy): route Amazon OAuth token refresh through authenticated proxy.

The single unresolved Copilot thread (conf/openlibrary.yml: http_proxies url field) was a false positive — the url field is intentional to allow per-service proxy URL overrides. Replied and resolved.

Note: This PR still has a merge conflict (CONFLICTING). The conflict is in semantic code (not mechanical), so it's been left for @mekarpeles to resolve manually.

Safeguards: Confirmed all non-bot commits authored by @mekarpeles. Used git push --force-with-lease.

To reverse: git reset --hard 70282ac then git push --force-with-lease.

@mekarpeles

Copy link
Copy Markdown
Member Author

Pushed an update (f0a2aff) that simplifies the proxy implementation significantly.

Root cause that was fixed upstream

urllib3.ProxyManager ignores credentials embedded in the proxy URL for HTTPS CONNECT tunneling — they must be passed via proxy_headers=make_headers(proxy_basic_auth=...) instead. The previous vendors.py worked around this with ~100 lines of post-construction injection. The fix is now in the SDK itself: mekarpeles/python-amazon-paapi@proxy-support, which has also been submitted upstream as sergioteula/python-amazon-paapi#150.

What changed in this commit

  • requirements.txt: pins to the fork branch (git+https://github.com/mekarpeles/python-amazon-paapi@proxy-support) instead of the upstream ==6.2.0 release which has no proxy support
  • vendors.py: deleted _ProxyAwareTokenManager subclass, manual RESTClientObject replacement, and OAuth2Config assembly (114 lines removed); replaced with a 21-line block that builds an authenticated proxy URL and passes proxy= directly to AmazonCreatorsApi()

Tested on ol-home0

End-to-end test via amz.py --from-config /olsystem/etc/openlibrary.yml returned 1 item for ASIN 0316769177 through the squid proxy — both the urllib3 HTTPS CONNECT leg and the OAuth2 token refresh succeeded.

@mekarpeles
mekarpeles force-pushed the 12715/per-service-proxy-params branch 2 times, most recently from c92958b to 19a390a Compare June 27, 2026 02:17
@mekarpeles mekarpeles changed the title feat(proxy): add per-service proxy auth via get_proxy_params() feat(proxy): route outbound requests through single authenticated http_proxy Jun 27, 2026
@mekarpeles
mekarpeles force-pushed the 12715/per-service-proxy-params branch 3 times, most recently from 537729c to fdf21f1 Compare June 27, 2026 04:10
…p_proxy

Replaces the per-service http_proxies config (with separate url/user/password
per service) with a single http_proxy URL containing embedded credentials
(e.g. http://user:pass@proxy.example.com:3128).

- vendors.py: AmazonCreatorsAPI passes proxy URL directly to the fork's
  python-amazon-paapi proxy= kwarg; removes credential-building logic
- affiliate_server.py: load_config() reads http_proxy directly
- utils.py: removes get_proxy_params() — proxy now handled via env vars
  set by setup_requests(), which requests picks up automatically
- recaptcha.py: removes explicit proxies= kwarg; relies on setup_requests()
- test_utils.py: removes TestGetProxyParams tests
- conf/openlibrary.yml: documents the new single-URL proxy config format
- requirements.txt: pins python-amazon-paapi to mekarpeles/proxy-support fork
@mekarpeles
mekarpeles force-pushed the 12715/per-service-proxy-params branch from fdf21f1 to b148937 Compare June 27, 2026 04:16
@mekarpeles
mekarpeles merged commit 3ee6fa3 into master Jun 27, 2026
8 checks passed
@mekarpeles
mekarpeles deleted the 12715/per-service-proxy-params branch June 27, 2026 04:26
mekarpeles added a commit that referenced this pull request Jun 30, 2026
…egression

fix(proxy): restore 5 items dropped by bad rebase in #12724
pull Bot pushed a commit to Spencerx/openlibrary that referenced this pull request Jun 30, 2026
…12724

The rebase on internetarchive#12724 (single http_proxy consolidation) incorrectly
dropped changes from master that the proxy refactor had no relation to:

- conf/openlibrary.yml: restore otp_seed dev seed (needed by OTP auth)
- conf/openlibrary.yml: restore sentry.frontend DSN block (Sentry JS)
- conf/openlibrary.yml: restore profiles_sample_rate: 0.05 (was 0.001)
- conf/openlibrary.yml: restore author_exclusions: [] (404 exclusions)
- requirements.txt: restore cryptography==44.0.2 (Fernet cookie encrypt)

The cryptography omission is the most critical: accounts/model.py
imports Fernet at runtime for S3 key cookie encryption, so its absence
causes ModuleNotFoundError on any code path touching S3 auth cookies.

Verified:
  docker compose run --rm home python -m pytest \
    openlibrary/tests/core/test_processors.py -x -v
  # 9 passed

  docker compose run --rm home python -c \
    "from cryptography.fernet import Fernet; print('OK')"
  # cryptography OK

Closes internetarchive#12724 regression
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs: Special Deploy This PR will need a non-standard deploy to production Priority: 0 Fix now: Issue prevents users from using the site or active data corruption. [managed]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setting global http_proxy via os now preventing domain-specific urls that require auth

3 participants