Skip to content

Commit 532e9b4

Browse files
authored
Merge pull request #180 from Dstack-TEE/gw-app-auth
gw: Add authority to run
2 parents 3cb1f2d + ad2beec commit 532e9b4

File tree

11 files changed

+119
-11
lines changed

11 files changed

+119
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
generated/
66
node_modules/
77
/.cargo
8+
.venv

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gateway/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ http-client = { workspace = true, features = ["prpc"] }
4242
sha2.workspace = true
4343
dstack-types.workspace = true
4444
serde-duration.workspace = true
45+
reqwest = { workspace = true, features = ["json"] }
4546

4647
[target.'cfg(unix)'.dependencies]
4748
nix = { workspace = true, features = ["resource"] }

gateway/dstack-app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/.app-compose.json
2+
/.prelaunch.sh
23
/.env
34
/.venv
45
/.app_env

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

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
#!/bin/bash
22

3+
APP_COMPOSE_FILE=""
4+
5+
usage() {
6+
echo "Usage: $0 [-c <app compose file>]"
7+
echo " -c App compose file"
8+
}
9+
10+
while getopts "c:h" opt; do
11+
case $opt in
12+
c)
13+
APP_COMPOSE_FILE=$OPTARG
14+
;;
15+
h)
16+
usage
17+
exit 0
18+
;;
19+
\?)
20+
usage
21+
exit 1
22+
;;
23+
esac
24+
done
25+
326
# Check if .env exists
427
if [ -f ".env" ]; then
528
# Load variables from .env
@@ -57,6 +80,9 @@ GATEWAY_SERVING_ADDR=0.0.0.0:9204
5780
GUEST_AGENT_ADDR=127.0.0.1:9206
5881
WG_ADDR=0.0.0.0:9202
5982
83+
# The token used to launch the App
84+
APP_LAUNCH_TOKEN=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
85+
6086
EOF
6187
echo "Please edit the .env file and set the required variables, then run this script again."
6288
exit 1
@@ -72,6 +98,7 @@ required_env_vars=(
7298
"WG_ADDR"
7399
"GATEWAY_APP_ID"
74100
"MY_URL"
101+
"APP_LAUNCH_TOKEN"
75102
# "BOOTNODE_URL"
76103
)
77104

@@ -112,17 +139,36 @@ WG_ENDPOINT=$PUBLIC_IP:$WG_PORT
112139
MY_URL=$MY_URL
113140
BOOTNODE_URL=$BOOTNODE_URL
114141
SUBNET_INDEX=$SUBNET_INDEX
142+
APP_LAUNCH_TOKEN=$APP_LAUNCH_TOKEN
115143
EOF
116144

117-
$CLI compose \
118-
--docker-compose "$COMPOSE_TMP" \
119-
--name dstack-gateway \
120-
--kms \
121-
--env-file .app_env \
122-
--public-logs \
123-
--public-sysinfo \
124-
--no-instance-id \
125-
--output .app-compose.json
145+
if [ -n "$APP_COMPOSE_FILE" ]; then
146+
cp "$APP_COMPOSE_FILE" .app-compose.json
147+
else
148+
149+
EXPECTED_TOKEN_HASH=$(echo -n "$APP_LAUNCH_TOKEN" | sha256sum | cut -d' ' -f1)
150+
cat >.prelaunch.sh <<EOF
151+
ACTUAL_TOKEN_HASH=\$(echo -n "\$APP_LAUNCH_TOKEN" | sha256sum | cut -d' ' -f1)
152+
if [ "$EXPECTED_TOKEN_HASH" != "\$ACTUAL_TOKEN_HASH" ]; then
153+
echo "Error: Incorrect APP_LAUNCH_TOKEN, please make sure set the correct APP_LAUNCH_TOKEN in env"
154+
reboot
155+
exit 1
156+
else
157+
echo "APP_LAUNCH_TOKEN checked OK"
158+
fi
159+
EOF
160+
161+
$CLI compose \
162+
--docker-compose "$COMPOSE_TMP" \
163+
--name dstack-gateway \
164+
--kms \
165+
--env-file .app_env \
166+
--public-logs \
167+
--public-sysinfo \
168+
--no-instance-id \
169+
--prelaunch-script .prelaunch.sh \
170+
--output .app-compose.json
171+
fi
126172

