Cloud upstreams dial the Cloud control plane implicitly, and the config keys that control it are undocumented · Issue #178 · temporalio/temporal-proxy · GitHub
Skip to content

Cloud upstreams dial the Cloud control plane implicitly, and the config keys that control it are undocumented #178

Open

Description

@thebharathkumar

Version: v0.7.0

I hit this setting the proxy up against a Temporal Cloud namespace from a network with an egress allowlist. The behavior itself seems reasonable, but I think the documentation gap around it is worth closing.

What happens

When an upstream resolves as Temporal Cloud, the proxy installs a method-translation layer that answers WorkflowService/ListNamespaces by dialling saas-api.tmprl.cloud:443 and calling CloudService/GetNamespaces there, reusing the upstream's API key.

internal/config/upstream.go turns this on if any of three things is true, two of which are inferred rather than declared:

if u.Cloud || cloud.IsEndpoint(u.Listen.HostPort) {
    return true
}
return u.Listen.TLS != nil && cloud.IsEndpoint(u.Listen.TLS.ServerName)

where cloud.IsEndpoint is a strings.HasSuffix(host, ".tmprl.cloud"). examples/listnamespace/config.yaml demonstrates exactly this: no cloud flag, apiTranslations commented out entirely, and translation happens anyway.

Why it is hard to find out

  • The README never mentions method translation, CloudService, apiTranslations, or the upstreams[].cloud flag. The feature list stops at routing, allowlist, namespace translation, TLS, encryption, key management, auth, codec transparency and deployment.
  • "Get started" links examples/cloud, examples/kms and examples/authz, but not examples/listnamespace, which is the only place this is shown.
  • The configure page on docs.temporal.io lists eight top-level config keys and none of apiTranslations, cloudAPI, or the upstream cloud flag.
  • cloudAPIConn does log "translating methods to the Cloud API" at startup, but it tags the methods, not the endpoint, so the log never reveals that saas-api.tmprl.cloud:443 is now an egress dependency.

The sentence that made me file this

The README says:

Service allowlist. Forward only the gRPC services you name, defaulting to WorkflowService and OperatorService. Server reflection is opt-in, and a service you leave out is never forwarded.

CloudService is not in the default allowlist and cannot be named by an operator, yet on a Cloud upstream the proxy speaks it outbound to a host the operator never configured. The allowlist being inbound-only is fine, but that sentence reads as though it is not.

Why it bites

  1. Behind an egress allowlist scoped to the namespace frontend, ListNamespaces fails opaquely. The Temporal UI calls it on load, so the symptom is a broken UI rather than an obvious network error.
  2. A security review told "the proxy talks to our Cloud namespace" is not being told it also talks to the control plane with the same key.
  3. internal/cloud/endpoint.go explicitly calls out private-link endpoints as the reason cloud: true exists, but an operator who needs that escape hatch cannot discover it from any document.

What I think would fix it

  • Document upstreams[].cloud and apiTranslations.cloudAPI in the README and on the configure page.
  • State the saas-api.tmprl.cloud:443 egress requirement, and that the upstream API key is reused against the control plane.
  • Link examples/listnamespace from "Get started".
  • Include the resolved control-plane host in the existing startup log line, so the egress dependency is visible without reading the source.

Happy to open a PR for the README and the log line if that is useful. The translation subsystem shipped in v0.6.0 eleven days ago, so if docs are already in flight, feel free to close this.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions