Skip to content

Feature Request: Support client certificates (mTLS) for registry-mirror back-to-source #1882

Description

@meobilivang

Feature request: support client certificates (mTLS) for registry-mirror back-to-source

Summary

dfdaemon can't present a client certificate when it fetches from an upstream
registry, so it can't talk to any registry that requires mutual TLS (mTLS).
The back-to-source fetch fails at the TLS handshake, even though a direct
containerd pull to the same registry works.

Today, using Dragonfly as a pull-through cache in front of an mTLS-enabled
registry requires standing up extra components (e.g. an external mTLS-terminating
proxy in front of dragonfly) to work around this. There's no config option for a
client cert/key: RegistryMirror only takes a CA cert, and the TLS client is
always built with .with_no_client_auth(). Native support for mTLS from Dragonfly would remove the need
for that extra setup.

Environment

  • dragonflyoss/client: v1.3.12 (also present on main)
  • dragonflyoss/helm-charts: dragonfly chart 1.6.27
  • Deployment: dfdaemon DaemonSet acting as a registry pull-through cache; containerd routes pulls through it via a hosts.toml mirror entry.

Use case

We run Dragonfly as a P2P registry pull-through cache in front of an OCI registry
that requires mTLS (clients must present an x509 client certificate). The
topology is the standard one:

  • containerd → Dragonfly mirror (hosts.toml) → dfdaemon → upstream registry.
  • On a cache miss, dfdaemon does a "back-to-source" fetch to the registry.

Direct containerd pulls work because hosts.toml has an option for a client key
pair:

[host."https://registry.example.com"]
  capabilities = ["pull", "resolve"]
  client = [["/path/to/client.pem", "/path/to/client-key.pem"]]

But routed through Dragonfly, dfdaemon presents no client certificate, so the
registry kills the handshake. Registries that don't require client auth work fine. This gap is specific to mTLS-enabled upstreams.

Current behavior / root cause

In dragonfly-client-backend/src/http.rs, both client builders hard-code
.with_no_client_auth(): link to code

// http.rs (default client, ~L163-166)
let client_config_builder = rustls::ClientConfig::builder()
    .dangerous()
    .with_custom_certificate_verifier(NoVerifier::new())
    .with_no_client_auth();              // <-- no client cert presented

link to code

// http.rs (cert-configured client, fn client ~L221-234)
fn client(&self, client_cert: Option<Vec<CertificateDer<'static>>>, ...) -> Result<...> {
    match client_cert.as_ref() {
        Some(client_cert) => {
            let mut root_cert_store = rustls::RootCertStore::empty();
            root_cert_store.add_parsable_certificates(client_cert.to_owned());

            let client_config_builder = rustls::ClientConfig::builder()
                .with_root_certificates(root_cert_store)   // <-- trust bundle to verify the server (in case running custom CA/self-signed cert)
                .with_no_client_auth();                    // <-- still no client cert presented

One thing worth highlighting: even though this path takes a client_cert, it's used as
a server trust bundle, which is passed to
.with_root_certificates(...) to verify the registry/server, rather than presented as a client
identity. So despite the name, there's currently no code path that calls
with_client_auth_cert(),
which is what would be needed to present a client certificate.

The config struct reflects the same gap. RegistryMirror in
dragonfly-client-config/src/dfdaemon.rs (~L1271-1294) only has the option to pass the trust bundle:

pub struct RegistryMirror {
    pub addr: String,
    /// Cert is the client cert path with PEM format for the registry.
    /// If registry use self-signed cert, the client should set the
    /// cert for the registry mirror.
    pub cert: Option<PathBuf>,          // <-- CA / server trust root, not client identity
    pub enable_task_id_based_blob_digest: bool,
}

If we are adding mTLS, we will need to update the Helm chart as well:
charts/dragonfly/values.yaml (~L1120-1135):

registryMirror:
  addr: https://index.docker.io
  enableTaskIDBasedBlobDigest: true
# cert: ""   # CA cert only

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions