From 3284bdcc2853c608a38046214644180bd6836fd3 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:09:18 +0200 Subject: [PATCH 01/15] feat: refactor deployer to run locally without auth --- Cargo.toml | 6 +- auth/src/user.rs | 28 +---- cargo-shuttle/Cargo.toml | 4 - common/src/claims.rs | 36 ++++++ deployer/Cargo.toml | 2 +- deployer/src/args.rs | 4 + deployer/src/handlers/local.rs | 13 +++ deployer/src/handlers/mod.rs | 197 +++++++++++++++++++-------------- deployer/src/lib.rs | 18 ++- 9 files changed, 184 insertions(+), 124 deletions(-) create mode 100644 deployer/src/handlers/local.rs diff --git a/Cargo.toml b/Cargo.toml index 5575d6a86..f3b849121 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,7 @@ members = [ exclude = [ "e2e", "examples", - "resources/aws-rds", - "resources/persist", - "resources/secrets", - "resources/shared-db", - "resources/static-folder", + "resources", "services", ] diff --git a/auth/src/user.rs b/auth/src/user.rs index 80453c7e0..f5b0c75f9 100644 --- a/auth/src/user.rs +++ b/auth/src/user.rs @@ -185,33 +185,11 @@ pub enum AccountTier { impl From for Vec { fn from(tier: AccountTier) -> Self { - let mut base = vec![ - Scope::Deployment, - Scope::DeploymentPush, - Scope::Logs, - Scope::Service, - Scope::ServiceCreate, - Scope::Project, - Scope::ProjectCreate, - Scope::Resources, - Scope::ResourcesWrite, - Scope::Secret, - Scope::SecretWrite, - ]; - if tier == AccountTier::Admin { - base.append(&mut vec![ - Scope::User, - Scope::UserCreate, - Scope::AcmeCreate, - Scope::CustomDomainCreate, - Scope::CustomDomainCertificateRenew, - Scope::GatewayCertificateRenew, - Scope::Admin, - ]); + Scope::admin() + } else { + Scope::base() } - - base } } diff --git a/cargo-shuttle/Cargo.toml b/cargo-shuttle/Cargo.toml index 4cc427f6e..58e29cd5f 100644 --- a/cargo-shuttle/Cargo.toml +++ b/cargo-shuttle/Cargo.toml @@ -64,10 +64,6 @@ features = ["models"] [dependencies.shuttle-proto] workspace = true -[dependencies.shuttle-secrets] -version = "0.14.0" -path = "../resources/secrets" - [dependencies.shuttle-service] workspace = true features = ["builder"] diff --git a/common/src/claims.rs b/common/src/claims.rs index f5ca7827d..43083c8d7 100644 --- a/common/src/claims.rs +++ b/common/src/claims.rs @@ -88,6 +88,42 @@ pub enum Scope { Admin, } +impl Scope { + /// Standard scopes for a new user in the free tier. + pub fn base() -> Vec { + vec![ + Scope::Deployment, + Scope::DeploymentPush, + Scope::Logs, + Scope::Service, + Scope::ServiceCreate, + Scope::Project, + Scope::ProjectCreate, + Scope::Resources, + Scope::ResourcesWrite, + Scope::Secret, + Scope::SecretWrite, + ] + } + + /// All scopes for an admin user. + pub fn admin() -> Vec { + let mut base = Scope::base(); + + base.extend(vec![ + Scope::User, + Scope::UserCreate, + Scope::AcmeCreate, + Scope::CustomDomainCreate, + Scope::CustomDomainCertificateRenew, + Scope::GatewayCertificateRenew, + Scope::Admin, + ]); + + base + } +} + #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] pub struct Claim { /// Expiration time (as UTC timestamp). diff --git a/deployer/Cargo.toml b/deployer/Cargo.toml index deba2cb8b..3e5e5eaa3 100644 --- a/deployer/Cargo.toml +++ b/deployer/Cargo.toml @@ -58,7 +58,7 @@ uuid = { workspace = true, features = ["v4"] } [dependencies.shuttle-common] workspace = true -features = ["backend", "models"] +features = ["backend", "models", "claims"] [dependencies.shuttle-proto] workspace = true diff --git a/deployer/src/args.rs b/deployer/src/args.rs index 73211faa8..bc2d37794 100644 --- a/deployer/src/args.rs +++ b/deployer/src/args.rs @@ -50,4 +50,8 @@ pub struct Args { /// Uri to folder to store all artifacts #[clap(long, default_value = "/tmp")] pub artifacts_path: PathBuf, + + /// Run deployer without auth locally + #[arg(long)] + pub local: bool, } diff --git a/deployer/src/handlers/local.rs b/deployer/src/handlers/local.rs new file mode 100644 index 000000000..99255c5a2 --- /dev/null +++ b/deployer/src/handlers/local.rs @@ -0,0 +1,13 @@ +use axum::http::Request; +use shuttle_common::claims::{Claim, Scope}; + +/// This middleware sets an admin token in the claim extension of every request, so we can +/// develop deployer locally without starting it with the gateway and without proxying commands +/// through gateway. +pub async fn set_admin_claim(mut request: Request) -> Request { + let claim = Claim::new("admin".to_string(), Scope::admin()); + + request.extensions_mut().insert::(claim); + + request +} diff --git a/deployer/src/handlers/mod.rs b/deployer/src/handlers/mod.rs index 4082715da..02fbd769c 100644 --- a/deployer/src/handlers/mod.rs +++ b/deployer/src/handlers/mod.rs @@ -4,7 +4,7 @@ use axum::extract::ws::{self, WebSocket}; use axum::extract::{Extension, Path, Query}; use axum::handler::Handler; use axum::headers::HeaderMapExt; -use axum::middleware::from_extractor; +use axum::middleware::{from_extractor, map_request}; use axum::routing::{get, post, Router}; use axum::{extract::BodyStream, Json}; use bytes::BufMut; @@ -34,8 +34,9 @@ use crate::persistence::{Deployment, Log, Persistence, ResourceManager, SecretGe use std::collections::HashMap; -pub use {self::error::Error, self::error::Result}; +pub use {self::error::Error, self::error::Result, self::local::set_admin_claim}; +mod local; mod project; #[derive(OpenApi)] @@ -72,88 +73,118 @@ mod project; )] pub struct ApiDoc; -pub async fn make_router( - persistence: Persistence, - deployment_manager: DeploymentManager, - proxy_fqdn: FQDN, - admin_secret: String, - auth_uri: Uri, +#[derive(Clone)] +pub struct RouterBuilder { + router: Router, project_name: ProjectName, -) -> Router { - Router::new() - // TODO: The `/swagger-ui` responds with a 303 See Other response which is followed in - // browsers but leads to 404 Not Found. This must be investigated. - .merge(SwaggerUi::new("/projects/:project_name/swagger-ui").url( - "/projects/:project_name/api-docs/openapi.json", - ApiDoc::openapi(), - )) - .route( - "/projects/:project_name/services", - get(get_services.layer(ScopedLayer::new(vec![Scope::Service]))), - ) - .route( - "/projects/:project_name/services/:service_name", - get(get_service.layer(ScopedLayer::new(vec![Scope::Service]))) - .post(create_service.layer(ScopedLayer::new(vec![Scope::ServiceCreate]))) - .delete(stop_service.layer(ScopedLayer::new(vec![Scope::ServiceCreate]))), - ) - .route( - "/projects/:project_name/services/:service_name/resources", - get(get_service_resources).layer(ScopedLayer::new(vec![Scope::Resources])), - ) - .route( - "/projects/:project_name/deployments", - get(get_deployments).layer(ScopedLayer::new(vec![Scope::Service])), - ) - .route( - "/projects/:project_name/deployments/:deployment_id", - get(get_deployment.layer(ScopedLayer::new(vec![Scope::Deployment]))) - .delete(delete_deployment.layer(ScopedLayer::new(vec![Scope::DeploymentPush]))), - ) - .route( - "/projects/:project_name/ws/deployments/:deployment_id/logs", - get(get_logs_subscribe.layer(ScopedLayer::new(vec![Scope::Logs]))), - ) - .route( - "/projects/:project_name/deployments/:deployment_id/logs", - get(get_logs.layer(ScopedLayer::new(vec![Scope::Logs]))), - ) - .route( - "/projects/:project_name/secrets/:service_name", - get(get_secrets.layer(ScopedLayer::new(vec![Scope::Secret]))), - ) - .route( - "/projects/:project_name/clean", - post(clean_project.layer(ScopedLayer::new(vec![Scope::DeploymentPush]))), - ) - .layer(Extension(persistence)) - .layer(Extension(deployment_manager)) - .layer(Extension(proxy_fqdn)) - .layer(JwtAuthenticationLayer::new(AuthPublicKey::new(auth_uri))) - .layer(AdminSecretLayer::new(admin_secret)) - // This route should be below the auth bearer since it does not need authentication - .route("/projects/:project_name/status", get(get_status)) - .route_layer(from_extractor::()) - .layer( - TraceLayer::new(|request| { - let account_name = request - .headers() - .typed_get::() - .unwrap_or_default(); - - request_span!( - request, - account.name = account_name.0, - request.params.project_name = field::Empty, - request.params.service_name = field::Empty, - request.params.deployment_id = field::Empty, - ) - }) - .with_propagation() - .build(), - ) - .route_layer(from_extractor::()) - .layer(Extension(project_name)) +} + +impl RouterBuilder { + pub fn new( + persistence: Persistence, + deployment_manager: DeploymentManager, + proxy_fqdn: FQDN, + project_name: ProjectName, + ) -> Self { + let router = Router::new() + // TODO: The `/swagger-ui` responds with a 303 See Other response which is followed in + // browsers but leads to 404 Not Found. This must be investigated. + .merge(SwaggerUi::new("/projects/:project_name/swagger-ui").url( + "/projects/:project_name/api-docs/openapi.json", + ApiDoc::openapi(), + )) + .route( + "/projects/:project_name/services", + get(get_services.layer(ScopedLayer::new(vec![Scope::Service]))), + ) + .route( + "/projects/:project_name/services/:service_name", + get(get_service.layer(ScopedLayer::new(vec![Scope::Service]))) + .post(create_service.layer(ScopedLayer::new(vec![Scope::ServiceCreate]))) + .delete(stop_service.layer(ScopedLayer::new(vec![Scope::ServiceCreate]))), + ) + .route( + "/projects/:project_name/services/:service_name/resources", + get(get_service_resources).layer(ScopedLayer::new(vec![Scope::Resources])), + ) + .route( + "/projects/:project_name/deployments", + get(get_deployments).layer(ScopedLayer::new(vec![Scope::Service])), + ) + .route( + "/projects/:project_name/deployments/:deployment_id", + get(get_deployment.layer(ScopedLayer::new(vec![Scope::Deployment]))) + .delete(delete_deployment.layer(ScopedLayer::new(vec![Scope::DeploymentPush]))), + ) + .route( + "/projects/:project_name/ws/deployments/:deployment_id/logs", + get(get_logs_subscribe.layer(ScopedLayer::new(vec![Scope::Logs]))), + ) + .route( + "/projects/:project_name/deployments/:deployment_id/logs", + get(get_logs.layer(ScopedLayer::new(vec![Scope::Logs]))), + ) + .route( + "/projects/:project_name/secrets/:service_name", + get(get_secrets.layer(ScopedLayer::new(vec![Scope::Secret]))), + ) + .route( + "/projects/:project_name/clean", + post(clean_project.layer(ScopedLayer::new(vec![Scope::DeploymentPush]))), + ) + .layer(Extension(persistence)) + .layer(Extension(deployment_manager)) + .layer(Extension(proxy_fqdn)); + + Self { + router, + project_name, + } + } + + pub fn with_auth_layer(mut self, auth_uri: Uri, admin_secret: String) -> Self { + let auth_public_key = AuthPublicKey::new(auth_uri); + + self.router = self + .router + .layer(JwtAuthenticationLayer::new(auth_public_key)) + .layer(AdminSecretLayer::new(admin_secret)); + + self + } + + /// Sets an admin jwt extension on every request for use when running deployer locally. + pub fn with_local_admin_layer(mut self) -> Self { + self.router = self.router.layer(map_request(set_admin_claim)); + + self + } + + pub fn into_router(self) -> Router { + self.router + .route("/projects/:project_name/status", get(get_status)) + .route_layer(from_extractor::()) + .layer( + TraceLayer::new(|request| { + let account_name = request + .headers() + .typed_get::() + .unwrap_or_default(); + + request_span!( + request, + account.name = account_name.0, + request.params.project_name = field::Empty, + request.params.service_name = field::Empty, + request.params.deployment_id = field::Empty, + ) + }) + .with_propagation() + .build(), + ) + .route_layer(from_extractor::()) + .layer(Extension(self.project_name)) + } } #[instrument(skip_all)] diff --git a/deployer/src/lib.rs b/deployer/src/lib.rs index 39bede711..fc3ce1e0d 100644 --- a/deployer/src/lib.rs +++ b/deployer/src/lib.rs @@ -12,7 +12,7 @@ pub use persistence::Persistence; use proxy::AddressGetter; pub use runtime_manager::RuntimeManager; use tokio::sync::Mutex; -use tracing::{error, info}; +use tracing::{error, info, warn}; use crate::deployment::gateway_client::GatewayClient; @@ -57,15 +57,21 @@ pub async fn start( deployment_manager.run_push(built).await; } - let router = handlers::make_router( + let builder = handlers::RouterBuilder::new( persistence, deployment_manager, args.proxy_fqdn, - args.admin_secret, - args.auth_uri, args.project, - ) - .await; + ); + + let router = if args.local { + warn!("Building deployer router with auth disabled, this should only be used for development."); + builder.with_local_admin_layer().into_router() + } else { + builder + .with_auth_layer(args.auth_uri, args.admin_secret) + .into_router() + }; info!(address=%args.api_address, "Binding to and listening at address"); From 0eb90639bedf3f49c51fdcb13b148ea1f36bb2a9 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:13:15 +0200 Subject: [PATCH 02/15] docs: update contributing deployer guide --- CONTRIBUTING.md | 46 ++++------------------------------------------ 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03f053f42..72c1dace2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -155,52 +155,14 @@ make docker-compose.rendered.yml docker compose -f docker-compose.rendered.yml up provisioner ``` -This starts the provisioner and the auth service, while preventing `gateway` from starting up. Next up we need to -insert an admin user into the `auth` state using the ID of the `auth` container and the auth CLI `init` command: +This starts the provisioner and the auth service, while preventing `gateway` from starting up. We're now ready to +start a local run of the deployer: ```bash -AUTH_CONTAINER_ID=$(docker ps -aqf "name=shuttle-auth") \ - docker exec $AUTH_CONTAINER_ID ./usr/local/bin/service \ - --state=/var/lib/shuttle-auth \ - init --name admin --key test-key +cargo run -p shuttle-deployer -- --provisioner-address http://localhost:8000 --proxy-fqdn local.rs --admin-secret test-key --local --project ``` -Before we can run commands against a local deployer, we need to get a valid JWT and set it in our -`.config/shuttle/config.toml` as our `api_key`. By running the following curl command, we will request -that our api-key in the `Authorization` header be converted to a JWT, which will be returned in the response: - -```bash -curl -H "Authorization: Bearer test-key" localhost:8008/auth/key -``` - -Now copy the `token` value (just the value, not the key) from the curl response, and write it to your shuttle -config (which will be a file named `config.toml` in a directory named `shuttle` in one of -[these places](https://docs.rs/dirs/latest/dirs/fn.config_dir.html) depending on your OS). - -```bash -# replace with the token from the previous command -echo "api_key = ''" > ~/.config/shuttle/config.toml -``` - -> Note: The JWT will expire in 15 minutes, at which point you need to run the commands again. -> If you have [`jq`](https://github.com/stedolan/jq/wiki/Installation) installed you can combine -> the two above commands into the following: -```bash -curl -s -H "Authorization: Bearer test-key" localhost:8008/auth/key \ - | jq -r '.token' \ - | read token; echo "api_key='$token'" > ~/.config/shuttle/config.toml -``` - -Finally we need to comment out the admin layer in the deployer handlers. So in `deployer/handlers/mod.rs`, -in the `make_router` function comment out this line: `.layer(AdminSecretLayer::new(admin_secret))`. - -And that's it, we're ready to start our deployer! - -```bash -cargo run -p shuttle-deployer -- --provisioner-address http://localhost:8000 --proxy-fqdn local.rs --admin-secret test-key --project -``` - -The `--admin-secret` can safely be changed to your api-key to make testing easier. While `` needs to match the name of the project that will be deployed to this deployer. This is the `Cargo.toml` or `Shuttle.toml` name for the project. +The `` needs to match the name of the project that will be deployed to this deployer. This is the `Cargo.toml` or `Shuttle.toml` name for the project. ### Using Podman instead of Docker From 10cc1ea65bd3a81be87b44ae5c5a0a8b144f391f Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 21 Apr 2023 13:34:32 +0200 Subject: [PATCH 03/15] refactor: workspace dep --- common/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index 3277b1c17..af8c00f5e 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -93,4 +93,4 @@ ring = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tower = { workspace = true, features = ["util"] } tracing-fluent-assertions = "0.3.0" -tracing-subscriber = { version = "0.3", default-features = false } +tracing-subscriber = { workspace = true, default-features = false } From d1c7721dddc22e2b4ee39f9c424539f54e71d8c9 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 21 Apr 2023 15:06:22 +0200 Subject: [PATCH 04/15] fix: bump regex to 1.8.1 to fix 1.8.0 bug --- Cargo.lock | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43015bcdd..89cd71698 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,6 +48,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "ambient-authority" version = "0.0.1" @@ -1094,7 +1103,6 @@ dependencies = [ "serde_json", "shuttle-common", "shuttle-proto", - "shuttle-secrets", "shuttle-service", "sqlx", "strum", @@ -2520,7 +2528,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -4369,13 +4377,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.1", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -4384,7 +4392,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] @@ -4393,6 +4401,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "reqwest" version = "0.11.15" @@ -5328,15 +5342,6 @@ dependencies = [ "wasmtime-wasi", ] -[[package]] -name = "shuttle-secrets" -version = "0.14.0" -dependencies = [ - "async-trait", - "serde", - "shuttle-service", -] - [[package]] name = "shuttle-service" version = "0.14.0" From 14201263634b454582ab0279499d7c5b53234db7 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:23:40 +0200 Subject: [PATCH 05/15] fix: provisioner port for local deployer --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72c1dace2..aefcae94d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -159,7 +159,7 @@ This starts the provisioner and the auth service, while preventing `gateway` fro start a local run of the deployer: ```bash -cargo run -p shuttle-deployer -- --provisioner-address http://localhost:8000 --proxy-fqdn local.rs --admin-secret test-key --local --project +cargo run -p shuttle-deployer -- --provisioner-address http://localhost:5000 --proxy-fqdn local.rs --admin-secret test-key --local --project ``` The `` needs to match the name of the project that will be deployed to this deployer. This is the `Cargo.toml` or `Shuttle.toml` name for the project. From 393694174afa2aa19a9ee55c0f59da250451db3e Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Sun, 23 Apr 2023 13:09:08 +0200 Subject: [PATCH 06/15] refactor: renaming --- common/Cargo.toml | 2 +- common/src/claims.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index af8c00f5e..752b84bc2 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -93,4 +93,4 @@ ring = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tower = { workspace = true, features = ["util"] } tracing-fluent-assertions = "0.3.0" -tracing-subscriber = { workspace = true, default-features = false } +tracing-subscriber = { workspace = true } diff --git a/common/src/claims.rs b/common/src/claims.rs index 43083c8d7..d287441f8 100644 --- a/common/src/claims.rs +++ b/common/src/claims.rs @@ -108,9 +108,9 @@ impl Scope { /// All scopes for an admin user. pub fn admin() -> Vec { - let mut base = Scope::base(); + let mut scopes = Scope::base(); - base.extend(vec![ + scopes.extend(vec![ Scope::User, Scope::UserCreate, Scope::AcmeCreate, @@ -120,7 +120,7 @@ impl Scope { Scope::Admin, ]); - base + scopes } } From dded0e487fefcee3ebb35d0955c39a3310e6cf28 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 28 Apr 2023 13:51:51 +0200 Subject: [PATCH 07/15] feat: refactor to request token from auth --- CONTRIBUTING.md | 19 +++++++-- deployer/src/handlers/local.rs | 74 ++++++++++++++++++++++++++++++---- deployer/src/handlers/mod.rs | 31 ++++++++------ deployer/src/lib.rs | 6 +-- 4 files changed, 103 insertions(+), 27 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b820e72d..cd8d221cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -155,11 +155,24 @@ make docker-compose.rendered.yml docker compose -f docker-compose.rendered.yml up provisioner ``` -This starts the provisioner and the auth service, while preventing `gateway` from starting up. We're now ready to -start a local run of the deployer: +This starts the provisioner and the auth service, while preventing `gateway` from starting up. +Next up we need to insert an admin user into the `auth` state using the ID of the `auth` +container and the auth CLI `init` command: ```bash -cargo run -p shuttle-deployer -- --provisioner-address http://localhost:5000 --proxy-fqdn local.rs --admin-secret test-key --local --project +AUTH_CONTAINER_ID=$(docker ps -aqf "name=shuttle-auth") \ + docker exec $AUTH_CONTAINER_ID ./usr/local/bin/service \ + --state=/var/lib/shuttle-auth \ + init --name admin --key test-key +``` + +> Note: if you have done this already for this container you will get a "UNIQUE constraint failed" +> error, you can ignore this. + +We're now ready to start a local run of the deployer: + +```bash +cargo run -p shuttle-deployer -- --provisioner-address http://localhost:5000 --auth-uri http://localhost:8008 --proxy-fqdn local.rs --admin-secret test-key --local --project ``` The `` needs to match the name of the project that will be deployed to this deployer. This is the `Cargo.toml` or `Shuttle.toml` name for the project. diff --git a/deployer/src/handlers/local.rs b/deployer/src/handlers/local.rs index 99255c5a2..8519c7573 100644 --- a/deployer/src/handlers/local.rs +++ b/deployer/src/handlers/local.rs @@ -1,13 +1,71 @@ -use axum::http::Request; -use shuttle_common::claims::{Claim, Scope}; +use std::net::Ipv4Addr; -/// This middleware sets an admin token in the claim extension of every request, so we can -/// develop deployer locally without starting it with the gateway and without proxying commands -/// through gateway. -pub async fn set_admin_claim(mut request: Request) -> Request { - let claim = Claim::new("admin".to_string(), Scope::admin()); +use axum::{ + headers::{Authorization, HeaderMapExt}, + http::Request, + middleware::Next, + response::Response, + Extension, +}; +use hyper::{ + client::{connect::dns::GaiResolver, HttpConnector}, + header::AUTHORIZATION, + Body, Client, StatusCode, Uri, +}; +use hyper_reverse_proxy::ReverseProxy; +use once_cell::sync::Lazy; +use serde_json::Value; - request.extensions_mut().insert::(claim); +const LOCAL_ADMIN_KEY: &str = "test-key"; + +static PROXY_CLIENT: Lazy>> = + Lazy::new(|| ReverseProxy::new(Client::new())); + +/// This middleware sends a request to the auth service with a [LOCAL_ADMIN_KEY] Bearer token, to +/// convert the [LOCAL_ADMIN_KEY] to a JWT. We extract the JWT and set it as the Bearer token of +/// the request as it proceeds into the deployer router, where it is converted to a Claim with the +/// token included. This way we can both access the admin scoped routes on deployer, and using the +/// token in the Claim we can pass the ClaimLayer in the provisioner and runtime clients when we +/// need to start services and provision resources. +/// +/// Follow the steps in https://github.com/shuttle-hq/shuttle/blob/main/CONTRIBUTING.md#testing-deployer-only +/// to learn how to insert the [LOCAL_ADMIN_KEY] in the auth state. +/// +/// WARNING: do not set this layer in production. +pub async fn set_jwt_bearer( + Extension(auth_uri): Extension, + mut request: Request, + next: Next, +) -> Result { + let auth_request = Request::builder() + .uri("http://localhost:8008/auth/key") + .header(AUTHORIZATION, format!("Bearer {LOCAL_ADMIN_KEY}")) + .body(Body::empty()) + .unwrap(); + + let response = PROXY_CLIENT + .call( + Ipv4Addr::LOCALHOST.into(), + &auth_uri.to_string(), + auth_request, + ) + .await + .expect("failed to proxy request to auth service"); + + // Since this will only be used for local development, we can always trust the client + // to not send a large body, so we skip the size check. + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let convert: Value = serde_json::from_slice(&body).unwrap(); + + let token = convert["token"] + .as_str() + .expect("response body should have a token"); request + .headers_mut() + .typed_insert(Authorization::bearer(token).expect("to set JWT token")); + + let response = next.run(request).await; + + Ok(response) } diff --git a/deployer/src/handlers/mod.rs b/deployer/src/handlers/mod.rs index 02fbd769c..15b6cc91b 100644 --- a/deployer/src/handlers/mod.rs +++ b/deployer/src/handlers/mod.rs @@ -4,7 +4,7 @@ use axum::extract::ws::{self, WebSocket}; use axum::extract::{Extension, Path, Query}; use axum::handler::Handler; use axum::headers::HeaderMapExt; -use axum::middleware::{from_extractor, map_request}; +use axum::middleware::{self, from_extractor}; use axum::routing::{get, post, Router}; use axum::{extract::BodyStream, Json}; use bytes::BufMut; @@ -23,7 +23,7 @@ use shuttle_common::project::ProjectName; use shuttle_common::storage_manager::StorageManager; use shuttle_common::{request_span, LogItem}; use shuttle_service::builder::clean_crate; -use tracing::{debug, error, field, instrument, trace}; +use tracing::{debug, error, field, instrument, trace, warn}; use utoipa::OpenApi; use utoipa_swagger_ui::SwaggerUi; @@ -34,7 +34,7 @@ use crate::persistence::{Deployment, Log, Persistence, ResourceManager, SecretGe use std::collections::HashMap; -pub use {self::error::Error, self::error::Result, self::local::set_admin_claim}; +pub use {self::error::Error, self::error::Result, self::local::set_jwt_bearer}; mod local; mod project; @@ -77,6 +77,7 @@ pub struct ApiDoc; pub struct RouterBuilder { router: Router, project_name: ProjectName, + auth_uri: Uri, } impl RouterBuilder { @@ -85,6 +86,7 @@ impl RouterBuilder { deployment_manager: DeploymentManager, proxy_fqdn: FQDN, project_name: ProjectName, + auth_uri: Uri, ) -> Self { let router = Router::new() // TODO: The `/swagger-ui` responds with a 303 See Other response which is followed in @@ -134,28 +136,31 @@ impl RouterBuilder { ) .layer(Extension(persistence)) .layer(Extension(deployment_manager)) - .layer(Extension(proxy_fqdn)); + .layer(Extension(proxy_fqdn)) + .layer(JwtAuthenticationLayer::new(AuthPublicKey::new( + auth_uri.clone(), + ))); Self { router, project_name, + auth_uri, } } - pub fn with_auth_layer(mut self, auth_uri: Uri, admin_secret: String) -> Self { - let auth_public_key = AuthPublicKey::new(auth_uri); - - self.router = self - .router - .layer(JwtAuthenticationLayer::new(auth_public_key)) - .layer(AdminSecretLayer::new(admin_secret)); + pub fn with_admin_secret_layer(mut self, admin_secret: String) -> Self { + self.router = self.router.layer(AdminSecretLayer::new(admin_secret)); self } - /// Sets an admin jwt extension on every request for use when running deployer locally. + /// Sets an admin JWT bearer token on every request for use when running deployer locally. pub fn with_local_admin_layer(mut self) -> Self { - self.router = self.router.layer(map_request(set_admin_claim)); + warn!("Building deployer router with auth bypassed, this should only be used for local development."); + self.router = self + .router + .layer(middleware::from_fn(set_jwt_bearer)) + .layer(Extension(self.auth_uri.clone())); self } diff --git a/deployer/src/lib.rs b/deployer/src/lib.rs index fc3ce1e0d..c2f306fb4 100644 --- a/deployer/src/lib.rs +++ b/deployer/src/lib.rs @@ -12,7 +12,7 @@ pub use persistence::Persistence; use proxy::AddressGetter; pub use runtime_manager::RuntimeManager; use tokio::sync::Mutex; -use tracing::{error, info, warn}; +use tracing::{error, info}; use crate::deployment::gateway_client::GatewayClient; @@ -62,14 +62,14 @@ pub async fn start( deployment_manager, args.proxy_fqdn, args.project, + args.auth_uri, ); let router = if args.local { - warn!("Building deployer router with auth disabled, this should only be used for development."); builder.with_local_admin_layer().into_router() } else { builder - .with_auth_layer(args.auth_uri, args.admin_secret) + .with_admin_secret_layer(args.admin_secret) .into_router() }; From 25d499737acdb3a09fe9d98632f7f957a419700a Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 28 Apr 2023 13:57:34 +0200 Subject: [PATCH 08/15] refactor: remove claims feature from deployer common --- deployer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployer/Cargo.toml b/deployer/Cargo.toml index 501bdd610..9a80149d1 100644 --- a/deployer/Cargo.toml +++ b/deployer/Cargo.toml @@ -58,7 +58,7 @@ uuid = { workspace = true, features = ["v4"] } [dependencies.shuttle-common] workspace = true -features = ["backend", "models", "openapi", "claims"] +features = ["backend", "models", "openapi"] [dependencies.shuttle-proto] workspace = true From c7369a3fabead2400655cee7fb8bb70a16d3c82d Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Fri, 28 Apr 2023 15:06:48 +0200 Subject: [PATCH 09/15] refactor: use key/cookie from original request --- CONTRIBUTING.md | 8 +++- deployer/src/handlers/local.rs | 87 ++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd8d221cc..517874b81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -165,10 +165,16 @@ AUTH_CONTAINER_ID=$(docker ps -aqf "name=shuttle-auth") \ --state=/var/lib/shuttle-auth \ init --name admin --key test-key ``` - > Note: if you have done this already for this container you will get a "UNIQUE constraint failed" > error, you can ignore this. +We need to make sure we're logged in with the same key we inserted for the admin user in the +previous step: + +```bash +cargo shuttle login --api-key test-key +``` + We're now ready to start a local run of the deployer: ```bash diff --git a/deployer/src/handlers/local.rs b/deployer/src/handlers/local.rs index 8519c7573..4cd8d0810 100644 --- a/deployer/src/handlers/local.rs +++ b/deployer/src/handlers/local.rs @@ -1,7 +1,7 @@ use std::net::Ipv4Addr; use axum::{ - headers::{Authorization, HeaderMapExt}, + headers::{authorization::Bearer, Authorization, Cookie, Header, HeaderMapExt}, http::Request, middleware::Next, response::Response, @@ -9,27 +9,22 @@ use axum::{ }; use hyper::{ client::{connect::dns::GaiResolver, HttpConnector}, - header::AUTHORIZATION, Body, Client, StatusCode, Uri, }; use hyper_reverse_proxy::ReverseProxy; use once_cell::sync::Lazy; use serde_json::Value; - -const LOCAL_ADMIN_KEY: &str = "test-key"; +use tracing::error; static PROXY_CLIENT: Lazy>> = Lazy::new(|| ReverseProxy::new(Client::new())); -/// This middleware sends a request to the auth service with a [LOCAL_ADMIN_KEY] Bearer token, to -/// convert the [LOCAL_ADMIN_KEY] to a JWT. We extract the JWT and set it as the Bearer token of -/// the request as it proceeds into the deployer router, where it is converted to a Claim with the -/// token included. This way we can both access the admin scoped routes on deployer, and using the -/// token in the Claim we can pass the ClaimLayer in the provisioner and runtime clients when we -/// need to start services and provision resources. +/// This middleware proxies a request to the auth service to get a JWT, which we need to access +/// the deployer endpoints, and we'll also need it in the claim layer of the provisioner and runtime +/// clients. /// /// Follow the steps in https://github.com/shuttle-hq/shuttle/blob/main/CONTRIBUTING.md#testing-deployer-only -/// to learn how to insert the [LOCAL_ADMIN_KEY] in the auth state. +/// to learn how to insert an admin user in the auth state. /// /// WARNING: do not set this layer in production. pub async fn set_jwt_bearer( @@ -37,35 +32,55 @@ pub async fn set_jwt_bearer( mut request: Request, next: Next, ) -> Result { - let auth_request = Request::builder() - .uri("http://localhost:8008/auth/key") - .header(AUTHORIZATION, format!("Bearer {LOCAL_ADMIN_KEY}")) - .body(Body::empty()) - .unwrap(); + let mut auth_details = None; - let response = PROXY_CLIENT - .call( - Ipv4Addr::LOCALHOST.into(), - &auth_uri.to_string(), - auth_request, - ) - .await - .expect("failed to proxy request to auth service"); + if let Some(bearer) = request.headers().typed_get::>() { + auth_details = Some(make_token_request("/auth/key", bearer)); + } - // Since this will only be used for local development, we can always trust the client - // to not send a large body, so we skip the size check. - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let convert: Value = serde_json::from_slice(&body).unwrap(); + if let Some(cookie) = request.headers().typed_get::() { + auth_details = Some(make_token_request("/auth/session", cookie)); + } - let token = convert["token"] - .as_str() - .expect("response body should have a token"); + if let Some(token_request) = auth_details { + let response = PROXY_CLIENT + .call( + Ipv4Addr::LOCALHOST.into(), + &auth_uri.to_string(), + token_request, + ) + .await + .expect("failed to proxy request to auth service"); - request - .headers_mut() - .typed_insert(Authorization::bearer(token).expect("to set JWT token")); + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let convert: Value = serde_json::from_slice(&body) + .expect("failed to deserialize body as JSON, did you login?"); + + let token = convert["token"] + .as_str() + .expect("response body should have a token"); + + request + .headers_mut() + .typed_insert(Authorization::bearer(token).expect("to set JWT token")); - let response = next.run(request).await; + let response = next.run(request).await; - Ok(response) + Ok(response) + } else { + error!("No api-key bearer token or cookie found, make sure you are logged in."); + Err(StatusCode::UNAUTHORIZED) + } +} + +fn make_token_request(uri: &str, header: impl Header) -> Request { + let mut token_request = Request::builder().uri(uri); + token_request + .headers_mut() + .expect("manual request to be valid") + .typed_insert(header); + + token_request + .body(Body::empty()) + .expect("manual request to be valid") } From 663820ad85cca2c0bc4bb5ca34343e3d1ae22f35 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Mon, 1 May 2023 15:34:25 +0200 Subject: [PATCH 10/15] refactor: implement scopebuilder --- auth/src/user.rs | 10 ++++--- common/src/claims.rs | 69 ++++++++++++++++++++++++-------------------- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/auth/src/user.rs b/auth/src/user.rs index f5b0c75f9..46fd5395f 100644 --- a/auth/src/user.rs +++ b/auth/src/user.rs @@ -9,7 +9,7 @@ use axum::{ }; use rand::distributions::{Alphanumeric, DistString}; use serde::{Deserialize, Deserializer, Serialize}; -use shuttle_common::claims::Scope; +use shuttle_common::claims::{Scope, ScopeBuilder}; use sqlx::{query, Row, SqlitePool}; use tracing::{trace, Span}; @@ -185,11 +185,13 @@ pub enum AccountTier { impl From for Vec { fn from(tier: AccountTier) -> Self { + let mut builder = ScopeBuilder::new(); + if tier == AccountTier::Admin { - Scope::admin() - } else { - Scope::base() + builder = builder.with_admin() } + + builder.build() } } diff --git a/common/src/claims.rs b/common/src/claims.rs index d287441f8..d1bb8f74d 100644 --- a/common/src/claims.rs +++ b/common/src/claims.rs @@ -88,39 +88,46 @@ pub enum Scope { Admin, } -impl Scope { - /// Standard scopes for a new user in the free tier. - pub fn base() -> Vec { - vec![ - Scope::Deployment, - Scope::DeploymentPush, - Scope::Logs, - Scope::Service, - Scope::ServiceCreate, - Scope::Project, - Scope::ProjectCreate, - Scope::Resources, - Scope::ResourcesWrite, - Scope::Secret, - Scope::SecretWrite, - ] +/// Standard scopes for new users. +const BASE_SCOPES: [Scope; 11] = [ + Scope::Deployment, + Scope::DeploymentPush, + Scope::Logs, + Scope::Service, + Scope::ServiceCreate, + Scope::Project, + Scope::ProjectCreate, + Scope::Resources, + Scope::ResourcesWrite, + Scope::Secret, + Scope::SecretWrite, +]; + +/// Additional scopes for admin users. +const ADMIN_SCOPES: [Scope; 7] = [ + Scope::User, + Scope::UserCreate, + Scope::AcmeCreate, + Scope::CustomDomainCreate, + Scope::CustomDomainCertificateRenew, + Scope::GatewayCertificateRenew, + Scope::Admin, +]; + +pub struct ScopeBuilder(Vec); + +impl ScopeBuilder { + pub fn new() -> Self { + Self(BASE_SCOPES.to_vec()) } - /// All scopes for an admin user. - pub fn admin() -> Vec { - let mut scopes = Scope::base(); - - scopes.extend(vec![ - Scope::User, - Scope::UserCreate, - Scope::AcmeCreate, - Scope::CustomDomainCreate, - Scope::CustomDomainCertificateRenew, - Scope::GatewayCertificateRenew, - Scope::Admin, - ]); - - scopes + pub fn with_admin(mut self) -> Self { + self.0.extend(ADMIN_SCOPES.into_iter()); + self + } + + pub fn build(self) -> Vec { + self.0 } } From a54bed8b8fbe4262016090e4dae51d0686966892 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Tue, 2 May 2023 14:41:03 +0200 Subject: [PATCH 11/15] fix: clippy --- common/src/claims.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/src/claims.rs b/common/src/claims.rs index d1bb8f74d..f6b6d2e16 100644 --- a/common/src/claims.rs +++ b/common/src/claims.rs @@ -131,6 +131,12 @@ impl ScopeBuilder { } } +impl Default for ScopeBuilder { + fn default() -> Self { + Self::new() + } +} + #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] pub struct Claim { /// Expiration time (as UTC timestamp). From 1c41c9b70a53d35c05d061bad358fa1cb210fc39 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Tue, 2 May 2023 17:24:57 +0200 Subject: [PATCH 12/15] refactor: address review --- CONTRIBUTING.md | 15 +++++++++++- common/src/claims.rs | 56 ++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 517874b81..3e1c58221 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,7 +148,10 @@ cargo run --manifest-path ../../../Cargo.toml --bin cargo-shuttle -- logs The steps outlined above starts all the services used by shuttle locally (ie. both `gateway` and `deployer`). However, sometimes you will want to quickly test changes to `deployer` only. To do this replace `make up` with the following: ```bash -# first generate the local docker-compose file +# if you didn't do this already, make the images +USE_PANAMAX=disable make images + +# then generate the local docker-compose file make docker-compose.rendered.yml # then run it @@ -183,6 +186,16 @@ cargo run -p shuttle-deployer -- --provisioner-address http://localhost:5000 --a The `` needs to match the name of the project that will be deployed to this deployer. This is the `Cargo.toml` or `Shuttle.toml` name for the project. +Now that your local deployer is running, you can run commands against using the cargo-shuttle CLI. +To do that you should navigate into an example, it needs to have the same project name as the +one you submitted when starting the deployer above. Then you can use the CLI like you normally +would: + +```bash +# the manifest path is the path to the root shuttle manifest from the example directory +cargo run --bin cargo-shuttle --manifest-path="../../../Cargo.toml" -- deploy +``` + ### Using Podman instead of Docker If you want to use Podman instead of Docker, you can configure the build process with environment variables. diff --git a/common/src/claims.rs b/common/src/claims.rs index f6b6d2e16..7a0f7d400 100644 --- a/common/src/claims.rs +++ b/common/src/claims.rs @@ -88,41 +88,41 @@ pub enum Scope { Admin, } -/// Standard scopes for new users. -const BASE_SCOPES: [Scope; 11] = [ - Scope::Deployment, - Scope::DeploymentPush, - Scope::Logs, - Scope::Service, - Scope::ServiceCreate, - Scope::Project, - Scope::ProjectCreate, - Scope::Resources, - Scope::ResourcesWrite, - Scope::Secret, - Scope::SecretWrite, -]; - -/// Additional scopes for admin users. -const ADMIN_SCOPES: [Scope; 7] = [ - Scope::User, - Scope::UserCreate, - Scope::AcmeCreate, - Scope::CustomDomainCreate, - Scope::CustomDomainCertificateRenew, - Scope::GatewayCertificateRenew, - Scope::Admin, -]; - pub struct ScopeBuilder(Vec); impl ScopeBuilder { + /// Create a builder with the standard scopes for new users. pub fn new() -> Self { - Self(BASE_SCOPES.to_vec()) + Self(vec![ + Scope::Deployment, + Scope::DeploymentPush, + Scope::Logs, + Scope::Service, + Scope::ServiceCreate, + Scope::Project, + Scope::ProjectCreate, + Scope::Resources, + Scope::ResourcesWrite, + Scope::Secret, + Scope::SecretWrite, + ]) } + /// Extend the current scopes with admin scopes. pub fn with_admin(mut self) -> Self { - self.0.extend(ADMIN_SCOPES.into_iter()); + self.0.extend(vec![ + Scope::Deployment, + Scope::DeploymentPush, + Scope::Logs, + Scope::Service, + Scope::ServiceCreate, + Scope::Project, + Scope::ProjectCreate, + Scope::Resources, + Scope::ResourcesWrite, + Scope::Secret, + Scope::SecretWrite, + ]); self } From 6342bf848ae4609b8a3e5d6717f3202cbb543fc7 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Tue, 2 May 2023 18:00:48 +0200 Subject: [PATCH 13/15] refactor: auth container command --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e1c58221..e77a89447 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -163,7 +163,7 @@ Next up we need to insert an admin user into the `auth` state using the ID of th container and the auth CLI `init` command: ```bash -AUTH_CONTAINER_ID=$(docker ps -aqf "name=shuttle-auth") \ +AUTH_CONTAINER_ID=$(docker ps -qf "name=auth") \ docker exec $AUTH_CONTAINER_ID ./usr/local/bin/service \ --state=/var/lib/shuttle-auth \ init --name admin --key test-key From 4a72b7ad272f0ff99b8f2a6e4d871c495d20ac37 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Tue, 2 May 2023 18:30:06 +0200 Subject: [PATCH 14/15] refactor: cleanup builder in start fn --- deployer/src/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/deployer/src/lib.rs b/deployer/src/lib.rs index c2f306fb4..0d1eadfa4 100644 --- a/deployer/src/lib.rs +++ b/deployer/src/lib.rs @@ -57,7 +57,7 @@ pub async fn start( deployment_manager.run_push(built).await; } - let builder = handlers::RouterBuilder::new( + let mut builder = handlers::RouterBuilder::new( persistence, deployment_manager, args.proxy_fqdn, @@ -65,14 +65,15 @@ pub async fn start( args.auth_uri, ); - let router = if args.local { - builder.with_local_admin_layer().into_router() + if args.local { + // If the --local flag is passed, setup an auth layer in deployer + builder = builder.with_local_admin_layer() } else { - builder - .with_admin_secret_layer(args.admin_secret) - .into_router() + builder = builder.with_admin_secret_layer(args.admin_secret) }; + let router = builder.into_router(); + info!(address=%args.api_address, "Binding to and listening at address"); axum::Server::bind(&args.api_address) From b1a8dca07e09ad7bf04852a897ac3b87dc10f323 Mon Sep 17 00:00:00 2001 From: oddgrd <29732646+oddgrd@users.noreply.github.com> Date: Tue, 2 May 2023 18:31:31 +0200 Subject: [PATCH 15/15] docs: local arg docs --- deployer/src/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployer/src/args.rs b/deployer/src/args.rs index bc2d37794..b224fff11 100644 --- a/deployer/src/args.rs +++ b/deployer/src/args.rs @@ -51,7 +51,7 @@ pub struct Args { #[clap(long, default_value = "/tmp")] pub artifacts_path: PathBuf, - /// Run deployer without auth locally + /// Add an auth layer to deployer for local development #[arg(long)] pub local: bool, }