Skip to content

Add Cloudflare Access authentication#176

Open
pius-pp wants to merge 11 commits into
remnawave:devfrom
pius-pp:cloudflare-access
Open

Add Cloudflare Access authentication#176
pius-pp wants to merge 11 commits into
remnawave:devfrom
pius-pp:cloudflare-access

Conversation

@pius-pp
Copy link
Copy Markdown

@pius-pp pius-pp commented May 24, 2026

Summary

Adds Cloudflare Access as an optional authentication method for the panel.

The implementation validates the Cf-Access-Jwt-Assertion JWT instead of trusting forwarded identity headers. It verifies the token signature against the Cloudflare Access cert endpoint, checks issuer and audience, extracts the authenticated email from the JWT payload, and then issues the regular Remnawave admin JWT.

Changes

  • Add Cloudflare Access auth route: POST /api/auth/cloudflare-access
  • Add Cloudflare Access settings to Remnawave settings:
    • enabled
    • team domain
    • audience
    • email allowlist toggle
    • allowed emails
    • allowed domains
  • Add Prisma JSONB column and migration for Cloudflare Access settings
  • Add settings seed/default validation
  • Expose Cloudflare Access status in auth status response
  • Validate Cloudflare Access JWTs against Cloudflare certs/JWKS
  • Allow disabling the extra Remnawave-side email allowlist when Cloudflare Access policy is already authoritative

Security Notes

  • Does not trust Cf-Access-Authenticated-User-Email
  • Requires a valid signed Cloudflare Access JWT
  • Verifies configured audience and issuer
  • Supports optional email/domain allowlist as a defense-in-depth layer
  • Does not log token contents

Validation

  • git diff --check

@snyk-io
Copy link
Copy Markdown

snyk-io Bot commented May 24, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@what-the-diff
Copy link
Copy Markdown

what-the-diff Bot commented May 24, 2026

PR Summary

  • Security Policy Addition: We've added a guide that outlines how to notify us about any possible security risks.
  • Enhanced Login Access: We've added a new way for users to sign in using Cloudflare Access. This new method is more secure and efficient.
  • Status Tracker Improvement: Our status tracker tool has been upgraded to also include Cloudflare Access status updates.
  • Improved Configuration Options: Users can now configure their Cloudflare Access settings more effectively as we've introduced a new setting schema.
  • Database and Model Upgrades: Our database and backend models have been updated to store and handle these new Cloudflare Access settings.
  • Increased Security: We've implemented strong checks and validations for the new Cloudflare Access login process, leading to better protection for user data.
  • Improved Data Response: We've restructured our data response model to include necessary information about Cloudflare Access, making it more comprehensive for users.
  • Effective Validation Checks: We've put in place more robust checks on Cloudflare Access settings that ensure the validity of email addresses and domain formats.

These improvements aim to enhance user experience, system security, and general product efficiency.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 24, 2026

Greptile Summary

This PR adds Cloudflare Access as an optional authentication method, validating Cf-Access-Jwt-Assertion JWTs against Cloudflare's public cert endpoint before issuing a Remnawave admin JWT. It adds a new Prisma JSONB column, settings schema, seed/default handling, a dedicated CloudflareAccessService, and exposes CF Access status in the auth-status response.

  • New auth route (POST /api/auth/cloudflare-access): reads the cf-access-jwt-assertion header, verifies it against Cloudflare's cert endpoint (with 1-hour caching), checks audience/issuer/email allowlist, and issues a Remnawave JWT for the first admin.
  • Settings management: adds a cloudflare_access_settings JSONB column (nullable, additive migration), default seeding, and validation rules for team domain, audience, and allowlist.
  • Email/domain allowlist: optional defense-in-depth layer on top of the Cloudflare Access policy check.

Confidence Score: 3/5

The new Cloudflare Access auth flow itself is well-structured, but two defects in the changed files can cause runtime crashes or a complete auth outage before any fixes are applied.

The validateSettings method accesses cloudflareAccessSettings.enabled without a null guard. After the migration runs (but before the seeder initializes the column), any settings update will throw a TypeError and return a 500, blocking all admin settings changes. Separately, the cert cache is never refreshed when a cached cert list exists but doesn't contain the incoming kid, so a Cloudflare key rotation would lock out all CF Access users for up to one hour until the cache TTL expires.

src/modules/remnawave-settings/remnawave-settings.service.ts (null guard on cloudflareAccessSettings) and src/modules/auth/services/cloudflare-access.service.ts (cert cache refresh on kid miss).

Important Files Changed

Filename Overview
src/modules/auth/services/cloudflare-access.service.ts New service implementing Cloudflare JWT validation, cert fetching and caching, and email allowlist checks — but the cert cache is not refreshed on kid miss (key rotation outage) and only RS256 is allowed despite EC key handling in fetchCerts.
src/modules/remnawave-settings/remnawave-settings.service.ts Adds Cloudflare Access validation into validateSettings, but accesses cloudflareAccessSettings.enabled without null guard — crashes for any existing row where the new nullable column is still null post-migration.
src/modules/auth/auth.service.ts Adds cloudflareAccessLogin method that checks feature flag, fetches the first admin, validates the assertion, then issues a Remnawave JWT; logic is sound with safe fallback on null settings.
prisma/migrations/20260524190000_add_cloudflare_access_settings/migration.sql Adds nullable JSONB column cloudflare_access_settings to remnawave_settings — straightforward additive migration.
prisma/seed/seeders/3_seed-remnawave-settings.ts Seeds default Cloudflare Access settings and validates/resets existing rows; covers the null-column case post-migration, but the null-safety issue in the service still exists during the window before the seed runs.
libs/contract/models/remnawave-settings/cloudflare-access-settings.schema.ts Clean Zod schema for Cloudflare Access settings with nullable teamDomain and audience, boolean flags, and email/domain arrays.
src/modules/auth/auth.controller.ts Adds cloudflareAccessLogin endpoint reading the cf-access-jwt-assertion header correctly, following existing controller patterns.

Reviews (1): Last reviewed commit: "Add Cloudflare Access authentication" | Re-trigger Greptile

Comment thread src/modules/remnawave-settings/remnawave-settings.service.ts Outdated
Comment thread src/modules/remnawave-settings/remnawave-settings.service.ts Outdated
Comment on lines +101 to +111
private async getCertByKeyId(teamDomain: string, keyId: string): Promise<string | null> {
const cacheKey = `cloudflare-access:${teamDomain}:certs`;
const cachedCerts = await this.rawCacheService.get<ICloudflareAccessCert[]>(cacheKey);
const certs = cachedCerts ?? (await this.fetchCerts(teamDomain));

if (!cachedCerts && certs.length > 0) {
await this.rawCacheService.set(cacheKey, certs, 3600);
}

return certs.find((cert) => cert.kid === keyId)?.cert ?? null;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Stale cache on Cloudflare key rotation

When Cloudflare rotates its signing keys (which it does periodically), new JWTs carry a new kid. If the cert list is already cached, cachedCerts ?? (await this.fetchCerts(...)) returns the stale cached list and certs.find((cert) => cert.kid === keyId) returns undefined — so the method returns null without ever re-fetching. Every Cloudflare Access login attempt then fails with 403 for up to 1 hour (the cache TTL), effectively locking out all CF Access users until the cache naturally expires.

Comment thread src/modules/auth/services/cloudflare-access.service.ts
pius-pp and others added 3 commits May 27, 2026 14:19
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@pius-pp
Copy link
Copy Markdown
Author

pius-pp commented May 27, 2026

Frontend PR: remnawave/frontend#348

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants