Skip to content

feat(cells): Add stub for synapse endpoint#105975

Merged
lynnagara merged 15 commits intomasterfrom
synapse-endpoint-stub
Jan 15, 2026
Merged

feat(cells): Add stub for synapse endpoint#105975
lynnagara merged 15 commits intomasterfrom
synapse-endpoint-stub

Conversation

@lynnagara
Copy link
Member

@lynnagara lynnagara commented Jan 8, 2026

to be based on #106231

@lynnagara lynnagara requested a review from markstory January 8, 2026 23:28
@lynnagara lynnagara requested review from a team as code owners January 8, 2026 23:28
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jan 8, 2026
if not settings.SYNAPSE_AUTH_SECRET:
return False

return request.META.get("HTTP_X_SYNAPSE_AUTH") == settings.SYNAPSE_AUTH_SECRET
Copy link
Member Author

Choose a reason for hiding this comment

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

is this an acceptable way to lock down this endpoint? i saw similar approaches used elsewhere in this codebase

Copy link
Member

Choose a reason for hiding this comment

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

Most of our other service-to-service requests are using payload/request hmac signatures. We should follow that pattern here so we can consolidate to a single implementation in the future.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated

@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Jan 8, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

🚨 Warning: This pull request contains Frontend and Backend changes!

It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently.

Have questions? Please ask in the #discuss-dev-infra channel.

if not settings.SYNAPSE_AUTH_SECRET:
return False

return request.META.get("HTTP_X_SYNAPSE_AUTH") == settings.SYNAPSE_AUTH_SECRET
Copy link
Member

Choose a reason for hiding this comment

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

Most of our other service-to-service requests are using payload/request hmac signatures. We should follow that pattern here so we can consolidate to a single implementation in the future.

),
# Cell routing endpoints
re_path(
r"^org-cell-mappings/$",
Copy link
Member

Choose a reason for hiding this comment

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

Should this be under the /internal path like other internal API endpoints?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea, moved to the internal list

Comment on lines 14 to 41
class SynapseAuthPermission(BasePermission):
"""
Validates HMAC signature for Synapse requests.

Expects the X-Synapse-Signature header to contain the HMAC-SHA256 hex digest
of the request body, signed with SYNAPSE_AUTH_SECRET.
"""

def has_permission(self, request: Request, view: object) -> bool:
if settings.IS_DEV:
return True

if not settings.SYNAPSE_HMAC_SECRET:
return False

signature = request.META.get("HTTP_X_SYNAPSE_SIGNATURE")
if not signature:
return False

body_bytes = request.body if request.body not in (None, b"") else b""

computed_hmac = hmac.new(
settings.SYNAPSE_HMAC_SECRET.encode("utf-8"),
body_bytes,
hashlib.sha256,
).hexdigest()

return hmac.compare_digest(signature, computed_hmac)
Copy link
Member

Choose a reason for hiding this comment

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

Typically we implement this logic in an authentication adapter. We should be able to use the existing generic signature adapter:

class ServiceRpcSignatureAuthentication(StandardAuthentication):
"""
Generic authentication for service RPC requests.
Requests are sent with an HMAC signed by a shared private key.
Subclasses should define:
- shared_secret_setting_name: str - name of the settings attribute (e.g., "SEER_RPC_SHARED_SECRET")
- service_name: str - name of the service for logging (e.g., "Seer", "Launchpad")
- sdk_tag_name: str - name for the SDK tag (e.g., "seer_rpc_auth", "launchpad_rpc_auth")
"""
token_name = b"rpcsignature"
shared_secret_setting_name: str
service_name: str
sdk_tag_name: str
def accepts_auth(self, auth: list[bytes]) -> bool:
if not auth or len(auth) < 2:
return False
return auth[0].lower() == self.token_name
def authenticate_token(self, request: Request, token: str) -> tuple[Any, Any]:
shared_secret_setting = getattr(settings, self.shared_secret_setting_name, None)
if shared_secret_setting is None:
raise RpcAuthenticationSetupException(
f"Cannot validate {self.service_name} RPC request signatures without shared secret"
)
if not compare_service_signature(
request.path_info, request.body, token, shared_secret_setting, self.service_name
):
raise AuthenticationFailed("Invalid signature")
sentry_sdk.get_isolation_scope().set_tag(self.sdk_tag_name, True)
return (AnonymousUser(), token)

Comment on lines 11 to 21
class OrgCellMappingsEndpoint(Endpoint):
"""
Returns the organization-to-cell mappings for all orgs in pages.
Only accessible by the Synapse internal service via X-Synapse-Auth header.
"""

owner = ApiOwner.INFRA_ENG
publish_status = {
"GET": ApiPublishStatus.PRIVATE,
}
permission_classes = (SynapseAuthPermission,)

This comment was marked as outdated.

CONDUIT_PUBLISH_JWT_ISSUER: str = os.getenv("CONDUIT_PUBLISH_JWT_ISSUER", "sentry.io")
CONDUIT_PUBLISH_JWT_AUDIENCE: str = os.getenv("CONDUIT_PUBLISH_JWT_AUDIENCE", "conduit")

SYNAPSE_HMAC_SECRET: str | None = os.getenv("SYNAPSE_HMAC_SECRET")

This comment was marked as outdated.

Copy link
Member

Choose a reason for hiding this comment

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

I added a linear task so we don't forget to add this secret in.

lynnagara added a commit to getsentry/synapse that referenced this pull request Jan 13, 2026
lynnagara added a commit to getsentry/synapse that referenced this pull request Jan 15, 2026
@lynnagara lynnagara merged commit 675566e into master Jan 15, 2026
69 checks passed
@lynnagara lynnagara deleted the synapse-endpoint-stub branch January 15, 2026 20:48
@github-actions github-actions bot locked and limited conversation to collaborators Jan 31, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments