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

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions