Skip to content

Commit

Permalink
Pass SSH public keys through to cloud-init (#974)
Browse files Browse the repository at this point in the history
  • Loading branch information
plotnick authored May 5, 2022
1 parent f3012a5 commit c979a5c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 7 deletions.
15 changes: 9 additions & 6 deletions nexus/src/cidata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ use uuid::Uuid;
pub const MAX_USER_DATA_BYTES: usize = 32 * 1024; // 32 KiB

impl Instance {
pub fn generate_cidata(&self) -> Result<Vec<u8>, Error> {
pub fn generate_cidata(
&self,
public_keys: &[String],
) -> Result<Vec<u8>, Error> {
// cloud-init meta-data is YAML, but YAML is a strict superset of JSON.
let meta_data = serde_json::to_vec(&MetaData {
instance_id: self.id(),
local_hostname: &self.runtime().hostname,
public_keys: &[], // TODO
public_keys,
})
.map_err(|_| Error::internal_error("failed to serialize meta-data"))?;
let cidata =
Expand Down Expand Up @@ -67,10 +70,10 @@ fn build_vfat(meta_data: &[u8], user_data: &[u8]) -> io::Result<Vec<u8>> {
let root_dir = fs.root_dir();
for (file, data) in [("meta-data", meta_data), ("user-data", user_data)]
{
if !data.is_empty() {
let mut file = root_dir.create_file(file)?;
file.write_all(data)?;
}
// Cloud-init requires the files `meta-data` and `user-data`
// to be present, even if empty.
let mut file = root_dir.create_file(file)?;
file.write_all(data)?;
}
}

Expand Down
30 changes: 29 additions & 1 deletion nexus/src/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ pub(crate) const MAX_DISKS_PER_INSTANCE: u32 = 8;

pub(crate) const MAX_NICS_PER_INSTANCE: u32 = 8;

pub(crate) const MAX_KEYS_PER_INSTANCE: u32 = 8;

/// Manages an Oxide fleet -- the heart of the control plane
pub struct Nexus {
/// uuid for this nexus instance.
Expand Down Expand Up @@ -1852,6 +1854,32 @@ impl Nexus {
.derive_guest_network_interface_info(&opctx, &authz_instance)
.await?;

// Gather the SSH public keys of the actor make the request so
// that they may be injected into the new image via cloud-init.
// TODO-security: this should be replaced with a lookup based on
// on `SiloUser` role assignments once those are in place.
let actor = opctx.authn.actor_required()?;
let (.., authz_user) = LookupPath::new(opctx, &self.db_datastore)
.silo_user_id(actor.actor_id())
.lookup_for(authz::Action::ListChildren)
.await?;
let public_keys = self
.db_datastore
.ssh_keys_list(
opctx,
&authz_user,
&DataPageParams {
marker: None,
direction: dropshot::PaginationOrder::Ascending,
limit: std::num::NonZeroU32::new(MAX_KEYS_PER_INSTANCE)
.unwrap(),
},
)
.await?
.into_iter()
.map(|ssh_key| ssh_key.public_key)
.collect::<Vec<String>>();

// Ask the sled agent to begin the state change. Then update the
// database to reflect the new intermediate state. If this update is
// not the newest one, that's fine. That might just mean the sled agent
Expand All @@ -1864,7 +1892,7 @@ impl Nexus {
nics,
disks: disk_reqs,
cloud_init_bytes: Some(base64::encode(
db_instance.generate_cidata()?,
db_instance.generate_cidata(&public_keys)?,
)),
};

Expand Down

0 comments on commit c979a5c

Please sign in to comment.