127173
# Remove the temporary file as it is no longer needed
128174
rm "$COMPOSE_TMP"

gateway/gateway.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ set_ulimit = true
1414
rpc_domain = ""
1515
run_in_dstack = true
1616

17+
[core.auth]
18+
enabled = false
19+
url = "http://localhost/app-auth"
20+
timeout = "5s"
21+
1722
[core.admin]
1823
enabled = false
1924
port = 8011

gateway/src/config.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ pub struct Config {
121121
pub admin: AdminConfig,
122122
pub run_in_dstack: bool,
123123
pub sync: SyncConfig,
124+
pub auth: AuthConfig,
125+
}
126+
127+
#[derive(Debug, Clone, Deserialize)]
128+
pub struct AuthConfig {
129+
pub enabled: bool,
130+
pub url: String,
131+
#[serde(with = "serde_duration")]
132+
pub timeout: Duration,
124133
}
125134

126135
impl Config {

gateway/src/main_service.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::{
66
};
77

88
use anyhow::{bail, Context, Result};
9+
use auth_client::AuthClient;
910
use certbot::{CertBot, WorkDir};
1011
use cmd_lib::run_cmd as cmd;
1112
use dstack_gateway_rpc::{
@@ -33,13 +34,16 @@ use crate::{
3334

3435
mod sync_client;
3536

37+
mod auth_client;
38+
3639
#[derive(Clone)]
3740
pub struct Proxy {
3841
pub(crate) config: Arc<Config>,
3942
pub(crate) certbot: Option<Arc<CertBot>>,
4043
my_app_id: Option<Vec<u8>>,
4144
sync_tx: Sender<SyncEvent>,
4245
inner: Arc<Mutex<ProxyState>>,
46+
auth_client: Arc<AuthClient>,
4347
}
4448

4549
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -102,12 +106,14 @@ impl Proxy {
102106
let (sync_tx, sync_rx) = mpsc::channel(1);
103107
start_sync_task(Arc::downgrade(&inner), config.clone(), sync_rx);
104108
let certbot = start_certbot_task(&config).await?;
109+
let auth_client = Arc::new(AuthClient::new(config.auth.clone()));
105110
Ok(Self {
106111
config,
107112
inner,
108113
certbot,
109114
my_app_id,
110115
sync_tx,
116+
auth_client,
111117
})
112118
}
113119
}
@@ -607,6 +613,11 @@ impl GatewayRpc for RpcHandler {
607613
let app_info = ra
608614
.decode_app_info(false)
609615
.context("failed to decode app-info from attestation")?;
616+
self.state
617+
.auth_client
618+
.ensure_app_authorized(&app_info)
619+
.await
620+
.context("App authorization failed")?;
610621
let app_id = hex::encode(&app_info.app_id);
611622
let instance_id = hex::encode(&app_info.instance_id);
612623

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use crate::config::AuthConfig;
2+
use anyhow::{Context, Result};
3+
use ra_tls::attestation::AppInfo;
4+
use reqwest::Client;
5+
6+
pub(crate) struct AuthClient {
7+
config: AuthConfig,
8+
client: Client,
9+
}
10+
11+
impl AuthClient {
12+
pub(crate) fn new(config: AuthConfig) -> Self {
13+
Self {
14+
config,
15+
client: reqwest::Client::new(),
16+
}
17+
}
18+
19+
pub(crate) async fn ensure_app_authorized(&self, app_info: &AppInfo) -> Result<()> {
20+
if !self.config.enabled {
21+
return Ok(());
22+
}
23+
let req = self.client.post(&self.config.url).json(app_info).send();
24+
let res = tokio::time::timeout(self.config.timeout, req)
25+
.await
26+
.context("Auth timeout")?
27+
.context("Failed to send request")?;
28+
res.error_for_status().context("Request failed")?;
29+
Ok(())
30+
}
31+
}

vmm/src/vmm-cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ def create_app_compose(self,
426426
"secure_time": True,
427427
}
428428
if prelaunch_script:
429-
app_compose["prelaunch_script"] = prelaunch_script
429+
app_compose["pre_launch_script"] = open(prelaunch_script, 'rb').read().decode('utf-8')
430430

431431
compose_file = json.dumps(app_compose, indent=4).encode('utf-8')
432432
compose_hash = hashlib.sha256(compose_file).hexdigest()

0 commit comments

Comments
 (0)