Skip to content

feat: implemented dual authentication support for /satellites/sync endpoint#319

Open
mahil-2040 wants to merge 2 commits intocontainer-registry:mainfrom
mahil-2040:feature/satellite-sync-auth
Open

feat: implemented dual authentication support for /satellites/sync endpoint#319
mahil-2040 wants to merge 2 commits intocontainer-registry:mainfrom
mahil-2040:feature/satellite-sync-auth

Conversation

@mahil-2040
Copy link

@mahil-2040 mahil-2040 commented Feb 8, 2026

Description

This PR secures the /satellites/sync endpoint by implementing dual authentication support:

  • SPIFFE/mTLS: Identity extracted from TLS client certificate (primary)
  • Robot Account Basic Auth: Credentials from Zero-Touch Registration (fallback)

Previously, this endpoint was publicly accessible, allowing any client to submit status reports for any satellite name—a security vulnerability.

Changes

Ground Control

File Change
middleware.go Added SatelliteAuthMiddleware with dual auth logic
routes.go Applied middleware to /satellites/sync endpoint
satellite_handlers.go Updated syncHandler to use authenticated context
robot_accounts.sql Added GetRobotAccByName query
cached_images_test.go Updated tests with authenticated context

Satellite

File Change
reporting_process.go Added Basic Auth header using ZTR credentials

Breaking Change

⚠️ Breaking: All requests to /satellites/sync now require authentication.

  • SPIFFE mode: Uses mTLS client certificates
  • Robot Account mode: Uses Basic Auth with ZTR credentials

Existing satellites will continue to work if they have valid robot credentials from ZTR or SPIFFE configured.

Testing

  • All existing tests pass
  • Updated sync handler tests with authenticated context
  • Build verification for both satellite and ground-control

Screenshot

Unauthenticated Request Rejected (401)

The endpoint now correctly rejects unauthenticated requests:

image

A 200 OK response requires a registered satellite with valid robot credentials from ZTR, which can be verified in a full e2e test environment.

Additional context


Summary by cubic

Secured the /satellites/sync endpoint with dual auth (SPIFFE/mTLS or robot Basic Auth) and added rate limiting to prevent abuse. The satellite client now sends Basic Auth when SPIFFE isn’t used.

  • New Features

    • Added SatelliteAuthMiddleware (SPIFFE first, robot Basic Auth fallback) and applied SPIFFE context extraction on satellite routes.
    • Protected and rate-limited /satellites/sync; handler uses authenticated context and rejects name mismatches.
    • Added GetRobotAccByName DB query for robot auth.
    • Satellite sends Basic Auth using ZTR credentials when not on SPIFFE.
    • Updated tests to inject authenticated satellite context.
  • Migration

    • Breaking: /satellites/sync now requires authentication and is rate-limited.
    • SPIFFE: ensure mTLS and SPIFFE IDs are configured.
    • Robot accounts: issue ZTR robot creds; satellites must send Basic Auth.

Written for commit d10dd6f. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Robot account lookup by name.
    • Dual satellite authentication (SPIFFE/mTLS + Robot Account Basic Auth).
    • Conditional Basic Auth for status reports when SPIFFE is not used.
  • Tests

    • Added helpers and tests to exercise satellite authentication context.
  • Chores

    • Updated generated code headers to the latest generator version.

…dpoint

Signed-off-by: Mahil Patel <mahilpatel0808@gmail.com>
@github-actions github-actions bot added the golang label Feb 8, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

📝 Walkthrough

Walkthrough

Adds satellite authentication for POST /satellites/sync (SPIFFE/mTLS and Robot Account Basic Auth), a DB query to lookup robot accounts by name, middleware to validate and attach satellite identity to request context, route changes to apply middleware, handler checks to enforce identity matching, test helper, and conditional Basic Auth in the reporter.

Changes

Cohort / File(s) Summary
DB — generated headers
ground-control/internal/database/artifacts.sql.go, ground-control/internal/database/configs.sql.go, ground-control/internal/database/db.go, ground-control/internal/database/groups.sql.go, ground-control/internal/database/login_attempts.sql.go, ground-control/internal/database/models.go, ground-control/internal/database/satellite_configs.sql.go, ground-control/internal/database/satellite_groups.sql.go, ground-control/internal/database/satellite_status.sql.go, ground-control/internal/database/satellite_token.sql.go, ground-control/internal/database/satellites.sql.go, ground-control/internal/database/sessions.sql.go, ground-control/internal/database/users.sql.go
Updated sqlc generated header comments (v1.28.0 -> v1.30.0); no functional changes.
Robot account query
ground-control/internal/database/robot_accounts.sql.go, ground-control/sql/queries/robot_accounts.sql
Added GetRobotAccByName SQL query and generated method to retrieve robot account by robot_name.
Server middleware & context helpers
ground-control/internal/server/middleware.go
New SatelliteAuthMiddleware implementing SPIFFE/mTLS and Robot Account Basic Auth checks; stores satellite_name and satellite_id in request context; added GetSatelliteNameFromContext and GetSatelliteIDFromContext.
Routing
ground-control/internal/server/routes.go
Applied SPIFFE auth middleware to satellites router; refactored /satellites/sync into subrouter with rate limiting and SatelliteAuthMiddleware.
Handler changes
ground-control/internal/server/satellite_handlers.go
syncHandler now reads authenticated satellite name from context and enforces that request body name (when present) matches the authenticated satellite; returns 401/403 on missing/mismatch.
Reporter auth
internal/state/reporting_process.go
When SPIFFE is disabled, reporter now fetches registry credentials via GetSourceRegistryCredentials and sets Basic Auth on the status POST if both username and password are present.
Tests
ground-control/internal/server/cached_images_test.go
Added withSatelliteAuth test helper to inject satellite name/ID into request context for tests.

Sequence Diagram(s)

sequenceDiagram
    participant Satellite
    participant GC_Router as GroundControl Router
    participant Middleware as SatelliteAuthMiddleware
    participant DB as Database
    participant Handler as syncHandler

    Satellite->>GC_Router: POST /satellites/sync (mTLS or Authorization header)
    GC_Router->>Middleware: Pass request

    alt SPIFFE/mTLS
        Middleware->>Middleware: Extract SPIFFE ID from TLS/JWT
        Middleware->>DB: Lookup satellite by SPIFFE ID
        DB-->>Middleware: Satellite record
    else Robot Basic Auth
        Middleware->>Middleware: Parse Basic Auth (robotName, secret)
        Middleware->>DB: GetRobotAccByName(robotName)
        DB-->>Middleware: RobotAccount
        Middleware->>Middleware: Verify secret hash
    end

    alt Auth success
        Middleware->>Middleware: Attach satellite_name & satellite_id to context
        Middleware->>Handler: Forward request
        Handler->>Handler: GetSatelliteNameFromContext()
        Handler->>Handler: Compare req.Name (if provided) to authenticated name
        alt Names match
            Handler->>DB: Fetch satellite details / process sync
            DB-->>Handler: OK
            Handler->>Satellite: 200 OK
        else Name mismatch
            Handler->>Satellite: 403 Forbidden
        end
    else Auth failure
        Middleware->>Satellite: 401 Unauthorized
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • harbor-satellite#256 — Overlapping changes around SatelliteAuthMiddleware, SPIFFE context handling, and route/middleware application.
  • harbor-satellite#311 — Related edits to internal/state/reporting_process.go for status-report authentication behavior.
  • harbor-satellite#54 — Overlaps on robot accounts DB queries / related types and usage.

Suggested labels

enhancement, component:satellite, component:ground-control, security, auth

Suggested reviewers

  • bupd
  • Vad1mo
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: implementing dual authentication support for the /satellites/sync endpoint, which is the primary objective of this PR.
Description check ✅ Passed The PR description comprehensively covers all required sections: it fixes issue #236, provides detailed description of changes with a clear table format, explains the breaking change, documents testing, and includes relevant context with a screenshot and auto-generated summary.
Linked Issues check ✅ Passed The PR implements all primary objectives from issue #236: dual authentication (SPIFFE/mTLS and Robot Account Basic Auth), middleware with proper validation, handler updates to reject mismatched names, authenticated context usage, database query for robot lookup, and satellite-side credential sending.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing dual authentication for /satellites/sync. The sqlc version updates (v1.28.0 to v1.30.0) across multiple generated files are necessary supporting changes, and rate limiting is a complementary security measure mentioned in commit messages.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
ground-control/internal/server/routes.go (1)

88-92: Middleware ordering consideration: rate limiting runs before authentication.

Current order means unauthenticated flood traffic from a given IP will exhaust the rate-limit budget, potentially blocking legitimate satellites behind the same IP/NAT. The flip side (auth-first) would expose the auth layer to unbounded traffic. The current choice is the safer default, but worth being aware of if satellites share egress IPs.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link

codacy-production bot commented Feb 8, 2026

Codacy's Analysis Summary

2 new issues (≤ 0 issue)
0 new security issue
16 complexity
2 duplications

Review Pull Request in Codacy →

AI Reviewer available: add the codacy-review label to get contextual insights without leaving GitHub.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@ground-control/internal/server/middleware.go`:
- Around line 117-138: The sync endpoint is missing rate limiting which allows
repeated Basic Auth attempts that trigger crypto.VerifySecret and DB lookups via
GetRobotAccByName; update the router setup so the syncRouter (in routes.go where
/sync is mounted) applies the existing RateLimitMiddleware (the same middleware
used for /ztr and /spiffe-ztr) so requests to the sync subrouter are
rate-limited, preventing brute-force CPU/DB exhaustion—ensure you attach
RateLimitMiddleware to the syncRouter definition before registering its
handlers.
🧹 Nitpick comments (4)
internal/state/reporting_process.go (1)

126-133: Log a warning when credentials are absent in non-SPIFFE mode.

If GetSourceRegistryCredentials() returns empty credentials, the request silently proceeds without authentication and will be rejected with 401 by the server. Adding a warning log here would help operators diagnose authentication failures.

💡 Proposed improvement
 	// Add authentication header for non-SPIFFE mode
 	// SPIFFE mode uses mTLS for authentication, so no header is needed
 	if s.spiffeClient == nil {
 		creds := s.cm.GetSourceRegistryCredentials()
 		if creds.Username != "" && creds.Password != "" {
 			httpReq.SetBasicAuth(creds.Username, creds.Password)
+		} else {
+			logger.FromContext(ctx).Warn().Msg("No credentials available for satellite authentication, request may be rejected")
 		}
 	}
ground-control/internal/server/satellite_handlers.go (1)

570-595: Redundant GetSatelliteByName lookup — the middleware already resolved the satellite.

SatelliteAuthMiddleware already verifies the satellite exists and stores its ID via satelliteIDKey in the context. The handler re-queries the same satellite by name on Line 587, producing an unnecessary DB round-trip on every sync request. You can use the ID already in context:

♻️ Suggested refactor
 	// Get authenticated satellite name from middleware context
 	satelliteName, ok := GetSatelliteNameFromContext(r.Context())
 	if !ok {
 		// This should never happen if middleware is properly applied
 		log.Println("syncHandler: no authenticated satellite in context")
 		HandleAppError(w, &AppError{Message: "authentication required", Code: http.StatusUnauthorized})
 		return
 	}
+	satelliteID, ok := GetSatelliteIDFromContext(r.Context())
+	if !ok {
+		log.Println("syncHandler: no authenticated satellite ID in context")
+		HandleAppError(w, &AppError{Message: "authentication required", Code: http.StatusUnauthorized})
+		return
+	}
 
 	// Validate that request body name matches authenticated identity (if provided)
 	// This prevents a satellite from submitting status on behalf of another satellite
 	if req.Name != "" && req.Name != satelliteName {
 		log.Printf("syncHandler: name mismatch - authenticated=%s, requested=%s", satelliteName, req.Name)
 		HandleAppError(w, &AppError{Message: "satellite name mismatch", Code: http.StatusForbidden})
 		return
 	}
 
-	sat, err := s.dbQueries.GetSatelliteByName(r.Context(), satelliteName)
-	if err != nil {
-		log.Printf("Unknown satellite: %s", satelliteName)
-		HandleAppError(w, &AppError{
-			Message: "unknown satellite entity",
-			Code:    http.StatusForbidden,
-		})
-		return
-	}

Then replace sat.ID with satelliteID in the downstream calls (Lines 638, 656).

ground-control/internal/server/middleware.go (2)

88-99: SPIFFE auth path: a DB-registered satellite name that later gets deleted could cause a brief inconsistency.

If a satellite is deleted from the database between the middleware's GetSatelliteByName check (here) and the handler's use of the satellite ID from context, the handler could fail with a confusing error. This is a narrow TOCTOU window and unlikely in practice — just worth noting for awareness. The defensive GetSatelliteByName in the handler (which I've suggested removing in my other comment) actually provides a secondary check against this.


118-137: Minor: failed auth attempts log the robot username, which is acceptable but consider log volume.

Lines 132 and 135 log every failed robot credential attempt including the username. Under sustained attack, this could generate significant log volume. If log ingestion costs are a concern, consider rate-limiting these log lines or downgrading to debug level.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 20 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="ground-control/internal/server/routes.go">

<violation number="1" location="ground-control/internal/server/routes.go:90">
P1: The sync endpoint should have rate limiting applied to prevent brute-force attacks on robot credentials. The `SatelliteAuthMiddleware` performs CPU-intensive argon2 password verification via `crypto.VerifySecret`, and without rate limiting, an attacker could exhaust server resources by repeatedly sending invalid credentials. Consider adding `RateLimitMiddleware` before `SatelliteAuthMiddleware`, consistent with how `/ztr` and `/spiffe-ztr` endpoints are protected.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Signed-off-by: Mahil Patel <mahilpatel0808@gmail.com>
@mahil-2040
Copy link
Author

@bupd Can you please review this PR?

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Satellite Authentication on /satellites/sync Endpoint

1 participant