mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-02 22:38:44 +02:00
This PR replaces the downloading and parsing of RISwhois files in Krill with the roto-api.
253 lines
11 KiB
Markdown
253 lines
11 KiB
Markdown
Certificate Authority Manager
|
|
=============================
|
|
|
|
A single Krill instance can have multiple Certificate Authorities (CAs). The
|
|
`CaManager` is the central component that is responsible for managing them.
|
|
|
|
The core functionality of CAs is contained in the event-sourced `CertAuth` type,
|
|
but we have two more companion types for CAs: `CaObjects` is used to manage
|
|
manifests and CRLs for CAs, and `StatusStore` is used to track the `CaStatus`
|
|
of each CA which tells us about the last successful exchange, or issues, that
|
|
a CA has had when connecting to its parent(s) and repository.
|
|
|
|
The reason for this split in responsibilities is that tracking the issuance
|
|
of new manifests and CRLs, or the last successful check with a parent for
|
|
resource entitlements (default: every 10 mins) would result in an excessive
|
|
amount of commands and events in the history. In itself that is not an issue
|
|
as long as these historical entries would be valuable to rebuilding or
|
|
understanding the current state of a CA, but the **automatic** generation
|
|
of CRLs and manifests, and historical connections between a CA and
|
|
its parent or repository are not very interesting in this regard.
|
|
|
|
The `CaManager` can also manage an embedded TA Signer, in case a testbed setup
|
|
is used (as we do in tests), and a TA Proxy.
|
|
|
|
It also has access to an `Arc<TaskQueue>` that is shared with the `Scheduler`
|
|
so that it can add or reschedule certain tasks through the API. For example,
|
|
this is used to add a task to sync a CA with its parent 'now' when requested
|
|
through the UI or CLI.
|
|
|
|
And, for completeness, this component has access to a `KrillSigner` that
|
|
can be used to sign things, and the central `Config` that contains runtime
|
|
settings such as the frequency by which to republish new manifests and CRLs.
|
|
|
|
Combined we then get this structure:
|
|
|
|
```rust
|
|
pub struct CaManager {
|
|
// Used to manage CAs.
|
|
ca_store: AggregateStore<CertAuth>,
|
|
|
|
// Used to manage objects for CAs. Also shared with the ca_store as well
|
|
// as a listener so that it can create manifests and CRLs as needed. Accessed
|
|
// here for publishing.
|
|
ca_objects_store: Arc<CaObjectsStore>,
|
|
|
|
// Keep track of CA parent and CA repository interaction status.
|
|
status_store: StatusStore,
|
|
|
|
// We may have a TA Proxy that we need to manage. Many functions are
|
|
// similar to CA operations, so it makes sense to manage this as a
|
|
// special kind of CA here.
|
|
ta_proxy_store: Option<AggregateStore<TrustAnchorProxy>>,
|
|
|
|
// We may also have a local TA signer - in case we are running in
|
|
// testbed or benchmarking mode - so that we can do all TA signing
|
|
// without the need for user interactions through the API and
|
|
// TA signer CLI.
|
|
ta_signer_store: Option<AggregateStore<TrustAnchorSigner>>,
|
|
|
|
// Shared task queue:
|
|
// - listens for events in the ca_store.
|
|
// - processed by the Scheduler.
|
|
// - can be used here to schedule tasks through the api.
|
|
tasks: Arc<TaskQueue>,
|
|
|
|
config: Arc<Config>,
|
|
signer: Arc<KrillSigner>,
|
|
|
|
// System actor is used for (scheduled or triggered) system actions where
|
|
// we have no operator actor context.
|
|
system_actor: Actor,
|
|
}
|
|
```
|
|
|
|
Initialization
|
|
--------------
|
|
|
|
The `CaManager` is instantiated when Krill starts. All its components ultimately
|
|
rely on the `KeyValueStore` - which currently only supports a disk based back-end,
|
|
but which can be modified to support other storage options in future.
|
|
|
|
For full details see `CaManager::build`. It includes extensive comments.
|
|
|
|
Worth noting here is that the event listening is used here. For example, if a
|
|
`CaEvtDet::RoasUpdated` event occurs, then this will trigger:
|
|
1. (pre-save) that the `CaObjectsStore` updates the ROAs held by the CA,
|
|
and generates a new CRL and manifest; and
|
|
2. (post-save) that the `TaskQueue` notices, and schedules a task to
|
|
synchronize the contents of the `CaObjects` with the CA's repository.
|
|
|
|
But, see issue https://github.com/NLnetLabs/krill/issues/1182. It would be
|
|
better to remove the pre-save listener trait altogether and let a new CRL
|
|
and manifest be generated post-save. This can be done as idempotent
|
|
`Task::RepublishIfNeeded` by changing the `CaObjects` implementation to
|
|
simply query the CA for the full set of current objects and then letting
|
|
it decide to republish, not only because it's time, but also because content
|
|
haas changed.
|
|
|
|
|
|
Asynchronous Actions
|
|
--------------------
|
|
|
|
The aforementioned `TaskQueue` is shared with the `Scheduler` which is owned
|
|
by `KrillServer`. The `Scheduler` runs as a separate async function and is started
|
|
and watches the `TaskQueue` for queued tasks.
|
|
|
|
The `TaskQueue` orders `Task`-s by `Priority`. The `Priority` is based on timestamps.
|
|
The lowest timestamp value results in the highest priority. The `Task` enum is explained
|
|
below and contains the various background tasks that may be scheduled. They are scheduled
|
|
or re-scheduled with an appropriate priority. Generally speaking:
|
|
- we use 'now' for triggered tasks - such as sync with repo
|
|
- we use 'now' pus 5 minutes for rescheduling failed syncs with parent/repo
|
|
- we use 'now' plus a configurable interval for scheduling any recurring tasks
|
|
|
|
Furthermore, it should be noted that if a task is already present on the `TaskQueue`,
|
|
then scheduling it again will result in a single task being scheduled using the
|
|
highest priority between 'new' and 'existing'.
|
|
|
|
A Krill instance only has a single (singleton) `CaManager` and `RepositoryManager`, which
|
|
are kept as `Arc<CaManager>` and `Arc<RepositoryManager>` so that they (well the reference)
|
|
can easily be shared with the `Scheduler`, allowing it for example to get the latest objects
|
|
for a CA, or to get a CA to sign an RFC 8181 or RFC 6492 message. Furthermore, it also
|
|
allows this background job to send new triggered commands to a CA, e.g.: update a received
|
|
certificate under a parent.
|
|
|
|
This approach allows that changes to CAs can be made locally and promptly, without
|
|
needing to wait for synchronization with a remote system like a parent or repository.
|
|
Furthermore it allows that in case of any issues in connecting to a remote system,
|
|
the task can be rescheduled.
|
|
|
|
Tasks are defined in the `daemon::mq::Task` enum. See the code and comments for
|
|
more details. Here, we will highlight a couple:
|
|
|
|
### Task::QueueStartTasks
|
|
|
|
Essentially this ensures that all *missing* tasks are rescheduled at startup. See
|
|
`daemon::scheduler::Scheduler::queue_start_tasks` for details.
|
|
|
|
|
|
### Task::SyncRepo
|
|
|
|
These tasks are used to trigger that a CA synchronizes with its repository. All
|
|
objects that need to be published have already been created, so this is just about
|
|
synchronizing the content. If a `SyncRepo` task fails, then it will be rescheduled
|
|
with a new priority 5 minutes into future (const: `SCHEDULER_REQUEUE_DELAY_SECONDS`).
|
|
|
|
Successes and failures will be tracked in the `StatusStore` held by the `CaManager`.
|
|
|
|
### Task::SyncParent
|
|
|
|
These tasks are used to trigger that a CA synchronizes with a specific parent.
|
|
|
|
There are three synchronization scenarios between a CA and its parent:
|
|
|
|
1) pending certificate request exists
|
|
|
|
In this case the pending request is sent to the parent. If the certificate is
|
|
signed by the parent this will trigger that a new command is sent to the `CertAuth`
|
|
to update the received certificate under a parent. This in turn can trigger changes
|
|
in the CA, such as issuing/removing ROAs because of changes in resource entitlements.
|
|
Such content changes will trigger that the `CaObjects` structure is updated,
|
|
generates a new manifest and CRL, and that a `SyncRepo` task is added.
|
|
|
|
2) pending revocation request exists
|
|
|
|
Revocation requests are added when the CA performs a key roll and it's ready to
|
|
remove the old key. If any revocation requests exist, then they will be sent to
|
|
the parent, and when confirmed the `CertAuth` will be sent a command to complete
|
|
the key roll. That in turn will trigger that the old key is removed and its objects
|
|
are removed from the `CaObjects` structure. That in turn will then trigger a
|
|
`SyncRepo` task to be added.
|
|
|
|
3) no pending request exists
|
|
|
|
In this case the job will result in a "Resource Class List Query" (section 3.3.1 of RFC 6492)
|
|
to be sent to the parent. The response to this query contains the current
|
|
entitlements to resources and validity times under a parent. This response
|
|
will then be sent to the `CertAuth` in the form of a command to update its
|
|
entitlements. This command will result in a no-op in case there are no changes.
|
|
But, if there are changes then it will result in appropriate new events, such
|
|
as generation of new certificate request. That will then trigger that the
|
|
parent synchronization is scheduled again.
|
|
|
|
If the task was successful, then a new `Task::SyncParent` is scheduled using
|
|
the appropriate (variable) interval returned by `config.ca_refresh_next()`.
|
|
If the task was a failure (hard communication error), then it will be rescheduled
|
|
with a new priority 5 minutes into future (const: `SCHEDULER_REQUEUE_DELAY_SECONDS`).
|
|
|
|
### Task::CheckSuspendChildren
|
|
|
|
This task triggers that inactive children are suspended IFF this non-default
|
|
behaviour is enabled in config.
|
|
|
|
### Task::RepublishIfNeeded
|
|
|
|
This task triggers that all CAs check whether any manifest or CRL need to be
|
|
re-issued, and if so do that re-issuance. Any such re-issuance will result
|
|
in events that the listener will pick up, which in turn will ensure that
|
|
the appropriate `Task::SyncRepo` is scheduled.
|
|
|
|
Note: we may change this behaviour in future to just schedule the specific
|
|
republish for a known CA and resource class based on last issuance instead.
|
|
This would save scanning, and would allow reporting the planned re-issuance
|
|
more easily.
|
|
|
|
### Task::RenewObjectsIfNeeded
|
|
|
|
This tasks triggers that all CAs check whether any signed objects need
|
|
to be re-issued.
|
|
|
|
We may want to change this behaviour in future to just have specific tasks
|
|
for reissuing specific objects instead. This would allow us to report these
|
|
things more clearly.
|
|
|
|
But it will require that we perform checks at startup time and/or have a
|
|
persistent queue - to ensure that the tasks are not lost (typically they
|
|
would be around 1 year into the future on every issuance). Plus we would
|
|
need logic to clean up tasks for removed objects - so this needs some thought.
|
|
|
|
### Task::SweepLoginCache
|
|
|
|
This task triggers that expired logins are removed from the cache in
|
|
case the `multi-user` feature is enabled.
|
|
|
|
### Task::ResourceClassRemoved
|
|
|
|
This task is planned when a resource class is removed, and it triggers
|
|
that any remaining keys are requested to be revoked by the parent. A resource
|
|
class can be removed because the parent no longer entitles the CA to have
|
|
certain resources, and in that case the revocation request may fail. This
|
|
is treated as a non-critical issue.
|
|
|
|
Note that if the CA actively removes a parent, it will pro-actively send
|
|
revocation requests for all its keys first, remove *all* resource classes
|
|
under a parent so that objects will be withdrawn, and then remove the
|
|
actual parent altogether.
|
|
|
|
### Task::UnexpectedKey
|
|
|
|
> NOTE: This case has never been observed in the wild.
|
|
|
|
This is an unlikely case which actually needs better testing.
|
|
|
|
In this case the parent CA returned "Resource Class List Response" in
|
|
an earlier exchange, that contained keys unknown to this CA. In such cases
|
|
the CA will generate events that trigger that revocation of any surplus
|
|
key is requested.
|
|
|
|
We should remove and replace this logic.. what we probably should do
|
|
in this case is that we do a full re-synchronization in terms of keys
|
|
with the parent in question. Removing and then re-adding the parent is
|
|
probably the easiest way to achieve this.
|