Allow dedicated Redis for OCSP publication
This commit is contained in:
@@ -167,6 +167,7 @@ class Settings:
|
||||
app_host: str
|
||||
app_port: int
|
||||
redis_url: Optional[str]
|
||||
ocsp_redis_url: Optional[str]
|
||||
static_dir: str
|
||||
logging_config: Optional[str]
|
||||
#: AES-256-GCM master key protecting private keys at rest.
|
||||
@@ -195,6 +196,11 @@ class Settings:
|
||||
ocsp_heartbeat_interval_seconds: int
|
||||
ocsp_redis_expiry_seconds: int
|
||||
|
||||
@property
|
||||
def ocsp_effective_redis_url(self) -> Optional[str]:
|
||||
"""Dedicated OCSP Redis when configured, otherwise the session Redis."""
|
||||
return self.ocsp_redis_url or self.redis_url
|
||||
|
||||
@staticmethod
|
||||
def from_env() -> "Settings":
|
||||
raw_master = os.environ.get("KCA_MASTER_KEY")
|
||||
@@ -226,6 +232,7 @@ class Settings:
|
||||
app_host=_env("APP_HOST", "0.0.0.0"),
|
||||
app_port=int(_env("APP_PORT", "8080")),
|
||||
redis_url=os.environ.get("REDIS_URL") or None,
|
||||
ocsp_redis_url=os.environ.get("KCA_OCSP_REDIS_URL") or None,
|
||||
static_dir=_env("STATIC_DIR", "web/dist"),
|
||||
logging_config=os.environ.get("LOGGING_CONFIG") or None,
|
||||
master_key=master_key,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""Per-CA OCSP status snapshots and Redis publication.
|
||||
|
||||
KCA is the source of truth for certificate status. This module builds a
|
||||
compact CBOR document per CA and stores it in the same Redis instance used for
|
||||
sessions, so the standalone OCSP responder never needs database access or a CA
|
||||
private key.
|
||||
compact CBOR document per CA and stores it in the selected Redis instance, so
|
||||
the standalone OCSP responder never needs database access or a CA private key.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -97,9 +96,9 @@ async def publish_snapshot(
|
||||
|
||||
owned_client = None
|
||||
if client is None:
|
||||
if not settings.redis_url:
|
||||
if not settings.ocsp_effective_redis_url:
|
||||
raise RuntimeError("REDIS_URL is not configured")
|
||||
client = Redis.from_url(settings.redis_url)
|
||||
client = Redis.from_url(settings.ocsp_effective_redis_url)
|
||||
owned_client = client
|
||||
try:
|
||||
await client.set(key, body, ex=settings.ocsp_redis_expiry_seconds)
|
||||
@@ -112,7 +111,7 @@ async def publish_snapshot(
|
||||
|
||||
async def mark_ocsp_dirty(ca_id: UUID) -> None:
|
||||
"""Mark one issuing CA's status snapshot as needing a republish."""
|
||||
if not settings.redis_url:
|
||||
if not settings.ocsp_effective_redis_url:
|
||||
return
|
||||
row = await OcspSnapshot.get_or_none(ca_id=ca_id)
|
||||
if row is None:
|
||||
@@ -126,7 +125,7 @@ async def mark_ocsp_dirty(ca_id: UUID) -> None:
|
||||
|
||||
|
||||
async def mark_all_cas_dirty(vault: Vault) -> None:
|
||||
if not settings.redis_url:
|
||||
if not settings.ocsp_effective_redis_url:
|
||||
return
|
||||
for ca in await Certificate.filter(vault_id=vault.id, is_ca=True):
|
||||
await mark_ocsp_dirty(ca.id)
|
||||
@@ -155,11 +154,14 @@ class OcspPublisherMixin(KayaMixin):
|
||||
return None
|
||||
|
||||
def setup(self, loop: asyncio.AbstractEventLoop) -> None:
|
||||
if not settings.redis_url:
|
||||
log.info("OCSP Redis publication disabled (REDIS_URL unset)")
|
||||
if not settings.ocsp_effective_redis_url:
|
||||
log.info(
|
||||
"OCSP Redis publication disabled "
|
||||
"(REDIS_URL and KCA_OCSP_REDIS_URL unset)"
|
||||
)
|
||||
return
|
||||
if self._redis_client is None:
|
||||
self._redis_client = Redis.from_url(settings.redis_url)
|
||||
self._redis_client = Redis.from_url(settings.ocsp_effective_redis_url)
|
||||
self._owns_redis_client = True
|
||||
self._task = loop.create_task(self._poll())
|
||||
log.info(
|
||||
@@ -249,7 +251,7 @@ def publisher_mixin_from_settings(
|
||||
settings_obj: Settings,
|
||||
ensure_context: Callable[[], Awaitable[None]],
|
||||
) -> Optional[OcspPublisherMixin]:
|
||||
if not settings_obj.redis_url:
|
||||
if not settings_obj.ocsp_effective_redis_url:
|
||||
return None
|
||||
return OcspPublisherMixin(
|
||||
settings_obj.ocsp_publish_interval_seconds,
|
||||
|
||||
@@ -155,9 +155,9 @@ def register(app: KayaApp, kca: "Kca") -> None:
|
||||
row = await OcspSnapshot.get_or_none(ca_id=cert.id)
|
||||
snapshot = await snapshot_for_ca(cert)
|
||||
await send_json(ctx, 200, {
|
||||
"enabled": bool(settings.redis_url),
|
||||
"enabled": bool(settings.ocsp_effective_redis_url),
|
||||
"prefix": settings.ocsp_redis_prefix,
|
||||
"dirty": row.dirty if row else bool(settings.redis_url),
|
||||
"dirty": row.dirty if row else bool(settings.ocsp_effective_redis_url),
|
||||
"published_at": row.published_at.isoformat() if row and row.published_at else None,
|
||||
"last_error": row.last_error if row else None,
|
||||
"snapshot": snapshot,
|
||||
|
||||
Reference in New Issue
Block a user