Skip to content

Commit 2d9afc3

Browse files
authored
Merge pull request #187 from Dstack-TEE/kms-clear-cache
kms: Add RPC to clear image cache
2 parents 14f630e + 722bd50 commit 2d9afc3

File tree

7 files changed

+75
-8
lines changed

7 files changed

+75
-8
lines changed

kms/dstack-app/compose-dev.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ services:
6666
- /var/run/dstack.sock:/var/run/dstack.sock
6767
environment:
6868
- IMAGE_DOWNLOAD_URL=${IMAGE_DOWNLOAD_URL}
69+
- ADMIN_TOKEN_HASH=${ADMIN_TOKEN_HASH}
6970
ports:
7071
- 8000:8000
7172
depends_on:

kms/dstack-app/deploy-to-vmm.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ GIT_REV=HEAD
4040
4141
# The DStack OS image name to use for the KMS app
4242
OS_IMAGE=dstack-0.5.0
43+
44+
# The admin token for the KMS app
45+
ADMIN_TOKEN=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
4346
EOF
4447
echo "Please edit the .env file and set the required variables, then run this script again."
4548
exit 1
@@ -69,6 +72,8 @@ COMPOSE_TMP=$(mktemp)
6972

7073
GIT_REV=$(git rev-parse $GIT_REV)
7174

75+
ADMIN_TOKEN_HASH=$(echo -n $ADMIN_TOKEN | sha256sum | cut -d' ' -f1)
76+
7277
cp compose-dev.yaml "$COMPOSE_TMP"
7378

7479
subvar() {
@@ -79,6 +84,7 @@ subvar ETH_RPC_URL
7984
subvar KMS_CONTRACT_ADDR
8085
subvar GIT_REV
8186
subvar IMAGE_DOWNLOAD_URL
87+
subvar ADMIN_TOKEN_HASH
8288

8389
echo "Docker compose file:"
8490
cat "$COMPOSE_TMP"

kms/dstack-app/entrypoint.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
set -e
33

44
cat <<EOF > ./kms.toml
5+
[core]
6+
admin_token_hash = "${ADMIN_TOKEN_HASH}"
7+
58
[core.image]
69
verify = true
710
cache_dir = "./images"

kms/kms.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mandatory = false
2121
[core]
2222
cert_dir = "/etc/kms/certs"
2323
subject_postfix = ".dstack"
24+
admin_token_hash = ""
2425

2526
[core.image]
2627
verify = true

kms/rpc/proto/kms_rpc.proto

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ service KMS {
8989
rpc GetTempCaCert(google.protobuf.Empty) returns (GetTempCaCertResponse);
9090
// Sign a certificate
9191
rpc SignCert(SignCertRequest) returns (SignCertResponse);
92+
// Clear the image cache
93+
rpc ClearImageCache(ClearImageCacheRequest) returns (google.protobuf.Empty);
94+
}
95+
96+
message ClearImageCacheRequest {
97+
string token = 1;
98+
string image_hash = 2;
99+
string config_hash = 3;
92100
}
93101

94102
message BootstrapRequest {

kms/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub(crate) struct KmsConfig {
3434
pub auth_api: AuthApi,
3535
pub onboard: OnboardConfig,
3636
pub image: ImageConfig,
37+
#[serde(with = "serde_human_bytes")]
38+
pub admin_token_hash: Vec<u8>,
3739
}
3840

3941
impl KmsConfig {

kms/src/main_service.rs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
use std::{ffi::OsStr, path::Path, sync::Arc};
1+
use std::{
2+
ffi::OsStr,
3+
path::{Path, PathBuf},
4+
sync::Arc,
5+
};
26

37
use anyhow::{bail, Context, Result};
48
use dstack_kms_rpc::{
59
kms_server::{KmsRpc, KmsServer},
6-
AppId, AppKeyResponse, GetAppKeyRequest, GetKmsKeyRequest, GetMetaResponse,
7-
GetTempCaCertResponse, KmsKeyResponse, KmsKeys, PublicKeyResponse, SignCertRequest,
8-
SignCertResponse,
10+
AppId, AppKeyResponse, ClearImageCacheRequest, GetAppKeyRequest, GetKmsKeyRequest,
11+
GetMetaResponse, GetTempCaCertResponse, KmsKeyResponse, KmsKeys, PublicKeyResponse,
12+
SignCertRequest, SignCertResponse,
913
};
1014
use dstack_types::VmConfig;
1115
use fs_err as fs;
@@ -149,8 +153,41 @@ impl RpcHandler {
149153
.await
150154
}
151155

156+
fn image_cache_dir(&self) -> PathBuf {
157+
self.state.config.image.cache_dir.join("images")
158+
}
159+
160+
fn mr_cache_dir(&self) -> PathBuf {
161+
self.state.config.image.cache_dir.join("computed")
162+
}
163+
164+
fn remove_cache(&self, parent_dir: &PathBuf, sub_dir: &str) -> Result<()> {
165+
if sub_dir.is_empty() {
166+
return Ok(());
167+
}
168+
if sub_dir == "all" {
169+
fs::remove_dir_all(parent_dir)?;
170+
} else {
171+
let path = parent_dir.join(sub_dir);
172+
if path.is_dir() {
173+
fs::remove_dir_all(path)?;
174+
} else {
175+
fs::remove_file(path)?;
176+
}
177+
}
178+
Ok(())
179+
}
180+
181+
fn ensure_admin(&self, token: &str) -> Result<()> {
182+
let token_hash = sha2::Sha256::new_with_prefix(token).finalize();
183+
if token_hash.as_slice() != self.state.config.admin_token_hash.as_slice() {
184+
bail!("Invalid token");
185+
}
186+
Ok(())
187+
}
188+
152189
fn get_cached_mrs(&self, key: &str) -> Result<Mrs> {
153-
let path = self.state.config.image.cache_dir.join("computed").join(key);
190+
let path = self.mr_cache_dir().join(key);
154191
if !path.exists() {
155192
bail!("Cached MRs not found");
156193
}
@@ -161,7 +198,7 @@ impl RpcHandler {
161198
}
162199

163200
fn cache_mrs(&self, key: &str, mrs: &Mrs) -> Result<()> {
164-
let path = self.state.config.image.cache_dir.join("computed").join(key);
201+
let path = self.mr_cache_dir().join(key);
165202
fs::create_dir_all(path.parent().unwrap()).context("Failed to create cache directory")?;
166203
safe_write::safe_write(
167204
&path,
@@ -194,7 +231,7 @@ impl RpcHandler {
194231
}
195232

196233
// Create a directory for the image if it doesn't exist
197-
let image_dir = self.state.config.image.cache_dir.join(&hex_os_image_hash);
234+
let image_dir = self.image_cache_dir().join(&hex_os_image_hash);
198235
// Check if metadata.json exists, if not download the image
199236
let metadata_path = image_dir.join("metadata.json");
200237
if !metadata_path.exists() {
@@ -253,7 +290,7 @@ impl RpcHandler {
253290
.replace("{OS_IMAGE_HASH}", hex_os_image_hash);
254291

255292
// Create a temporary directory for extraction within the cache directory
256-
let cache_dir = self.state.config.image.cache_dir.join("tmp");
293+
let cache_dir = self.image_cache_dir().join("tmp");
257294
fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?;
258295
let auto_delete_temp_dir = tempfile::Builder::new()
259296
.prefix("tmp-download-")
@@ -578,6 +615,15 @@ impl KmsRpc for RpcHandler {
578615
],
579616
})
580617
}
618+
619+
async fn clear_image_cache(self, request: ClearImageCacheRequest) -> Result<()> {
620+
self.ensure_admin(&request.token)?;
621+
self.remove_cache(&self.image_cache_dir(), &request.image_hash)
622+
.context("Failed to clear image cache")?;
623+
self.remove_cache(&self.mr_cache_dir(), &request.config_hash)
624+
.context("Failed to clear MR cache")?;
625+
Ok(())
626+
}
581627
}
582628

583629
impl RpcCall<KmsState> for RpcHandler {

0 commit comments

Comments
 (0)