Skip to content

Commit

Permalink
iotedge config apply: support issued Edge CA cert (#4899) (#5629)
Browse files Browse the repository at this point in the history
Allows an issued Edge CA certificate to be specified in the super config.

The Edge CA certificate may be issued over EST or by a local CA certificate. The SelfSigned issuance option is equivalent to the existing quickstart option, but allows a customizable certificate common name.
  • Loading branch information
gordonwang0 authored Oct 6, 2021
1 parent 4faa9a0 commit 6368eb6
Show file tree
Hide file tree
Showing 25 changed files with 671 additions and 30 deletions.
67 changes: 55 additions & 12 deletions edgelet/contrib/config/linux/template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
# source = "manual"
# iothub_hostname = "example.azure-devices.net"
# device_id = "my-device"
#
#
# [provisioning.authentication]
# method = "sas"
#
Expand All @@ -99,7 +99,7 @@
# source = "manual"
# iothub_hostname = "example.azure-devices.net"
# device_id = "my-device"
#
#
# [provisioning.authentication]
# method = "x509"
#
Expand All @@ -116,7 +116,7 @@
# source = "dps"
# global_endpoint = "https://global.azure-devices-provisioning.net"
# id_scope = "0ab1234C5D6"
#
#
# [provisioning.attestation]
# method = "symmetric_key"
# registration_id = "my-device"
Expand All @@ -131,7 +131,7 @@
# source = "dps"
# global_endpoint = "https://global.azure-devices-provisioning.net"
# id_scope = "0ab1234C5D6"
#
#
# [provisioning.attestation]
# method = "x509"
# registration_id = "my-device"
Expand All @@ -149,7 +149,7 @@
# source = "dps"
# global_endpoint = "https://global.azure-devices-provisioning.net"
# id_scope = "0ab1234C5D6"
#
#
# [provisioning.attestation]
# method = "tpm"
# registration_id = "my-device"
Expand All @@ -170,7 +170,7 @@
# trusted_certs = [
# "file:///var/secrets/est-id-ca.pem",
# ]
#
#
# [cert_issuance.est.auth]
# username = "estuser"
# password = "estpwd"
Expand Down Expand Up @@ -276,26 +276,69 @@
# ==============================================================================
#
# If you have your own Edge CA certificate that you want all module certificates
# to be issued by, uncomment this section and replace the values with your own.
#
# to be issued by, uncomment one of the sections below and replace the values with
# your own.

# Edge CA certificate loaded from a file:
# ---------------------

# [edge_ca]
# cert = "file:///var/secrets/edge-ca.pem" # file URI
#
# pk = "file:///var/secrets/edge-ca.key.pem" # file URI, or...
# pk = "pkcs11:slot-id=0;object=edge%20ca?pin-value=1234" # PKCS#11 URI

# Edge CA certificate issued over EST:
# ---------------------

# [edge_ca]
# method = "est"
#
# # Optional EST configuration for issuing the Edge CA certificate below.
# # If not set, the defaults in [cert_issuance.est] will be used.
#
# common_name = "iotedged workload ca"
# expiry_days = 90
# url = "https://example.org/.well-known/est"
#
# username = "estuser"
# password = "estpwd"
#
# # EST ID cert already on device, or...
# identity_cert = "file:///var/secrets/est-id.pem"
#
# identity_pk = "file:///var/secrets/est-id.key.pem" # file URI, or...
# identity_pk = "pkcs11:slot-id=0;object=est-id?pin-value=1234" # PKCS#11 URI
#
# # EST ID cert requested via EST bootstrap ID cert
# bootstrap_identity_cert = "file:///var/secrets/est-bootstrap-id.pem"
#
# bootstrap_identity_pk = "file:///var/secrets/est-bootstrap-id.key.pem" # file URI, or...
# bootstrap_identity_pk = "pkcs11:slot-id=0;object=est-bootstrap-id?pin-value=1234" # PKCS#11 URI

# Edge CA certificate issued from a local CA certificate:
# Requires [cert_issuance.local_ca] to be set.
# ---------------------

# [edge_ca]
# method = "local_ca"
#
# # Optional configuration below.
#
# common_name = "iotedged workload ca"
# expiry_days = 90

# ==============================================================================
# Edge CA certificate (Quickstart)
# ==============================================================================
#
# If you do not have your own Edge CA certificate that you want all
# module certificates to be issued by, you can uncomment this section and
# set the number of days for the lifetime of the auto-generated
# If you do not have your own Edge CA certificate that you want all
# module certificates to be issued by, you can uncomment this section and
# set the number of days for the lifetime of the auto-generated
# self-signed Edge CA certificate. Defaults to 90 days.
#
# SECURITY NOTE: this setting is NOT recommended for production usage. Please
# configure your own Edge CA certificate in the Edge CA certificate section
# configure your own Edge CA certificate in the Edge CA certificate section
# above instead.
#
# [edge_ca]
Expand Down
132 changes: 116 additions & 16 deletions edgelet/iotedge/src/config/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,91 @@ fn execute_inner(
});

let (edge_ca_cert, edge_ca_key) = match edge_ca {
super_config::EdgeCa::Explicit { cert, pk } => {
super_config::EdgeCa::Issued { cert } => {
let common_name = Some(cert.common_name.unwrap_or(format!(
"{} {}",
IOTEDGED_COMMONNAME_PREFIX, identityd_config.hostname
)));
let expiry_days = cert.expiry_days;

match cert.method {
common_config::super_config::CertIssuanceMethod::Est { url, auth } => {
let mut aziotcs_principal = aziot_keyd_config::Principal {
uid: aziotcs_uid.as_raw(),
keys: vec![edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()],
};

let mut keys = std::collections::BTreeMap::default();

let auth = common_config::apply::set_est_auth(
&auth,
&mut certd_config.preloaded_certs,
&mut keys,
&mut aziotcs_principal,
edgelet_core::AZIOT_EDGED_CA_ALIAS,
);

keyd_config.principal.push(aziotcs_principal);

keyd_config.preloaded_keys.append(
&mut keys
.into_iter()
.map(|(id, location)| (id, location.to_string()))
.collect(),
);

certd_config.cert_issuance.certs.insert(
edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned(),
aziot_certd_config::CertIssuanceOptions {
method: aziot_certd_config::CertIssuanceMethod::Est { url, auth },
common_name,
expiry_days,
},
);

(
Some(edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()),
Some(edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()),
)
}
common_config::super_config::CertIssuanceMethod::LocalCa => {
certd_config.cert_issuance.certs.insert(
edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned(),
aziot_certd_config::CertIssuanceOptions {
method: aziot_certd_config::CertIssuanceMethod::LocalCa,
common_name,
expiry_days,
},
);

keyd_config.principal.push(aziot_keyd_config::Principal {
uid: aziotcs_uid.as_raw(),
keys: vec![edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()],
});

(
Some(edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()),
Some(edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()),
)
}
common_config::super_config::CertIssuanceMethod::SelfSigned => {
// This is equivalent to a quickstart CA.
set_quickstart_ca(
&mut keyd_config,
&mut certd_config,
aziotcs_uid,
expiry_days,
common_name,
);

(
Some(edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()),
Some(edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()),
)
}
}
}
super_config::EdgeCa::Preloaded { cert, pk } => {
keyd_config.preloaded_keys.insert(
edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned(),
pk.to_string(),
Expand All @@ -272,23 +356,17 @@ fn execute_inner(
super_config::EdgeCa::Quickstart {
auto_generated_edge_ca_expiry_days,
} => {
certd_config.cert_issuance.certs.insert(
edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned(),
aziot_certd_config::CertIssuanceOptions {
method: aziot_certd_config::CertIssuanceMethod::SelfSigned,
common_name: Some(format!(
"{} {}",
IOTEDGED_COMMONNAME_PREFIX, identityd_config.hostname
)),
expiry_days: Some(auto_generated_edge_ca_expiry_days),
},
set_quickstart_ca(
&mut keyd_config,
&mut certd_config,
aziotcs_uid,
Some(auto_generated_edge_ca_expiry_days),
Some(format!(
"{} {}",
IOTEDGED_COMMONNAME_PREFIX, identityd_config.hostname
)),
);

keyd_config.principal.push(aziot_keyd_config::Principal {
uid: aziotcs_uid.as_raw(),
keys: vec![edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()],
});

(None, None)
}
};
Expand Down Expand Up @@ -430,6 +508,28 @@ fn execute_inner(
})
}

fn set_quickstart_ca(
keyd_config: &mut aziot_keyd_config::Config,
certd_config: &mut aziot_certd_config::Config,
aziotcs_uid: nix::unistd::Uid,
expiry_days: Option<u32>,
common_name: Option<String>,
) {
certd_config.cert_issuance.certs.insert(
edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned(),
aziot_certd_config::CertIssuanceOptions {
method: aziot_certd_config::CertIssuanceMethod::SelfSigned,
common_name,
expiry_days,
},
);

keyd_config.principal.push(aziot_keyd_config::Principal {
uid: aziotcs_uid.as_raw(),
keys: vec![edgelet_core::AZIOT_EDGED_CA_ALIAS.to_owned()],
});
}

#[cfg(test)]
mod tests {
#[test]
Expand Down
2 changes: 1 addition & 1 deletion edgelet/iotedge/src/config/import/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ fn execute_inner(
}) = device_cert
{
(
Some(super_config::EdgeCa::Explicit {
Some(super_config::EdgeCa::Preloaded {
cert: device_ca_cert,
pk: device_ca_pk,
}),
Expand Down
8 changes: 7 additions & 1 deletion edgelet/iotedge/src/config/super_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::collections::BTreeMap;

use url::Url;

use aziotctl_common::config as common_config;

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub(super) struct Config {
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -67,7 +69,11 @@ pub(super) fn default_agent() -> edgelet_core::ModuleSpec<edgelet_docker::Docker
#[derive(Debug, serde_derive::Deserialize, serde_derive::Serialize)]
#[serde(untagged)]
pub(super) enum EdgeCa {
Explicit {
Issued {
#[serde(flatten)]
cert: common_config::super_config::CertIssuanceOptions,
},
Preloaded {
cert: Url,
pk: Url,
},
Expand Down
24 changes: 24 additions & 0 deletions edgelet/iotedge/test-files/config/ca-certs-est/certd.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This file is auto-generated by `iotedge config apply`
# Do not edit it manually; any edits will be lost when the command is run again.

homedir_path = "/var/lib/aziot/certd"
[cert_issuance.aziot-edged-ca]
common_name = "iotedged workload ca my-device"
expiry_days = 120
method = "est"
url = "https://example.org/.well-known/est"
username = "user"
password = "password"
identity_cert = "est-id-aziot-edged-ca"
identity_pk = "est-id-aziot-edged-ca"
bootstrap_identity_cert = "est-bootstrap-id-aziot-edged-ca"
bootstrap_identity_pk = "est-bootstrap-id-aziot-edged-ca"

[preloaded_certs]
aziot-edged-trust-bundle = ["aziot-edged-ca", "trust-bundle-user"]
est-bootstrap-id-aziot-edged-ca = "file:///var/secrets/est-bootstrap-id.pem"
trust-bundle-user = "file:///var/secrets/trusted-ca.pem"

[[principal]]
uid = 5558
certs = ["aziot-edged-ca", "aziot-edged/module/*"]
1 change: 1 addition & 0 deletions edgelet/iotedge/test-files/config/ca-certs-est/device-id
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aziot-identity-service|aziot-ide
40 changes: 40 additions & 0 deletions edgelet/iotedge/test-files/config/ca-certs-est/edged.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This file is auto-generated by `iotedge config apply`
# Do not edit it manually; any edits will be lost when the command is run again.

hostname = "my-device"
edge_ca_cert = "aziot-edged-ca"
edge_ca_key = "aziot-edged-ca"
trust_bundle_cert = "aziot-edged-trust-bundle"
auto_reprovisioning_mode = "OnErrorOnly"
homedir = "/var/lib/aziot/edged"
allow_elevated_docker_permissions = true

[agent]
name = "edgeAgent"
type = "docker"
imagePullPolicy = "on-create"

[agent.config]
image = "mcr.microsoft.com/azureiotedge-agent:1.0"

[agent.config.createOptions]

[agent.config.auth]

[agent.env]

[connect]
workload_uri = "unix:///var/run/iotedge/workload.sock"
management_uri = "unix:///var/run/iotedge/mgmt.sock"

[listen]
workload_uri = "fd://aziot-edged.workload.socket"
management_uri = "fd://aziot-edged.mgmt.socket"
min_tls_version = "tls1.0"

[watchdog]
max_retries = "infinite"

[moby_runtime]
uri = "unix:///var/run/docker.sock"
network = "azure-iot-edge"
Loading

0 comments on commit 6368eb6

Please sign in to comment.