From 7fe82232436c55cbdd0817140329b55345a2952f Mon Sep 17 00:00:00 2001 From: Erikson Tung Date: Tue, 22 Oct 2019 16:15:13 -0700 Subject: [PATCH 1/3] api: create new modeled type Identifier for container names Creates a new modeled type named Identifier for string used to identify container names which might be used to create files/directories --- workspaces/api/apiserver/src/model.rs | 4 +- workspaces/api/apiserver/src/modeled_types.rs | 100 ++++++++++++++++++ workspaces/api/host-containers/src/main.rs | 4 +- 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/workspaces/api/apiserver/src/model.rs b/workspaces/api/apiserver/src/model.rs index 762c4448c91..95e5135ae98 100644 --- a/workspaces/api/apiserver/src/model.rs +++ b/workspaces/api/apiserver/src/model.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::net::Ipv4Addr; -use crate::modeled_types::{SingleLineString, ValidBase64}; +use crate::modeled_types::{SingleLineString, ValidBase64, Identifier}; ///// Primary user-visible settings @@ -32,7 +32,7 @@ pub struct Settings { pub updates: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub host_containers: Option>, + pub host_containers: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub ntp: Option, diff --git a/workspaces/api/apiserver/src/modeled_types.rs b/workspaces/api/apiserver/src/modeled_types.rs index 23d7a7f6a86..ad433c6e0dd 100644 --- a/workspaces/api/apiserver/src/modeled_types.rs +++ b/workspaces/api/apiserver/src/modeled_types.rs @@ -205,3 +205,103 @@ mod test_single_line_string { // paragraph separator } } + +// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= + +/// Identifier can only be created by deserializing from a string that contains +/// ASCII alphanumeric characters, plus hyphens, which we use as our standard word separator +/// character in user-facing identifiers. It stores the original form and makes it accessible +/// through standard traits. Its purpose is to validate input for identifiers like container names +/// that might be used to create files/directories. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct Identifier { + inner: String, +} + +impl<'de> Deserialize<'de> for Identifier { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let original = String::deserialize(deserializer)?; + + if !original + .chars() + .all(|c| (c.is_ascii() && c.is_alphanumeric()) || c == '-') + { + Err(D::Error::custom(format!( + "Identifiers may only contain ASCII alphanumerics plus hyphens; received '{}'", + original, + ))) + } else { + Ok(Identifier { inner: original }) + } + } +} + +/// Serialize the original string back out. +impl Serialize for Identifier { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.inner) + } +} + +impl Deref for Identifier { + type Target = str; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Borrow for Identifier { + fn borrow(&self) -> &String { + &self.inner + } +} + +impl Borrow for Identifier { + fn borrow(&self) -> &str { + &self.inner + } +} + +impl AsRef for Identifier { + fn as_ref(&self) -> &str { + &self.inner + } +} + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} + +#[cfg(test)] +mod test_valid_identifier { + use super::Identifier; + + #[test] + fn valid_identifier() { + assert!(serde_json::from_str::("\"hello-world\"").is_ok()); + assert!(serde_json::from_str::("\"helloworld\"").is_ok()); + assert!(serde_json::from_str::("\"123321hello\"").is_ok()); + assert!(serde_json::from_str::("\"hello-1234\"").is_ok()); + assert!(serde_json::from_str::("\"--------\"").is_ok()); + assert!(serde_json::from_str::("\"11111111\"").is_ok()); + } + + #[test] + fn invalid_identifier() { + assert!(serde_json::from_str::("\"../\"").is_err()); + assert!(serde_json::from_str::("\"{}\"").is_err()); + assert!(serde_json::from_str::("\"hello|World\"").is_err()); + assert!(serde_json::from_str::("\"hello\nWorld\"").is_err()); + assert!(serde_json::from_str::("\"hello_world\"").is_err()); + assert!(serde_json::from_str::("\"タール\"").is_err()); + assert!(serde_json::from_str::("\"💝\"").is_err()); + } +} diff --git a/workspaces/api/host-containers/src/main.rs b/workspaces/api/host-containers/src/main.rs index c376623eab2..ee1b3b0fc8e 100644 --- a/workspaces/api/host-containers/src/main.rs +++ b/workspaces/api/host-containers/src/main.rs @@ -18,7 +18,7 @@ use std::path::{Path, PathBuf}; use std::process::{self, Command}; use apiserver::model; -use apiserver::modeled_types::SingleLineString; +use apiserver::modeled_types::Identifier; #[macro_use] extern crate tracing; @@ -111,7 +111,7 @@ mod error { type Result = std::result::Result; /// Query the API for the currently defined host containers -fn get_host_containers

(socket_path: P) -> Result> +fn get_host_containers

(socket_path: P) -> Result> where P: AsRef, { From 37ef1280bcb1ccc98075637c322b3c957bcb1adf Mon Sep 17 00:00:00 2001 From: Erikson Tung Date: Tue, 22 Oct 2019 16:24:37 -0700 Subject: [PATCH 2/3] host-ctr: adds persistent storage for host containers Creates a persistent storage location for containers under /local/host-containers/CONTAINER-NAME and mapped into the container under /.thar/host-containers/CONTAINER-NAME --- packages/workspaces/host-containers@.service | 3 +++ workspaces/host-ctr/cmd/host-ctr/main.go | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packages/workspaces/host-containers@.service b/packages/workspaces/host-containers@.service index 6f29c85da68..05573f3ff0a 100644 --- a/packages/workspaces/host-containers@.service +++ b/packages/workspaces/host-containers@.service @@ -6,6 +6,9 @@ BindsTo=host-containerd.service [Service] Type=simple EnvironmentFile=/etc/host-containers/%i.env +Environment=LOCAL_DIR=/local +# Create directories for container persistent storage +ExecStartPre=/usr/bin/mkdir -m 1777 -p ${LOCAL_DIR}/host-containers/%i ExecStart=/usr/bin/host-ctr -ctr-id='%i' -source='${CTR_SOURCE}' -superpowered='${CTR_SUPERPOWERED}' Restart=always RestartSec=10 diff --git a/workspaces/host-ctr/cmd/host-ctr/main.go b/workspaces/host-ctr/cmd/host-ctr/main.go index 68dcb848e97..02cad045276 100644 --- a/workspaces/host-ctr/cmd/host-ctr/main.go +++ b/workspaces/host-ctr/cmd/host-ctr/main.go @@ -135,6 +135,12 @@ func _main() int { Options: []string{"bind", "ro"}, Destination: "/usr/local/bin/apiclient", Source: "/usr/bin/apiclient", + }, + // Mount in the persistent storage location for this container + { + Options: []string{"rbind", "rw"}, + Destination: "/.thar/host-containers/" + targetCtr, + Source: "/local/host-containers/" + targetCtr, }}), withSuperpowered(superpowered), ) From b13e2cb40729ca19a12f4b527dd972425ad58278 Mon Sep 17 00:00:00 2001 From: Erikson Tung Date: Tue, 22 Oct 2019 16:27:12 -0700 Subject: [PATCH 3/3] admin-ctr: generate the host ssh keys in container persistent storage Generates host ssh keys in admin container persistent storage under `/.thar/host-containers/admin/etc/ssh` --- extras/admin-container/sshd_config | 6 +++--- extras/admin-container/start_admin_sshd.sh | 4 ++-- workspaces/api/storewolf/defaults.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extras/admin-container/sshd_config b/extras/admin-container/sshd_config index 3caba695175..7a56e1b25f1 100644 --- a/extras/admin-container/sshd_config +++ b/extras/admin-container/sshd_config @@ -1,6 +1,6 @@ -HostKey /etc/ssh/ssh_host_rsa_key -HostKey /etc/ssh/ssh_host_ecdsa_key -HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /.thar/host-containers/admin/etc/ssh/ssh_host_rsa_key +HostKey /.thar/host-containers/admin/etc/ssh/ssh_host_ecdsa_key +HostKey /.thar/host-containers/admin/etc/ssh/ssh_host_ed25519_key PasswordAuthentication no diff --git a/extras/admin-container/start_admin_sshd.sh b/extras/admin-container/start_admin_sshd.sh index 9e8afa4ca55..f5d5670a55a 100644 --- a/extras/admin-container/start_admin_sshd.sh +++ b/extras/admin-container/start_admin_sshd.sh @@ -3,7 +3,7 @@ set -e mkdir -p /home/ec2-user/.ssh/ chmod 700 /home/ec2-user/.ssh/ -ssh_host_key_dir="/etc/ssh" +ssh_host_key_dir="/.thar/host-containers/admin/etc/ssh" ssh_config_dir="/home/ec2-user/.ssh" # Populate authorized_keys with all the public keys found in instance meta-data @@ -39,6 +39,7 @@ fi chown ec2-user -R "${ssh_config_dir}" # Generate the server keys +mkdir -p "${ssh_host_key_dir}" for key in rsa ecdsa ed25519; do # If both of the keys exist, don't overwrite them if [ -s "${ssh_host_key_dir}/ssh_host_${key}_key" ] && [ -s "${ssh_host_key_dir}/ssh_host_${key}_key.pub" ]; then @@ -49,7 +50,6 @@ for key in rsa ecdsa ed25519; do rm -rf \ "${ssh_host_key_dir}/ssh_host_${key}_key" \ "${ssh_host_key_dir}/ssh_host_${key}_key.pub" - # Generate new host keys. This would happen every time this script is invoked when we start the admin container. if ssh-keygen -t "${key}" -f "${ssh_host_key_dir}/ssh_host_${key}_key" -q -N ""; then chmod 600 "${ssh_host_key_dir}/ssh_host_${key}_key" chmod 644 "${ssh_host_key_dir}/ssh_host_${key}_key.pub" diff --git a/workspaces/api/storewolf/defaults.toml b/workspaces/api/storewolf/defaults.toml index f930837f20d..c76e05fb8fb 100644 --- a/workspaces/api/storewolf/defaults.toml +++ b/workspaces/api/storewolf/defaults.toml @@ -97,7 +97,7 @@ val = "bork seed" [settings.host-containers.admin] enabled = false -source = "328549459982.dkr.ecr.us-west-2.amazonaws.com/thar-admin:v0.1" +source = "328549459982.dkr.ecr.us-west-2.amazonaws.com/thar-admin:v0.2" superpowered = true [settings.host-containers.control]