diff --git a/Cargo.toml b/Cargo.toml index bf4209f187..88ac046158 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" -members = ["packages/api/actor","packages/api/auth","packages/api/cf-verification","packages/api/cloud","packages/api/games","packages/api/group","packages/api/identity","packages/api/job","packages/api/matchmaker","packages/api/monolith-edge","packages/api/monolith-public","packages/api/portal","packages/api/provision","packages/api/status","packages/api/traefik-provider","packages/api/ui","packages/common/api-helper/build","packages/common/api-helper/macros","packages/common/cache/build","packages/common/cache/result","packages/common/chirp-workflow/core","packages/common/chirp-workflow/macros","packages/common/chirp/client","packages/common/chirp/metrics","packages/common/chirp/perf","packages/common/chirp/types","packages/common/chirp/worker","packages/common/chirp/worker-attributes","packages/common/claims","packages/common/config","packages/common/connection","packages/common/convert","packages/common/deno-embed","packages/common/env","packages/common/formatted-error","packages/common/global-error","packages/common/health-checks","packages/common/hub-embed","packages/common/kv-str","packages/common/metrics","packages/common/migrate","packages/common/nomad-util","packages/common/operation/core","packages/common/operation/macros","packages/common/pools","packages/common/redis-util","packages/common/runtime","packages/common/s3-util","packages/common/schemac","packages/common/service-manager","packages/common/smithy-output/api-auth/rust","packages/common/smithy-output/api-auth/rust-server","packages/common/smithy-output/api-cf-verification/rust","packages/common/smithy-output/api-cf-verification/rust-server","packages/common/smithy-output/api-cloud/rust","packages/common/smithy-output/api-cloud/rust-server","packages/common/smithy-output/api-group/rust","packages/common/smithy-output/api-group/rust-server","packages/common/smithy-output/api-identity/rust","packages/common/smithy-output/api-identity/rust-server","packages/common/smithy-output/api-job/rust","packages/common/smithy-output/api-job/rust-server","packages/common/smithy-output/api-kv/rust","packages/common/smithy-output/api-kv/rust-server","packages/common/smithy-output/api-matchmaker/rust","packages/common/smithy-output/api-matchmaker/rust-server","packages/common/smithy-output/api-party/rust","packages/common/smithy-output/api-party/rust-server","packages/common/smithy-output/api-portal/rust","packages/common/smithy-output/api-portal/rust-server","packages/common/smithy-output/api-status/rust","packages/common/smithy-output/api-status/rust-server","packages/common/smithy-output/api-traefik-provider/rust","packages/common/smithy-output/api-traefik-provider/rust-server","packages/common/test","packages/common/test-images","packages/common/types-proto/build","packages/common/types-proto/core","packages/common/util/core","packages/common/util/macros","packages/common/util/search","packages/infra/client/actor-kv","packages/infra/client/config","packages/infra/client/container-runner","packages/infra/client/echo","packages/infra/client/isolate-v8-runner","packages/infra/client/logs","packages/infra/client/manager","packages/infra/legacy/job-runner","packages/infra/schema-generator","packages/infra/server","packages/services/build","packages/services/build/ops/create","packages/services/build/ops/get","packages/services/build/ops/list-for-env","packages/services/build/ops/list-for-game","packages/services/build/standalone/default-create","packages/services/build/util","packages/services/captcha/ops/hcaptcha-config-get","packages/services/captcha/ops/hcaptcha-verify","packages/services/captcha/ops/request","packages/services/captcha/ops/turnstile-config-get","packages/services/captcha/ops/turnstile-verify","packages/services/captcha/ops/verify","packages/services/captcha/util","packages/services/cdn/ops/namespace-auth-user-remove","packages/services/cdn/ops/namespace-auth-user-update","packages/services/cdn/ops/namespace-create","packages/services/cdn/ops/namespace-domain-create","packages/services/cdn/ops/namespace-domain-remove","packages/services/cdn/ops/namespace-get","packages/services/cdn/ops/namespace-resolve-domain","packages/services/cdn/ops/ns-auth-type-set","packages/services/cdn/ops/ns-enable-domain-public-auth-set","packages/services/cdn/ops/site-create","packages/services/cdn/ops/site-get","packages/services/cdn/ops/site-list-for-game","packages/services/cdn/ops/version-get","packages/services/cdn/ops/version-prepare","packages/services/cdn/ops/version-publish","packages/services/cdn/util","packages/services/cdn/worker","packages/services/cf-custom-hostname/ops/get","packages/services/cf-custom-hostname/ops/list-for-namespace-id","packages/services/cf-custom-hostname/ops/resolve-hostname","packages/services/cf-custom-hostname/worker","packages/services/cloud/ops/device-link-create","packages/services/cloud/ops/game-config-create","packages/services/cloud/ops/game-config-get","packages/services/cloud/ops/game-token-create","packages/services/cloud/ops/namespace-create","packages/services/cloud/ops/namespace-get","packages/services/cloud/ops/namespace-token-development-create","packages/services/cloud/ops/namespace-token-public-create","packages/services/cloud/ops/version-get","packages/services/cloud/ops/version-publish","packages/services/cloud/standalone/default-create","packages/services/cloud/worker","packages/services/cluster","packages/services/cluster/standalone/datacenter-tls-renew","packages/services/cluster/standalone/default-update","packages/services/cluster/standalone/gc","packages/services/cluster/standalone/metrics-publish","packages/services/custom-user-avatar/ops/list-for-game","packages/services/custom-user-avatar/ops/upload-complete","packages/services/debug/ops/email-res","packages/services/ds","packages/services/ds-log/ops/export","packages/services/ds-log/ops/read","packages/services/dynamic-config","packages/services/email-verification/ops/complete","packages/services/email-verification/ops/create","packages/services/email/ops/send","packages/services/external/ops/request-validate","packages/services/external/worker","packages/services/faker/ops/build","packages/services/faker/ops/cdn-site","packages/services/faker/ops/game","packages/services/faker/ops/game-namespace","packages/services/faker/ops/game-version","packages/services/faker/ops/job-run","packages/services/faker/ops/job-template","packages/services/faker/ops/mm-lobby","packages/services/faker/ops/mm-lobby-row","packages/services/faker/ops/mm-player","packages/services/faker/ops/region","packages/services/faker/ops/team","packages/services/faker/ops/user","packages/services/game/ops/banner-upload-complete","packages/services/game/ops/create","packages/services/game/ops/get","packages/services/game/ops/list-all","packages/services/game/ops/list-for-team","packages/services/game/ops/logo-upload-complete","packages/services/game/ops/namespace-create","packages/services/game/ops/namespace-get","packages/services/game/ops/namespace-list","packages/services/game/ops/namespace-resolve-name-id","packages/services/game/ops/namespace-resolve-url","packages/services/game/ops/namespace-validate","packages/services/game/ops/namespace-version-history-list","packages/services/game/ops/namespace-version-set","packages/services/game/ops/recommend","packages/services/game/ops/resolve-name-id","packages/services/game/ops/resolve-namespace-id","packages/services/game/ops/token-development-validate","packages/services/game/ops/validate","packages/services/game/ops/version-create","packages/services/game/ops/version-get","packages/services/game/ops/version-list","packages/services/game/ops/version-validate","packages/services/ip/ops/info","packages/services/job-log/ops/read","packages/services/job-log/worker","packages/services/job-run","packages/services/job/standalone/gc","packages/services/job/util","packages/services/linode","packages/services/linode/standalone/gc","packages/services/load-test/standalone/api-cloud","packages/services/load-test/standalone/mm","packages/services/load-test/standalone/mm-sustain","packages/services/load-test/standalone/sqlx","packages/services/load-test/standalone/watch-requests","packages/services/mm-config/ops/game-get","packages/services/mm-config/ops/game-upsert","packages/services/mm-config/ops/lobby-group-get","packages/services/mm-config/ops/lobby-group-resolve-name-id","packages/services/mm-config/ops/lobby-group-resolve-version","packages/services/mm-config/ops/namespace-config-set","packages/services/mm-config/ops/namespace-config-validate","packages/services/mm-config/ops/namespace-create","packages/services/mm-config/ops/namespace-get","packages/services/mm-config/ops/version-get","packages/services/mm-config/ops/version-prepare","packages/services/mm-config/ops/version-publish","packages/services/mm/ops/dev-player-token-create","packages/services/mm/ops/lobby-find-fail","packages/services/mm/ops/lobby-find-lobby-query-list","packages/services/mm/ops/lobby-find-try-complete","packages/services/mm/ops/lobby-for-run-id","packages/services/mm/ops/lobby-get","packages/services/mm/ops/lobby-history","packages/services/mm/ops/lobby-idle-update","packages/services/mm/ops/lobby-list-for-namespace","packages/services/mm/ops/lobby-list-for-user-id","packages/services/mm/ops/lobby-player-count","packages/services/mm/ops/lobby-runtime-aggregate","packages/services/mm/ops/lobby-state-get","packages/services/mm/ops/player-count-for-namespace","packages/services/mm/ops/player-get","packages/services/mm/standalone/gc","packages/services/mm/util","packages/services/mm/worker","packages/services/monolith/standalone/worker","packages/services/monolith/standalone/workflow-worker","packages/services/nomad/standalone/monitor","packages/services/pegboard","packages/services/pegboard/standalone/dc-init","packages/services/pegboard/standalone/gc","packages/services/pegboard/standalone/metrics-publish","packages/services/pegboard/standalone/ws","packages/services/region/ops/get","packages/services/region/ops/list","packages/services/region/ops/list-for-game","packages/services/region/ops/recommend","packages/services/region/ops/resolve","packages/services/region/ops/resolve-for-game","packages/services/server-spec","packages/services/team-invite/ops/get","packages/services/team-invite/worker","packages/services/team/ops/avatar-upload-complete","packages/services/team/ops/get","packages/services/team/ops/join-request-list","packages/services/team/ops/member-count","packages/services/team/ops/member-get","packages/services/team/ops/member-list","packages/services/team/ops/member-relationship-get","packages/services/team/ops/profile-validate","packages/services/team/ops/recommend","packages/services/team/ops/resolve-display-name","packages/services/team/ops/user-ban-get","packages/services/team/ops/user-ban-list","packages/services/team/ops/validate","packages/services/team/util","packages/services/team/worker","packages/services/telemetry/standalone/beacon","packages/services/tier","packages/services/token/ops/create","packages/services/token/ops/exchange","packages/services/token/ops/get","packages/services/token/ops/revoke","packages/services/upload/ops/complete","packages/services/upload/ops/file-list","packages/services/upload/ops/get","packages/services/upload/ops/list-for-user","packages/services/upload/ops/prepare","packages/services/upload/worker","packages/services/user","packages/services/user-identity/ops/create","packages/services/user-identity/ops/delete","packages/services/user-identity/ops/get","packages/services/user/ops/avatar-upload-complete","packages/services/user/ops/get","packages/services/user/ops/pending-delete-toggle","packages/services/user/ops/profile-validate","packages/services/user/ops/resolve-email","packages/services/user/ops/team-list","packages/services/user/ops/token-create","packages/services/user/standalone/delete-pending","packages/services/user/worker","packages/services/workflow/standalone/gc","packages/services/workflow/standalone/metrics-publish","packages/toolchain/actors-sdk-embed","packages/toolchain/cli","packages/toolchain/js-utils-embed","packages/toolchain/toolchain","sdks/api/full/rust"] +members = ["packages/api/actor","packages/api/auth","packages/api/cf-verification","packages/api/cloud","packages/api/games","packages/api/group","packages/api/identity","packages/api/job","packages/api/matchmaker","packages/api/monolith-edge","packages/api/monolith-public","packages/api/portal","packages/api/provision","packages/api/status","packages/api/traefik-provider","packages/api/ui","packages/common/api-helper/build","packages/common/api-helper/macros","packages/common/cache/build","packages/common/cache/result","packages/common/chirp-workflow/core","packages/common/chirp-workflow/macros","packages/common/chirp/client","packages/common/chirp/metrics","packages/common/chirp/perf","packages/common/chirp/types","packages/common/chirp/worker","packages/common/chirp/worker-attributes","packages/common/claims","packages/common/config","packages/common/connection","packages/common/convert","packages/common/deno-embed","packages/common/env","packages/common/formatted-error","packages/common/global-error","packages/common/health-checks","packages/common/hub-embed","packages/common/kv-str","packages/common/metrics","packages/common/migrate","packages/common/nomad-util","packages/common/operation/core","packages/common/operation/macros","packages/common/pools","packages/common/redis-util","packages/common/runtime","packages/common/s3-util","packages/common/schemac","packages/common/service-manager","packages/common/smithy-output/api-auth/rust","packages/common/smithy-output/api-auth/rust-server","packages/common/smithy-output/api-cf-verification/rust","packages/common/smithy-output/api-cf-verification/rust-server","packages/common/smithy-output/api-cloud/rust","packages/common/smithy-output/api-cloud/rust-server","packages/common/smithy-output/api-group/rust","packages/common/smithy-output/api-group/rust-server","packages/common/smithy-output/api-identity/rust","packages/common/smithy-output/api-identity/rust-server","packages/common/smithy-output/api-job/rust","packages/common/smithy-output/api-job/rust-server","packages/common/smithy-output/api-kv/rust","packages/common/smithy-output/api-kv/rust-server","packages/common/smithy-output/api-matchmaker/rust","packages/common/smithy-output/api-matchmaker/rust-server","packages/common/smithy-output/api-party/rust","packages/common/smithy-output/api-party/rust-server","packages/common/smithy-output/api-portal/rust","packages/common/smithy-output/api-portal/rust-server","packages/common/smithy-output/api-status/rust","packages/common/smithy-output/api-status/rust-server","packages/common/smithy-output/api-traefik-provider/rust","packages/common/smithy-output/api-traefik-provider/rust-server","packages/common/test","packages/common/test-images","packages/common/types-proto/build","packages/common/types-proto/core","packages/common/util/core","packages/common/util/macros","packages/common/util/search","packages/infra/client/actor-kv","packages/infra/client/config","packages/infra/client/container-runner","packages/infra/client/echo","packages/infra/client/isolate-v8-runner","packages/infra/client/logs","packages/infra/client/manager","packages/infra/legacy/job-runner","packages/infra/schema-generator","packages/infra/server","packages/services/build","packages/services/build/ops/create","packages/services/build/ops/get","packages/services/build/ops/list-for-env","packages/services/build/ops/list-for-game","packages/services/build/standalone/default-create","packages/services/build/util","packages/services/captcha/ops/hcaptcha-config-get","packages/services/captcha/ops/hcaptcha-verify","packages/services/captcha/ops/request","packages/services/captcha/ops/turnstile-config-get","packages/services/captcha/ops/turnstile-verify","packages/services/captcha/ops/verify","packages/services/captcha/util","packages/services/cdn/ops/namespace-auth-user-remove","packages/services/cdn/ops/namespace-auth-user-update","packages/services/cdn/ops/namespace-create","packages/services/cdn/ops/namespace-domain-create","packages/services/cdn/ops/namespace-domain-remove","packages/services/cdn/ops/namespace-get","packages/services/cdn/ops/namespace-resolve-domain","packages/services/cdn/ops/ns-auth-type-set","packages/services/cdn/ops/ns-enable-domain-public-auth-set","packages/services/cdn/ops/site-create","packages/services/cdn/ops/site-get","packages/services/cdn/ops/site-list-for-game","packages/services/cdn/ops/version-get","packages/services/cdn/ops/version-prepare","packages/services/cdn/ops/version-publish","packages/services/cdn/util","packages/services/cdn/worker","packages/services/cf-custom-hostname/ops/get","packages/services/cf-custom-hostname/ops/list-for-namespace-id","packages/services/cf-custom-hostname/ops/resolve-hostname","packages/services/cf-custom-hostname/worker","packages/services/cloud/ops/device-link-create","packages/services/cloud/ops/game-config-create","packages/services/cloud/ops/game-config-get","packages/services/cloud/ops/game-token-create","packages/services/cloud/ops/namespace-create","packages/services/cloud/ops/namespace-get","packages/services/cloud/ops/namespace-token-development-create","packages/services/cloud/ops/namespace-token-public-create","packages/services/cloud/ops/version-get","packages/services/cloud/ops/version-publish","packages/services/cloud/standalone/default-create","packages/services/cloud/worker","packages/services/cluster","packages/services/cluster/standalone/datacenter-tls-renew","packages/services/cluster/standalone/default-update","packages/services/cluster/standalone/gc","packages/services/cluster/standalone/metrics-publish","packages/services/cluster/standalone/tunnel-tls-renew","packages/services/custom-user-avatar/ops/list-for-game","packages/services/custom-user-avatar/ops/upload-complete","packages/services/debug/ops/email-res","packages/services/ds","packages/services/ds-log/ops/export","packages/services/ds-log/ops/read","packages/services/dynamic-config","packages/services/email-verification/ops/complete","packages/services/email-verification/ops/create","packages/services/email/ops/send","packages/services/external/ops/request-validate","packages/services/external/worker","packages/services/faker/ops/build","packages/services/faker/ops/cdn-site","packages/services/faker/ops/game","packages/services/faker/ops/game-namespace","packages/services/faker/ops/game-version","packages/services/faker/ops/job-run","packages/services/faker/ops/job-template","packages/services/faker/ops/mm-lobby","packages/services/faker/ops/mm-lobby-row","packages/services/faker/ops/mm-player","packages/services/faker/ops/region","packages/services/faker/ops/team","packages/services/faker/ops/user","packages/services/game/ops/banner-upload-complete","packages/services/game/ops/create","packages/services/game/ops/get","packages/services/game/ops/list-all","packages/services/game/ops/list-for-team","packages/services/game/ops/logo-upload-complete","packages/services/game/ops/namespace-create","packages/services/game/ops/namespace-get","packages/services/game/ops/namespace-list","packages/services/game/ops/namespace-resolve-name-id","packages/services/game/ops/namespace-resolve-url","packages/services/game/ops/namespace-validate","packages/services/game/ops/namespace-version-history-list","packages/services/game/ops/namespace-version-set","packages/services/game/ops/recommend","packages/services/game/ops/resolve-name-id","packages/services/game/ops/resolve-namespace-id","packages/services/game/ops/token-development-validate","packages/services/game/ops/validate","packages/services/game/ops/version-create","packages/services/game/ops/version-get","packages/services/game/ops/version-list","packages/services/game/ops/version-validate","packages/services/ip/ops/info","packages/services/job-log/ops/read","packages/services/job-log/worker","packages/services/job-run","packages/services/job/standalone/gc","packages/services/job/util","packages/services/linode","packages/services/linode/standalone/gc","packages/services/load-test/standalone/api-cloud","packages/services/load-test/standalone/mm","packages/services/load-test/standalone/mm-sustain","packages/services/load-test/standalone/sqlx","packages/services/load-test/standalone/watch-requests","packages/services/mm-config/ops/game-get","packages/services/mm-config/ops/game-upsert","packages/services/mm-config/ops/lobby-group-get","packages/services/mm-config/ops/lobby-group-resolve-name-id","packages/services/mm-config/ops/lobby-group-resolve-version","packages/services/mm-config/ops/namespace-config-set","packages/services/mm-config/ops/namespace-config-validate","packages/services/mm-config/ops/namespace-create","packages/services/mm-config/ops/namespace-get","packages/services/mm-config/ops/version-get","packages/services/mm-config/ops/version-prepare","packages/services/mm-config/ops/version-publish","packages/services/mm/ops/dev-player-token-create","packages/services/mm/ops/lobby-find-fail","packages/services/mm/ops/lobby-find-lobby-query-list","packages/services/mm/ops/lobby-find-try-complete","packages/services/mm/ops/lobby-for-run-id","packages/services/mm/ops/lobby-get","packages/services/mm/ops/lobby-history","packages/services/mm/ops/lobby-idle-update","packages/services/mm/ops/lobby-list-for-namespace","packages/services/mm/ops/lobby-list-for-user-id","packages/services/mm/ops/lobby-player-count","packages/services/mm/ops/lobby-runtime-aggregate","packages/services/mm/ops/lobby-state-get","packages/services/mm/ops/player-count-for-namespace","packages/services/mm/ops/player-get","packages/services/mm/standalone/gc","packages/services/mm/util","packages/services/mm/worker","packages/services/monolith/standalone/worker","packages/services/monolith/standalone/workflow-worker","packages/services/nomad/standalone/monitor","packages/services/pegboard","packages/services/pegboard/standalone/dc-init","packages/services/pegboard/standalone/gc","packages/services/pegboard/standalone/metrics-publish","packages/services/pegboard/standalone/ws","packages/services/region/ops/get","packages/services/region/ops/list","packages/services/region/ops/list-for-game","packages/services/region/ops/recommend","packages/services/region/ops/resolve","packages/services/region/ops/resolve-for-game","packages/services/server-spec","packages/services/team-invite/ops/get","packages/services/team-invite/worker","packages/services/team/ops/avatar-upload-complete","packages/services/team/ops/get","packages/services/team/ops/join-request-list","packages/services/team/ops/member-count","packages/services/team/ops/member-get","packages/services/team/ops/member-list","packages/services/team/ops/member-relationship-get","packages/services/team/ops/profile-validate","packages/services/team/ops/recommend","packages/services/team/ops/resolve-display-name","packages/services/team/ops/user-ban-get","packages/services/team/ops/user-ban-list","packages/services/team/ops/validate","packages/services/team/util","packages/services/team/worker","packages/services/telemetry/standalone/beacon","packages/services/tier","packages/services/token/ops/create","packages/services/token/ops/exchange","packages/services/token/ops/get","packages/services/token/ops/revoke","packages/services/upload/ops/complete","packages/services/upload/ops/file-list","packages/services/upload/ops/get","packages/services/upload/ops/list-for-user","packages/services/upload/ops/prepare","packages/services/upload/worker","packages/services/user","packages/services/user-identity/ops/create","packages/services/user-identity/ops/delete","packages/services/user-identity/ops/get","packages/services/user/ops/avatar-upload-complete","packages/services/user/ops/get","packages/services/user/ops/pending-delete-toggle","packages/services/user/ops/profile-validate","packages/services/user/ops/resolve-email","packages/services/user/ops/team-list","packages/services/user/ops/token-create","packages/services/user/standalone/delete-pending","packages/services/user/worker","packages/services/workflow/standalone/gc","packages/services/workflow/standalone/metrics-publish","packages/toolchain/actors-sdk-embed","packages/toolchain/cli","packages/toolchain/js-utils-embed","packages/toolchain/toolchain","sdks/api/full/rust"] [workspace.package] version = "0.0.1" @@ -524,6 +524,9 @@ path = "packages/services/cluster/standalone/gc" [workspace.dependencies.cluster-metrics-publish] path = "packages/services/cluster/standalone/metrics-publish" +[workspace.dependencies.cluster-tunnel-tls-renew] +path = "packages/services/cluster/standalone/tunnel-tls-renew" + [workspace.dependencies.custom-user-avatar-list-for-game] path = "packages/services/custom-user-avatar/ops/list-for-game" diff --git a/packages/api/provision/src/route/mod.rs b/packages/api/provision/src/route/mod.rs index e5c0b9873d..199f55bbf5 100644 --- a/packages/api/provision/src/route/mod.rs +++ b/packages/api/provision/src/route/mod.rs @@ -6,6 +6,7 @@ use uuid::Uuid; pub mod datacenters; pub mod servers; +pub mod tunnel; define_router! { routes: { @@ -28,5 +29,11 @@ define_router! { internal_endpoint: true, ), }, + + "tunnel" / "tls": { + GET: tunnel::tls( + internal_endpoint: true, + ), + } }, } diff --git a/packages/api/provision/src/route/tunnel.rs b/packages/api/provision/src/route/tunnel.rs new file mode 100644 index 0000000000..c16e97259c --- /dev/null +++ b/packages/api/provision/src/route/tunnel.rs @@ -0,0 +1,24 @@ +use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; +use rivet_api::models; +use rivet_operation::prelude::*; + +use crate::auth::Auth; + +// MARK: GET /tunnel/tls +pub async fn tls( + ctx: Ctx, + _watch_index: WatchIndexQuery, +) -> GlobalResult { + ctx.auth().server()?; + + let tunnel_tls_res = ctx.op(cluster::ops::tunnel::tls_get::Input {}).await?; + + let tls_config = &ctx.config().server()?.tls()?; + let ca_cert_pem = tls_config.root_ca_cert_pem.read(); + + Ok(models::ProvisionTunnelGetTlsResponse { + cert_pem: tunnel_tls_res.cert_pem.clone(), + root_ca_cert_pem: ca_cert_pem.clone(), + private_key_pem: tunnel_tls_res.private_key_pem.clone(), + }) +} diff --git a/packages/common/config/src/config/server/mod.rs b/packages/common/config/src/config/server/mod.rs index ba5e4a0d7d..db6bf5a5f5 100644 --- a/packages/common/config/src/config/server/mod.rs +++ b/packages/common/config/src/config/server/mod.rs @@ -242,8 +242,7 @@ pub struct CloudflareZone { #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct Tls { pub root_ca_cert_pem: Secret, - pub cert_locally_signed_job_cert_pem: Secret, - pub cert_locally_signed_job_key_pem: Secret, + pub root_ca_key_pem: Secret, pub acme: TlsAcme, } diff --git a/packages/common/service-manager/src/lib.rs b/packages/common/service-manager/src/lib.rs index 24a0ee65b2..4521e91077 100644 --- a/packages/common/service-manager/src/lib.rs +++ b/packages/common/service-manager/src/lib.rs @@ -56,6 +56,22 @@ impl ServiceKind { Cron(config) => ServiceBehavior::Cron(config.clone()), } } + + pub fn eq(&self, other: &Self) -> bool { + use ServiceKind::*; + + match (self, other) { + (ApiPublic, ApiPublic) + | (ApiEdge, ApiEdge) + | (ApiPrivate, ApiPrivate) + | (Standalone, Standalone) + | (Singleton, Singleton) + | (Oneshot, Oneshot) + | (Core, Core) => true, + (Cron(_), Cron(_)) => true, + _ => false, + } + } } /// Defines how a service should be ran. diff --git a/packages/common/util/core/src/math.rs b/packages/common/util/core/src/math.rs index 09fb668a4b..a25e97ef0c 100644 --- a/packages/common/util/core/src/math.rs +++ b/packages/common/util/core/src/math.rs @@ -1,5 +1,5 @@ /// Divide integers of any type, rounding up. Panics on dividing by 0. -#[deprecated(note="use x.div_ceil(y) instead")] +#[deprecated(note = "use x.div_ceil(y) instead")] #[macro_export] macro_rules! div_up { ($a:expr, $b:expr) => { diff --git a/packages/infra/client/actor-kv/src/lib.rs b/packages/infra/client/actor-kv/src/lib.rs index e5678013cc..0f4037c0b8 100644 --- a/packages/infra/client/actor-kv/src/lib.rs +++ b/packages/infra/client/actor-kv/src/lib.rs @@ -9,13 +9,13 @@ pub use entry::Entry; use entry::{EntryBuilder, SubKey}; use foundationdb::{self as fdb, directory::Directory, tuple::Subspace}; use futures_util::{StreamExt, TryStreamExt}; +use indexmap::IndexMap; use key::Key; use list_query::ListLimitReached; pub use list_query::ListQuery; pub use metadata::Metadata; use pegboard::protocol; use prost::Message; -use indexmap::IndexMap; use utils::{owner_segment, validate_entries, validate_keys, TransactionExt}; mod entry; @@ -238,7 +238,8 @@ impl ActorKv { // Short circuit when limit is reached. This relies on data from the stream // being in order. - if size == limit && matches!(entry, indexmap::map::Entry::Vacant(_)) { + if size == limit && matches!(entry, indexmap::map::Entry::Vacant(_)) + { return Err(ListLimitReached(acc).into()); } diff --git a/packages/infra/client/actor-kv/src/list_query.rs b/packages/infra/client/actor-kv/src/list_query.rs index 7b3fb33d01..f070d3006a 100644 --- a/packages/infra/client/actor-kv/src/list_query.rs +++ b/packages/infra/client/actor-kv/src/list_query.rs @@ -1,7 +1,7 @@ use anyhow::*; use foundationdb::tuple::Subspace; -use serde::Deserialize; use indexmap::IndexMap; +use serde::Deserialize; use crate::{ entry::EntryBuilder, diff --git a/packages/infra/client/isolate-v8-runner/src/ext/kv.rs b/packages/infra/client/isolate-v8-runner/src/ext/kv.rs index a728a44afd..b28cfb4898 100644 --- a/packages/infra/client/isolate-v8-runner/src/ext/kv.rs +++ b/packages/infra/client/isolate-v8-runner/src/ext/kv.rs @@ -1,8 +1,8 @@ use std::{collections::HashMap, future::Future, sync::Arc}; use deno_core::{error::AnyError, op2, JsBuffer, OpState, ToJsBuffer}; -use serde::Serialize; use pegboard_actor_kv as actor_kv; +use serde::Serialize; type FakeMap = Box<[(T, U)]>; diff --git a/packages/infra/client/isolate-v8-runner/src/isolate.rs b/packages/infra/client/isolate-v8-runner/src/isolate.rs index c0ceabafdc..e182c22a5d 100644 --- a/packages/infra/client/isolate-v8-runner/src/isolate.rs +++ b/packages/infra/client/isolate-v8-runner/src/isolate.rs @@ -10,7 +10,6 @@ use std::{ thread::JoinHandle, }; -use pegboard_actor_kv::ActorKv; use anyhow::*; use deno_core::{ error::JsError, v8, v8::CreateParams, ModuleId, ModuleSpecifier, StaticModuleLoader, @@ -26,6 +25,7 @@ use deno_runtime::{ }; use nix::{libc, unistd::pipe}; use pegboard::protocol; +use pegboard_actor_kv::ActorKv; use pegboard_config::isolate_runner as config; use tokio::{fs, sync::mpsc}; use uuid::Uuid; diff --git a/packages/infra/client/isolate-v8-runner/src/main.rs b/packages/infra/client/isolate-v8-runner/src/main.rs index 267b1e1a50..489baf3124 100644 --- a/packages/infra/client/isolate-v8-runner/src/main.rs +++ b/packages/infra/client/isolate-v8-runner/src/main.rs @@ -7,13 +7,13 @@ use std::{ time::Duration, }; -use pegboard_actor_kv::ActorKv; use anyhow::*; use deno_core::{v8_set_flags, JsRuntime}; use deno_runtime::worker::MainWorkerTerminateHandle; use foundationdb as fdb; use futures_util::{stream::SplitStream, SinkExt, StreamExt}; use pegboard::protocol; +use pegboard_actor_kv::ActorKv; use pegboard_config::{isolate_runner::Config, runner_protocol}; use tokio::{ fs, diff --git a/packages/infra/server/Cargo.toml b/packages/infra/server/Cargo.toml index 7869f362fc..3015428953 100644 --- a/packages/infra/server/Cargo.toml +++ b/packages/infra/server/Cargo.toml @@ -17,6 +17,7 @@ colored_json = "5.0.0" global-error.workspace = true include_dir = "0.7.4" indoc = "2.0.5" +reqwest = "0.12.9" rivet-api.workspace = true rivet-migrate.workspace = true rivet-pools.workspace = true @@ -58,6 +59,7 @@ workflow-gc.workspace = true workflow-metrics-publish.workspace = true # Cron +cluster-tunnel-tls-renew.workspace = true telemetry-beacon.workspace = true user-delete-pending.workspace = true @@ -67,16 +69,14 @@ api-monolith-public.workspace = true # Oneshot build-default-create.workspace = true +chirp-client.workspace = true +chirp-workflow.workspace = true +cloud-default-create.workspace = true cluster-default-update.workspace = true pegboard-dc-init.workspace = true -rivet-config.workspace = true -reqwest = "0.12.9" -chirp-client.workspace = true rivet-cache.workspace = true -chirp-workflow.workspace = true +rivet-config.workspace = true rivet-connection.workspace = true -cloud-default-create.workspace = true - [dependencies.sqlx] workspace = true diff --git a/packages/infra/server/src/commands/start.rs b/packages/infra/server/src/commands/start.rs index 2726ee1aab..39f73fe204 100644 --- a/packages/infra/server/src/commands/start.rs +++ b/packages/infra/server/src/commands/start.rs @@ -65,7 +65,7 @@ impl Opts { run_config .services .iter() - .filter(|x| service_kinds.iter().any(|y| *y == x.kind)) + .filter(|x| service_kinds.iter().any(|y| y.eq(&x.kind))) .cloned() .collect::>() }; diff --git a/packages/infra/server/src/run_config.rs b/packages/infra/server/src/run_config.rs index 2b584571d8..439711bc31 100644 --- a/packages/infra/server/src/run_config.rs +++ b/packages/infra/server/src/run_config.rs @@ -112,6 +112,15 @@ pub fn config(rivet_config: rivet_config::Config) -> Result { ServiceKind::Singleton, |config, pools| Box::pin(cluster_datacenter_tls_renew::start(config, pools)), )); + + services.push(Service::new( + "cluster_tunnel_tls_renew", + ServiceKind::Cron(CronConfig { + run_immediately: true, + schedule: "0 0 0 1 * *".into(), + }), + |config, pools| Box::pin(cluster_tunnel_tls_renew::start(config, pools)), + )); } if server_config.rivet.auth.access_kind == rivet_config::config::rivet::AccessKind::Development diff --git a/packages/services/build/tests/patch_tags.rs b/packages/services/build/tests/patch_tags.rs index f5ab607065..2dc324d6a8 100644 --- a/packages/services/build/tests/patch_tags.rs +++ b/packages/services/build/tests/patch_tags.rs @@ -316,7 +316,11 @@ async fn multiple_exclusive_tags(ctx: TestCtx) { ("current".to_string(), Some("true".to_string())), ("other".to_string(), Some("tag".to_string())), ]), - exclusive_tags: Some(vec!["version".to_string(), "current".to_string(), "other".to_string()]), + exclusive_tags: Some(vec![ + "version".to_string(), + "current".to_string(), + "other".to_string(), + ]), }) .await .unwrap(); diff --git a/packages/services/cluster/db/cluster/migrations/20250107005643_add_tunnel_tls.down.sql b/packages/services/cluster/db/cluster/migrations/20250107005643_add_tunnel_tls.down.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/services/cluster/db/cluster/migrations/20250107005643_add_tunnel_tls.up.sql b/packages/services/cluster/db/cluster/migrations/20250107005643_add_tunnel_tls.up.sql new file mode 100644 index 0000000000..d2f9162085 --- /dev/null +++ b/packages/services/cluster/db/cluster/migrations/20250107005643_add_tunnel_tls.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE tunnel_tls ( + _id INT PRIMARY KEY, -- Solely to allow ON CONFLICT, there should only be 1 row in this table + cert_pem TEXT, + private_key_pem TEXT, + state INT NOT NULL, -- cluster::types::TlsState + expire_ts INT NOT NULL +); diff --git a/packages/services/cluster/src/lib.rs b/packages/services/cluster/src/lib.rs index 20b9d4e639..8272845cb0 100644 --- a/packages/services/cluster/src/lib.rs +++ b/packages/services/cluster/src/lib.rs @@ -1,10 +1,10 @@ use chirp_workflow::prelude::*; +pub mod metrics; pub mod ops; pub mod types; pub mod util; pub mod workflows; -pub mod metrics; pub fn registry() -> WorkflowResult { use workflows::*; diff --git a/packages/services/cluster/src/ops/mod.rs b/packages/services/cluster/src/ops/mod.rs index d69e65b3d5..7598817e0e 100644 --- a/packages/services/cluster/src/ops/mod.rs +++ b/packages/services/cluster/src/ops/mod.rs @@ -4,3 +4,4 @@ pub mod get_for_game; pub mod list; pub mod resolve_for_name_id; pub mod server; +pub mod tunnel; diff --git a/packages/services/cluster/src/ops/tunnel/mod.rs b/packages/services/cluster/src/ops/tunnel/mod.rs new file mode 100644 index 0000000000..8284560ed3 --- /dev/null +++ b/packages/services/cluster/src/ops/tunnel/mod.rs @@ -0,0 +1 @@ +pub mod tls_get; diff --git a/packages/services/cluster/src/ops/tunnel/tls_get.rs b/packages/services/cluster/src/ops/tunnel/tls_get.rs new file mode 100644 index 0000000000..dc1ea1a93e --- /dev/null +++ b/packages/services/cluster/src/ops/tunnel/tls_get.rs @@ -0,0 +1,32 @@ +use chirp_workflow::prelude::*; + +use crate::types::TlsState; + +#[derive(Debug)] +pub struct Input {} + +#[derive(Debug)] +pub struct Output { + pub cert_pem: String, + pub private_key_pem: String, +} + +#[operation] +pub async fn cluster_datacenter_tls_get(ctx: &OperationCtx, input: &Input) -> GlobalResult { + let row = sql_fetch_optional!( + [ctx, (String, String)] + " + SELECT cert_pem, private_key_pem + FROM db_cluster.tunnel_tls + WHERE state != $1 + ", + TlsState::Creating as i64, + ) + .await?; + let (cert_pem, private_key_pem) = unwrap!(row, "tunnel tls not created yet"); + + Ok(Output { + cert_pem, + private_key_pem, + }) +} diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/components/rivet.rs b/packages/services/cluster/src/workflows/server/install/install_scripts/components/rivet.rs index 59c4d0f2e1..d2dbf2bee9 100644 --- a/packages/services/cluster/src/workflows/server/install/install_scripts/components/rivet.rs +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/components/rivet.rs @@ -22,14 +22,40 @@ pub fn fetch_info(server_token: &str) -> GlobalResult { )) } -pub fn fetch_tls( +pub fn fetch_tunnel_tls( + initialize_immediately: bool, + server_token: &str, + traefik_instance_name: &str, +) -> GlobalResult { + let mut script = include_str!("../files/rivet_fetch_tunnel_tls.sh") + .replace("__TRAEFIK_INSTANCE_NAME__", traefik_instance_name) + .replace("__SERVER_TOKEN__", server_token) + .replace( + "__TUNNEL_API_EDGE_API__", + &format!("http://127.0.0.1:{TUNNEL_API_EDGE_PORT}"), + ); + + if initialize_immediately { + // Start timer & run script immediately + script.push_str(indoc!( + " + systemctl start rivet_fetch_tunnel_tls.timer + systemctl start --no-block rivet_fetch_tunnel_tls.service + " + )); + } + + Ok(script) +} + +pub fn fetch_gg_tls( initialize_immediately: bool, server_token: &str, traefik_instance_name: &str, datacenter_id: Uuid, ) -> GlobalResult { - let mut script = include_str!("../files/rivet_fetch_tls.sh") - .replace("__NAME__", traefik_instance_name) + let mut script = include_str!("../files/rivet_fetch_gg_tls.sh") + .replace("__TRAEFIK_INSTANCE_NAME__", traefik_instance_name) .replace("__SERVER_TOKEN__", server_token) .replace( "__TUNNEL_API_EDGE_API__", @@ -41,8 +67,8 @@ pub fn fetch_tls( // Start timer & run script immediately script.push_str(indoc!( " - systemctl start rivet_fetch_tls.timer - systemctl start --no-block rivet_fetch_tls.service + systemctl start rivet_fetch_gg_tls.timer + systemctl start --no-block rivet_fetch_gg_tls.service " )); } diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/components/traefik.rs b/packages/services/cluster/src/workflows/server/install/install_scripts/components/traefik.rs index da1fe3fe22..13e275f008 100644 --- a/packages/services/cluster/src/workflows/server/install/install_scripts/components/traefik.rs +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/components/traefik.rs @@ -56,6 +56,7 @@ pub fn install() -> String { include_str!("../files/traefik.sh").to_string() } +#[derive(Clone)] pub struct TlsCert { pub cert_pem: String, pub key_pem: String, @@ -71,17 +72,18 @@ pub struct Instance { pub struct ServerTransport { pub server_name: String, pub root_cas: Vec, + /// IMPORTANT: Make sure the first cert is always the tunnel cert. pub certs: Vec, } /// Creates a Traefik instance. /// /// Requires `install()`. -pub fn instance(config: Instance) -> String { +pub fn instance(config: Instance) -> GlobalResult { let config_name = &config.name; let mut script = include_str!("../files/traefik_instance.sh") - .replace("__NAME__", &config.name) + .replace("__TRAEFIK_INSTANCE_NAME__", &config.name) .replace("__STATIC_CONFIG__", &config.static_config) .replace("__DYNAMIC_CONFIG__", &config.dynamic_config); @@ -150,10 +152,14 @@ pub fn instance(config: Instance) -> String { )); } - script + Ok(script) } -pub fn tunnel(config: &rivet_config::Config, name: &str) -> GlobalResult { +pub fn tunnel( + config: &rivet_config::Config, + name: &str, + tunnel_cert: &TlsCert, +) -> GlobalResult { // Build transports for each service let tls_config = &config.server()?.tls()?; let mut tcp_server_transports = HashMap::new(); @@ -163,20 +169,17 @@ pub fn tunnel(config: &rivet_config::Config, name: &str) -> GlobalResult ServerTransport { server_name: format!("{name}.tunnel.rivet.gg"), root_cas: vec![tls_config.root_ca_cert_pem.read().clone()], - certs: vec![TlsCert { - cert_pem: tls_config.cert_locally_signed_job_cert_pem.read().clone(), - key_pem: tls_config.cert_locally_signed_job_key_pem.read().clone(), - }], + certs: vec![tunnel_cert.clone()], }, ); } - Ok(instance(Instance { + instance(Instance { name: name.to_string(), static_config: tunnel_static_config(), dynamic_config: tunnel_dynamic_config(&config.server()?.rivet.tunnel.public_host), tcp_server_transports, - })) + }) } fn tunnel_static_config() -> String { diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_tls.sh b/packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_gg_tls.sh similarity index 68% rename from packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_tls.sh rename to packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_gg_tls.sh index 4ecba1379a..7fac502d15 100644 --- a/packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_tls.sh +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_gg_tls.sh @@ -1,17 +1,17 @@ # Create dir to hold TLS certs # # The Traefik install script also creates these directories (and chown them), -# but we need the dirs to exist for the rivet_fetch_tls.sh script to run before +# but we need the dirs to exist for the rivet_fetch_gg_tls.sh script to run before # Traefik is installed when using initialize_immediately. -mkdir -p /etc/__NAME__/dynamic/tls /etc/__NAME__/tls +mkdir -p /etc/__TRAEFIK_INSTANCE_NAME__/dynamic/tls /etc/__TRAEFIK_INSTANCE_NAME__/tls # Write script -cat << 'EOF' > /usr/bin/rivet_fetch_tls.sh +cat << 'EOF' > /usr/bin/rivet_fetch_gg_tls.sh #!/usr/bin/env bash set -eu -o pipefail CERT_ID="job" -STUB="/etc/__NAME__/tls/$CERT_ID" +STUB="/etc/__TRAEFIK_INSTANCE_NAME__/tls/$CERT_ID" # Retry script every 5 seconds until success @@ -31,20 +31,20 @@ echo $response | jq -r .job_cert_pem > "${STUB}_cert.pem" echo $response | jq -r .job_private_key_pem > "${STUB}_key.pem" # Write traefik config file -cat << EOF2 > "/etc/__NAME__/dynamic/tls/${CERT_ID}.toml" +cat << EOF2 > "/etc/__TRAEFIK_INSTANCE_NAME__/dynamic/tls/${CERT_ID}.toml" [[tls.certificates]] certFile = "${STUB}_cert.pem" keyFile = "${STUB}_key.pem" EOF2 # Force config reload -touch /etc/__NAME__/dynamic +touch /etc/__TRAEFIK_INSTANCE_NAME__/dynamic EOF -chmod +x /usr/bin/rivet_fetch_tls.sh +chmod +x /usr/bin/rivet_fetch_gg_tls.sh # Create systemd service file -cat << 'EOF' > /etc/systemd/system/rivet_fetch_tls.service +cat << 'EOF' > /etc/systemd/system/rivet_fetch_gg_tls.service [Unit] Description=Rivet TLS Fetch Requires=network-online.target @@ -54,14 +54,14 @@ After=network-online.target User=root Group=root Type=oneshot -ExecStart=/usr/bin/rivet_fetch_tls.sh +ExecStart=/usr/bin/rivet_fetch_gg_tls.sh [Install] WantedBy=multi-user.target EOF # Create systemd timer file -cat << 'EOF' > /etc/systemd/system/rivet_fetch_tls.timer +cat << 'EOF' > /etc/systemd/system/rivet_fetch_gg_tls.timer [Unit] Description=Runs TLS fetch every minute Requires=network-online.target @@ -74,7 +74,7 @@ OnBootSec=0 OnCalendar=*:0 # Prevent stampeding herd RandomizedDelaySec=60 -Unit=rivet_fetch_tls.service +Unit=rivet_fetch_gg_tls.service # Real time service CPUSchedulingPolicy=fifo @@ -89,5 +89,5 @@ EOF # Enable tls fetch script to run on reboot systemctl daemon-reload -systemctl enable rivet_fetch_tls.timer -systemctl enable rivet_fetch_tls.service +systemctl enable rivet_fetch_gg_tls.timer +systemctl enable rivet_fetch_gg_tls.service diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_tunnel_tls.sh b/packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_tunnel_tls.sh new file mode 100644 index 0000000000..f041de5f13 --- /dev/null +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/files/rivet_fetch_tunnel_tls.sh @@ -0,0 +1,91 @@ +# Create dir to hold TLS certs +# +# The Traefik install script also creates these directories (and chown them), +# but we need the dirs to exist for the rivet_fetch_tunnel_tls.sh script to run before +# Traefik is installed when using initialize_immediately. +mkdir -p /etc/__TRAEFIK_INSTANCE_NAME__/dynamic/tls /etc/__TRAEFIK_INSTANCE_NAME__/tls + +# Write script +cat << 'EOF' > /usr/bin/rivet_fetch_tunnel_tls.sh +#!/usr/bin/env bash +set -eu -o pipefail + +# Retry script every 5 seconds until success +echo 'Fetching rivet tls' +while true; do + response=$( + curl -f \ + -H "Authorization: Bearer __SERVER_TOKEN__" \ + "__TUNNEL_API_EDGE_API__/provision/tunnel/tls" + ) && break || sleep 5 +done + +echo "TLS received" + +# Write tls certs +for file in /etc/__TRAEFIK_INSTANCE_NAME__/tls/transport_*_cert_0_cert.pem; do + echo $response | jq -r .cert_pem > "$file" +done + +for file in /etc/__TRAEFIK_INSTANCE_NAME__/tls/transport_*_root_ca_0_cert.pem; do + echo $response | jq -r .root_ca_cert_pem > "$file" +done + +for file in /etc/__TRAEFIK_INSTANCE_NAME__/tls/transport_*_cert_0_key.pem; do + echo $response | jq -r .private_key_pem > "$file" +done + +# Force config reload +touch /etc/__TRAEFIK_INSTANCE_NAME__/dynamic +EOF + +chmod +x /usr/bin/rivet_fetch_tunnel_tls.sh + +# Create systemd service file +cat << 'EOF' > /etc/systemd/system/rivet_fetch_tunnel_tls.service +[Unit] +Description=Rivet TLS Fetch +Requires=network-online.target +After=network-online.target + +[Service] +User=root +Group=root +Type=oneshot +ExecStart=/usr/bin/rivet_fetch_tunnel_tls.sh + +[Install] +WantedBy=multi-user.target +EOF + +# Create systemd timer file +cat << 'EOF' > /etc/systemd/system/rivet_fetch_tunnel_tls.timer +[Unit] +Description=Runs TLS fetch every minute +Requires=network-online.target +After=network-online.target + +[Timer] +# Run immediately on startup +OnBootSec=0 +# Trigger every hour +OnCalendar=*:0 +# Prevent stampeding herd +RandomizedDelaySec=60 +Unit=rivet_fetch_tunnel_tls.service + +# Real time service +CPUSchedulingPolicy=fifo +# High CPU priority +CPUSchedulingPriority=90 +# Prevent killing from system OOM +OOMScoreAdjust=-800 + +[Install] +WantedBy=timers.target +EOF + +# Enable tls fetch script to run on reboot +systemctl daemon-reload +systemctl enable rivet_fetch_tunnel_tls.timer +systemctl enable rivet_fetch_tunnel_tls.service diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/files/traefik_instance.sh b/packages/services/cluster/src/workflows/server/install/install_scripts/files/traefik_instance.sh index 1c57f4085d..4ad6df4cb5 100644 --- a/packages/services/cluster/src/workflows/server/install/install_scripts/files/traefik_instance.sh +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/files/traefik_instance.sh @@ -1,34 +1,34 @@ -if ! id -u "__NAME__" &>/dev/null; then - useradd -r -s /bin/false __NAME__ +if ! id -u "__TRAEFIK_INSTANCE_NAME__" &>/dev/null; then + useradd -r -s /bin/false __TRAEFIK_INSTANCE_NAME__ fi -mkdir -p /etc/__NAME__ /etc/__NAME__/dynamic /etc/__NAME__/dynamic/tls /etc/__NAME__/tls /opt/__NAME__ +mkdir -p /etc/__TRAEFIK_INSTANCE_NAME__ /etc/__TRAEFIK_INSTANCE_NAME__/dynamic /etc/__TRAEFIK_INSTANCE_NAME__/dynamic/tls /etc/__TRAEFIK_INSTANCE_NAME__/tls /opt/__TRAEFIK_INSTANCE_NAME__ # Static config -cat << 'EOF' > /etc/__NAME__/traefik.toml +cat << 'EOF' > /etc/__TRAEFIK_INSTANCE_NAME__/traefik.toml __STATIC_CONFIG__ EOF # Dynamic config -cat << 'EOF' > /etc/__NAME__/dynamic/common.toml +cat << 'EOF' > /etc/__TRAEFIK_INSTANCE_NAME__/dynamic/common.toml __DYNAMIC_CONFIG__ EOF -chown -R __NAME__:__NAME__ /etc/__NAME__ /etc/__NAME__/dynamic /etc/__NAME__/dynamic/tls /etc/__NAME__/tls /opt/__NAME__ +chown -R __TRAEFIK_INSTANCE_NAME__:__TRAEFIK_INSTANCE_NAME__ /etc/__TRAEFIK_INSTANCE_NAME__ /etc/__TRAEFIK_INSTANCE_NAME__/dynamic /etc/__TRAEFIK_INSTANCE_NAME__/dynamic/tls /etc/__TRAEFIK_INSTANCE_NAME__/tls /opt/__TRAEFIK_INSTANCE_NAME__ # Systemd service # # See https://doc.traefik.io/traefik-enterprise/installing/on-premise/#systemd-linux-only -cat << 'EOF' > /etc/systemd/system/__NAME__.service +cat << 'EOF' > /etc/systemd/system/__TRAEFIK_INSTANCE_NAME__.service [Unit] -Description=__NAME__ +Description=__TRAEFIK_INSTANCE_NAME__ After=network-online.target Wants=network-online.target systemd-networkd-wait-online.service [Service] -User=__NAME__ -Group=__NAME__ -ExecStart=/usr/bin/traefik --configFile=/etc/__NAME__/traefik.toml +User=__TRAEFIK_INSTANCE_NAME__ +Group=__TRAEFIK_INSTANCE_NAME__ +ExecStart=/usr/bin/traefik --configFile=/etc/__TRAEFIK_INSTANCE_NAME__/traefik.toml PrivateTmp=true PrivateDevices=false ProtectHome=true @@ -54,6 +54,6 @@ EOF # Start and enable the service systemctl daemon-reload -systemctl enable __NAME__ -systemctl start __NAME__ +systemctl enable __TRAEFIK_INSTANCE_NAME__ +systemctl start __TRAEFIK_INSTANCE_NAME__ diff --git a/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs b/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs index a5c9e0baea..38c39864db 100644 --- a/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs +++ b/packages/services/cluster/src/workflows/server/install/install_scripts/mod.rs @@ -17,6 +17,7 @@ pub async fn gen_install( initialize_immediately: bool, server_token: &str, datacenter_id: Uuid, + tunnel_cert: &components::traefik::TlsCert, ) -> GlobalResult { // MARK: Common (pre) let mut script = vec![ @@ -24,7 +25,9 @@ pub async fn gen_install( components::node_exporter::install(), components::sysctl::install(), components::traefik::install(), - components::traefik::tunnel(config, TUNNEL_NAME)?, + // NOTE: TLS certs expire in a year, prebakes expire in 6 months + components::traefik::tunnel(config, TUNNEL_NAME, tunnel_cert)?, + components::rivet::fetch_tunnel_tls(initialize_immediately, server_token, TUNNEL_NAME)?, components::vector::install(), ]; @@ -40,7 +43,7 @@ pub async fn gen_install( script.push(components::nomad::install()); } PoolType::Gg => { - script.push(components::rivet::fetch_tls( + script.push(components::rivet::fetch_gg_tls( initialize_immediately, server_token, GG_TRAEFIK_INSTANCE_NAME, @@ -132,7 +135,7 @@ pub async fn gen_initialize( dynamic_config: components::traefik::gg_dynamic_config(config, datacenter_id)?, tcp_server_transports: Default::default(), }, - )); + )?); } PoolType::Ats => { script.push(components::traffic_server::configure(config).await?); diff --git a/packages/services/cluster/src/workflows/server/install/mod.rs b/packages/services/cluster/src/workflows/server/install/mod.rs index e8f0cb60ce..024e83ba42 100644 --- a/packages/services/cluster/src/workflows/server/install/mod.rs +++ b/packages/services/cluster/src/workflows/server/install/mod.rs @@ -9,8 +9,8 @@ use rivet_operation::prelude::proto::{self, backend::pkg::token}; use ssh2::Session; use crate::{ - types::{Datacenter, PoolType}, metrics, + types::{Datacenter, PoolType}, }; mod install_scripts; @@ -31,14 +31,32 @@ pub(crate) async fn cluster_server_install( ) -> GlobalResult<()> { let server_token = ctx.activity(CreateTokenInput {}).await?; - ctx.activity(InstallOverSshInput { - datacenter_id: input.datacenter_id, - public_ip: input.public_ip, - pool_type: input.pool_type, - initialize_immediately: input.initialize_immediately, - server_token, - }) - .await?; + match ctx.check_version(2).await? { + 1 => { + ctx.activity(InstallOverSshInputV1 { + datacenter_id: input.datacenter_id, + public_ip: input.public_ip, + pool_type: input.pool_type, + initialize_immediately: input.initialize_immediately, + server_token, + }) + .await?; + } + _ => { + let tunnel_cert_res = ctx.activity(ReadTunnelCertInput {}).await?; + + ctx.activity(InstallOverSshInputV2 { + datacenter_id: input.datacenter_id, + public_ip: input.public_ip, + pool_type: input.pool_type, + initialize_immediately: input.initialize_immediately, + server_token, + tunnel_cert_cert_pem: tunnel_cert_res.cert_pem, + tunnel_cert_private_key_pem: tunnel_cert_res.private_key_pem, + }) + .await?; + } + } // If the server id is set this is not a prebake server if let Some(server_id) = input.server_id { @@ -87,7 +105,29 @@ async fn create_token(ctx: &ActivityCtx, input: &CreateTokenInput) -> GlobalResu } #[derive(Debug, Serialize, Deserialize, Hash)] -struct InstallOverSshInput { +struct ReadTunnelCertInput {} + +#[derive(Debug, Serialize, Deserialize)] +struct ReadTunnelCertOutput { + cert_pem: String, + private_key_pem: String, +} + +#[activity(ReadTunnelCert)] +async fn read_tunnel_cert( + ctx: &ActivityCtx, + _input: &ReadTunnelCertInput, +) -> GlobalResult { + let tunnel_tls_res = ctx.op(crate::ops::tunnel::tls_get::Input {}).await?; + + Ok(ReadTunnelCertOutput { + cert_pem: tunnel_tls_res.cert_pem, + private_key_pem: tunnel_tls_res.private_key_pem, + }) +} + +#[derive(Debug, Serialize, Deserialize, Hash)] +struct InstallOverSshInputV1 { datacenter_id: Uuid, public_ip: Ipv4Addr, pool_type: PoolType, @@ -97,7 +137,24 @@ struct InstallOverSshInput { #[activity(InstallOverSsh)] #[timeout = 300] -async fn install_over_ssh(ctx: &ActivityCtx, input: &InstallOverSshInput) -> GlobalResult<()> { +async fn install_over_ssh(ctx: &ActivityCtx, _input: &InstallOverSshInputV1) -> GlobalResult<()> { + bail!("old version, should not rerun"); +} + +#[derive(Debug, Serialize, Deserialize, Hash)] +struct InstallOverSshInputV2 { + datacenter_id: Uuid, + public_ip: Ipv4Addr, + pool_type: PoolType, + initialize_immediately: bool, + server_token: String, + tunnel_cert_cert_pem: String, + tunnel_cert_private_key_pem: String, +} + +#[activity(InstallOverSshV2)] +#[timeout = 300] +async fn install_over_ssh_v2(ctx: &ActivityCtx, input: &InstallOverSshInputV2) -> GlobalResult<()> { let public_ip = input.public_ip; let private_key_openssh = ctx .config() @@ -114,6 +171,10 @@ async fn install_over_ssh(ctx: &ActivityCtx, input: &InstallOverSshInput) -> Glo input.initialize_immediately, &input.server_token, input.datacenter_id, + &install_scripts::components::traefik::TlsCert { + cert_pem: input.tunnel_cert_cert_pem.clone(), + key_pem: input.tunnel_cert_private_key_pem.clone(), + }, ) .await?; let hook_script = install_scripts::gen_hook(&input.server_token).await?; diff --git a/packages/services/cluster/src/workflows/server/mod.rs b/packages/services/cluster/src/workflows/server/mod.rs index 7644e8800c..8c58e69b9e 100644 --- a/packages/services/cluster/src/workflows/server/mod.rs +++ b/packages/services/cluster/src/workflows/server/mod.rs @@ -14,8 +14,8 @@ pub(crate) mod install; pub(crate) mod undrain; use crate::{ - types::{Pool, PoolType, Provider}, metrics, + types::{Pool, PoolType, Provider}, }; #[derive(Debug, Serialize, Deserialize)] diff --git a/packages/services/cluster/standalone/metrics-publish/src/lib.rs b/packages/services/cluster/standalone/metrics-publish/src/lib.rs index 125cc54814..45ef0f7be5 100644 --- a/packages/services/cluster/standalone/metrics-publish/src/lib.rs +++ b/packages/services/cluster/standalone/metrics-publish/src/lib.rs @@ -2,8 +2,8 @@ use std::convert::{TryFrom, TryInto}; use chirp_workflow::prelude::*; use cluster::{ - types::{Datacenter, PoolType}, metrics, + types::{Datacenter, PoolType}, }; pub async fn start(config: rivet_config::Config, pools: rivet_pools::Pools) -> GlobalResult<()> { diff --git a/packages/services/cluster/standalone/tunnel-tls-renew/Cargo.toml b/packages/services/cluster/standalone/tunnel-tls-renew/Cargo.toml new file mode 100644 index 0000000000..727383e381 --- /dev/null +++ b/packages/services/cluster/standalone/tunnel-tls-renew/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "cluster-tunnel-tls-renew" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + +[dependencies] +chirp-client.workspace = true +chirp-workflow.workspace = true +openssl = "0.10" +rivet-connection.workspace = true +rivet-health-checks.workspace = true +rivet-metrics.workspace = true +rivet-runtime.workspace = true +tokio = { version = "1.40", features = ["full"] } +tracing = "0.1" +tracing-logfmt = "0.3" +tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "json", "ansi"] } + +cluster.workspace = true +rivet-config.workspace = true + +[dependencies.sqlx] +workspace = true + +[dev-dependencies] diff --git a/packages/services/cluster/standalone/tunnel-tls-renew/src/lib.rs b/packages/services/cluster/standalone/tunnel-tls-renew/src/lib.rs new file mode 100644 index 0000000000..5012667577 --- /dev/null +++ b/packages/services/cluster/standalone/tunnel-tls-renew/src/lib.rs @@ -0,0 +1,138 @@ +use chirp_workflow::prelude::*; +use cluster::types::TlsState; +use openssl::asn1::Asn1Time; +use openssl::pkey::PKey; +use openssl::rsa::Rsa; +use openssl::x509::extension::{ExtendedKeyUsage, SubjectAlternativeName}; +use openssl::x509::{X509NameBuilder, X509Req, X509}; + +// How much time before the cert expires to renew it +const EXPIRE_PADDING: i64 = util::duration::days(30); +// Days +const VALIDITY_PERIOD: u32 = 365; + +pub async fn start(config: rivet_config::Config, pools: rivet_pools::Pools) -> GlobalResult<()> { + run_from_env(config.clone(), pools.clone(), util::timestamp::now()).await +} + +#[tracing::instrument(skip_all)] +pub async fn run_from_env( + config: rivet_config::Config, + pools: rivet_pools::Pools, + ts: i64, +) -> GlobalResult<()> { + let client = + chirp_client::SharedClient::from_env(pools.clone())?.wrap_new("cluster-tunnel-tls-issue"); + let cache = rivet_cache::CacheInner::from_env(pools.clone())?; + let ctx = StandaloneCtx::new( + chirp_workflow::compat::db_from_pools(&pools).await?, + config, + rivet_connection::Connection::new(client, pools, cache), + "cluster-tunnel-tls-issue", + ) + .await?; + + let (create_cert,) = sql_fetch_one!( + [ctx, (bool,)] + " + WITH + -- NULL if row does not exist, true if within EXPIRE_PADDING and state is Active + selected AS ( + SELECT (state = $5 AND expire_ts < $1 + $2) AS expiring + FROM db_cluster.tunnel_tls + ), + updating AS ( + INSERT INTO db_cluster.tunnel_tls (_id, cert_pem, private_key_pem, state, expire_ts) + VALUES (0, NULL, NULL, $3, $1) + ON CONFLICT (_id) DO UPDATE + SET state = CASE WHEN (SELECT expiring FROM selected) + THEN $4 + ELSE db_cluster.tunnel_tls.state + END + RETURNING 1 + ) + SELECT NOT EXISTS(SELECT 1 FROM selected) OR (SELECT expiring FROM selected) + ", + ts, + EXPIRE_PADDING, + TlsState::Creating as i64, + TlsState::Renewing as i64, + TlsState::Active as i64, + ) + .await?; + + if create_cert { + tracing::info!("creating new tunnel cert"); + + let tls_config = &ctx.config().server()?.tls()?; + let ca_cert_pem = tls_config.root_ca_cert_pem.read(); + let ca_private_key_pem = tls_config.root_ca_key_pem.read(); + let expire_ts = util::timestamp::now() + util::duration::days(VALIDITY_PERIOD as i64); + + // Generate private key + let rsa_key = Rsa::generate(2048)?; + let private_key = PKey::from_rsa(rsa_key)?; + + // Create X.509 certificate request + let mut name_builder = X509NameBuilder::new()?; + name_builder.append_entry_by_text("CN", "Tunnel Client")?; + name_builder.append_entry_by_text("O", "Rivet Gaming, Inc.")?; + let name = name_builder.build(); + + let mut req = X509Req::builder()?; + req.set_subject_name(&name)?; + req.set_pubkey(&private_key)?; + req.sign(&private_key, openssl::hash::MessageDigest::sha256())?; + let cert_req = req.build(); + + // Load CA private key and certificate + let ca_cert = X509::from_pem(ca_cert_pem.as_bytes())?; + let ca_key = PKey::private_key_from_pem(ca_private_key_pem.as_bytes())?; + + // Create certificate from the certificate request + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + cert_builder.set_subject_name(cert_req.subject_name())?; + cert_builder.set_issuer_name(ca_cert.subject_name())?; + cert_builder.set_pubkey(&private_key)?; + cert_builder.set_not_before(Asn1Time::days_from_now(0)?.as_ref())?; + cert_builder.set_not_after(Asn1Time::days_from_now(VALIDITY_PERIOD)?.as_ref())?; // 1 year validity + + // Add extensions + let key_usage = ExtendedKeyUsage::new().client_auth().build()?; + cert_builder.append_extension(key_usage)?; + + let san = SubjectAlternativeName::new() + .dns("*.tunnel.rivet.gg") + .build(&cert_builder.x509v3_context(Some(&ca_cert), None))?; + cert_builder.append_extension(san)?; + + // Sign the certificate + cert_builder.sign(&ca_key, openssl::hash::MessageDigest::sha256())?; + let cert = cert_builder.build(); + + let cert_pem = cert.to_pem()?; + let cert_pem = std::str::from_utf8(&cert_pem)?; + let private_key_pem = private_key.private_key_to_pem_pkcs8()?; + let private_key_pem = std::str::from_utf8(&private_key_pem)?; + + sql_execute!( + [ctx] + " + UPDATE db_cluster.tunnel_tls + SET + cert_pem = $1, + private_key_pem = $2, + state = $3, + expire_ts = $4 + ", + cert_pem, + private_key_pem, + TlsState::Active as i64, + expire_ts, + ) + .await?; + } + + Ok(()) +} diff --git a/packages/services/ds/src/workers/undrain_all.rs b/packages/services/ds/src/workers/undrain_all.rs index 7038432215..679ca162c5 100644 --- a/packages/services/ds/src/workers/undrain_all.rs +++ b/packages/services/ds/src/workers/undrain_all.rs @@ -42,7 +42,7 @@ async fn worker(ctx: &OperationContext) -> Global }; for (server_id,) in server_rows { - chirp_workflow::compat::signal(ctx, crate::workflows::server::Undrain { }) + chirp_workflow::compat::signal(ctx, crate::workflows::server::Undrain {}) .await? .tag("server_id", server_id) .send() diff --git a/packages/services/ds/src/workflows/server/pegboard/mod.rs b/packages/services/ds/src/workflows/server/pegboard/mod.rs index beec5f2b86..f96a65c246 100644 --- a/packages/services/ds/src/workflows/server/pegboard/mod.rs +++ b/packages/services/ds/src/workflows/server/pegboard/mod.rs @@ -758,9 +758,12 @@ async fn reschedule_actor( (retry_count - 1).try_into()?, ); let next = backoff.step().expect("should not have max retry"); - + // Sleep for backoff or destroy early - if let Some(sig) = ctx.listen_with_timeout::(next - Instant::now()).await? { + if let Some(sig) = ctx + .listen_with_timeout::(next - Instant::now()) + .await? + { tracing::debug!("destroying before actor start"); return Ok(Loop::Break(Some(sig))); diff --git a/packages/services/pegboard/src/workflows/client.rs b/packages/services/pegboard/src/workflows/client.rs index c88dfad545..6a7f260889 100644 --- a/packages/services/pegboard/src/workflows/client.rs +++ b/packages/services/pegboard/src/workflows/client.rs @@ -470,7 +470,10 @@ pub async fn handle_commands( // Update actor state based on commands for command in commands { if let protocol::Command::SignalActor { - actor_id, signal, ignore_future_state, .. + actor_id, + signal, + ignore_future_state, + .. } = command { if matches!(signal.try_into()?, Signal::SIGTERM | Signal::SIGKILL) { diff --git a/packages/services/pegboard/standalone/metrics-publish/src/lib.rs b/packages/services/pegboard/standalone/metrics-publish/src/lib.rs index 4f7637a40c..c30f486a79 100644 --- a/packages/services/pegboard/standalone/metrics-publish/src/lib.rs +++ b/packages/services/pegboard/standalone/metrics-publish/src/lib.rs @@ -1,8 +1,7 @@ use chirp_workflow::prelude::*; pub async fn start(config: rivet_config::Config, pools: rivet_pools::Pools) -> GlobalResult<()> { - let mut interval = - tokio::time::interval(std::time::Duration::from_secs(15)); + let mut interval = tokio::time::interval(std::time::Duration::from_secs(15)); loop { interval.tick().await; diff --git a/packages/toolchain/js-utils-embed/build.rs b/packages/toolchain/js-utils-embed/build.rs index 168102449a..4d63b45d42 100644 --- a/packages/toolchain/js-utils-embed/build.rs +++ b/packages/toolchain/js-utils-embed/build.rs @@ -22,9 +22,7 @@ async fn main() -> Result<()> { } // Create the target directory first - let copy_options = CopyOptions::new() - .overwrite(true) - .copy_inside(true); + let copy_options = CopyOptions::new().overwrite(true).copy_inside(true); copy(&js_utils_path, &out_js_utils_path, ©_options).with_context(|| { format!( diff --git a/sdks/api/fern/definition/provision/tunnel/__package__.yml b/sdks/api/fern/definition/provision/tunnel/__package__.yml new file mode 100644 index 0000000000..6d596b93d1 --- /dev/null +++ b/sdks/api/fern/definition/provision/tunnel/__package__.yml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json + +service: + auth: true + base-path: /tunnel + endpoints: + getTls: + path: /tls + method: GET + response: GetTlsResponse + +types: + GetTlsResponse: + properties: + cert_pem: string + root_ca_cert_pem: string + private_key_pem: string diff --git a/sdks/api/full/go/provision/client/client.go b/sdks/api/full/go/provision/client/client.go index a504f9b355..22f646dfc9 100644 --- a/sdks/api/full/go/provision/client/client.go +++ b/sdks/api/full/go/provision/client/client.go @@ -7,6 +7,7 @@ import ( core "sdk/core" datacentersclient "sdk/provision/datacenters/client" serversclient "sdk/provision/servers/client" + tunnelclient "sdk/provision/tunnel/client" ) type Client struct { @@ -16,6 +17,7 @@ type Client struct { Datacenters *datacentersclient.Client Servers *serversclient.Client + Tunnel *tunnelclient.Client } func NewClient(opts ...core.ClientOption) *Client { @@ -29,5 +31,6 @@ func NewClient(opts ...core.ClientOption) *Client { header: options.ToHeader(), Datacenters: datacentersclient.NewClient(opts...), Servers: serversclient.NewClient(opts...), + Tunnel: tunnelclient.NewClient(opts...), } } diff --git a/sdks/api/full/go/provision/tunnel/client/client.go b/sdks/api/full/go/provision/tunnel/client/client.go new file mode 100644 index 0000000000..1c7a16cfcf --- /dev/null +++ b/sdks/api/full/go/provision/tunnel/client/client.go @@ -0,0 +1,110 @@ +// This file was auto-generated by Fern from our API Definition. + +package client + +import ( + bytes "bytes" + context "context" + json "encoding/json" + errors "errors" + io "io" + http "net/http" + sdk "sdk" + core "sdk/core" + tunnel "sdk/provision/tunnel" +) + +type Client struct { + baseURL string + caller *core.Caller + header http.Header +} + +func NewClient(opts ...core.ClientOption) *Client { + options := core.NewClientOptions() + for _, opt := range opts { + opt(options) + } + return &Client{ + baseURL: options.BaseURL, + caller: core.NewCaller(options.HTTPClient), + header: options.ToHeader(), + } +} + +func (c *Client) GetTls(ctx context.Context) (*tunnel.GetTlsResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := baseURL + "/" + "tunnel/tls" + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *tunnel.GetTlsResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: c.header, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} diff --git a/sdks/api/full/go/provision/tunnel/tunnel.go b/sdks/api/full/go/provision/tunnel/tunnel.go new file mode 100644 index 0000000000..09a4fc2311 --- /dev/null +++ b/sdks/api/full/go/provision/tunnel/tunnel.go @@ -0,0 +1,40 @@ +// This file was auto-generated by Fern from our API Definition. + +package tunnel + +import ( + json "encoding/json" + fmt "fmt" + core "sdk/core" +) + +type GetTlsResponse struct { + CertPem string `json:"cert_pem"` + RootCaCertPem string `json:"root_ca_cert_pem"` + PrivateKeyPem string `json:"private_key_pem"` + + _rawJSON json.RawMessage +} + +func (g *GetTlsResponse) UnmarshalJSON(data []byte) error { + type unmarshaler GetTlsResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *g = GetTlsResponse(value) + g._rawJSON = json.RawMessage(data) + return nil +} + +func (g *GetTlsResponse) String() string { + if len(g._rawJSON) > 0 { + if value, err := core.StringifyJSON(g._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(g); err == nil { + return value + } + return fmt.Sprintf("%#v", g) +} diff --git a/sdks/api/full/openapi/openapi.yml b/sdks/api/full/openapi/openapi.yml index 6c68bfa151..9df76ca00a 100644 --- a/sdks/api/full/openapi/openapi.yml +++ b/sdks/api/full/openapi/openapi.yml @@ -4114,6 +4114,56 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 + /tunnel/tls: + get: + operationId: provision_tunnel_getTls + tags: + - ProvisionTunnel + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ProvisionTunnelGetTlsResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 /games/{game_id}/environments/{environment_id}/servers/{server_id}: get: description: Gets a dynamic server. @@ -9826,6 +9876,19 @@ components: - wan_ip - vlan_ip - public_ip + ProvisionTunnelGetTlsResponse: + type: object + properties: + cert_pem: + type: string + root_ca_cert_pem: + type: string + private_key_pem: + type: string + required: + - cert_pem + - root_ca_cert_pem + - private_key_pem ServersGetServerResponse: type: object properties: diff --git a/sdks/api/full/openapi_compat/openapi.yml b/sdks/api/full/openapi_compat/openapi.yml index 8d89eef9e7..b80250f728 100644 --- a/sdks/api/full/openapi_compat/openapi.yml +++ b/sdks/api/full/openapi_compat/openapi.yml @@ -4114,6 +4114,56 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 + /tunnel/tls: + get: + operationId: provision_tunnel_getTls + tags: + - ProvisionTunnel + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ProvisionTunnelGetTlsResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 '/games/{game_id}/environments/{environment_id}/servers/{server_id}': get: description: Gets a dynamic server. @@ -9826,6 +9876,19 @@ components: - wan_ip - vlan_ip - public_ip + ProvisionTunnelGetTlsResponse: + type: object + properties: + cert_pem: + type: string + root_ca_cert_pem: + type: string + private_key_pem: + type: string + required: + - cert_pem + - root_ca_cert_pem + - private_key_pem ServersGetServerResponse: type: object properties: diff --git a/sdks/api/full/rust/.openapi-generator/FILES b/sdks/api/full/rust/.openapi-generator/FILES index 75f1b888f0..45c2017a87 100644 --- a/sdks/api/full/rust/.openapi-generator/FILES +++ b/sdks/api/full/rust/.openapi-generator/FILES @@ -337,6 +337,8 @@ docs/ProvisionPoolType.md docs/ProvisionServer.md docs/ProvisionServersApi.md docs/ProvisionServersGetInfoResponse.md +docs/ProvisionTunnelApi.md +docs/ProvisionTunnelGetTlsResponse.md docs/ServersApi.md docs/ServersBuild.md docs/ServersBuildCompression.md @@ -413,6 +415,7 @@ src/apis/mod.rs src/apis/portal_games_api.rs src/apis/provision_datacenters_api.rs src/apis/provision_servers_api.rs +src/apis/provision_tunnel_api.rs src/apis/servers_api.rs src/apis/servers_builds_api.rs src/apis/servers_datacenters_api.rs @@ -716,6 +719,7 @@ src/models/provision_datacenters_get_tls_response.rs src/models/provision_pool_type.rs src/models/provision_server.rs src/models/provision_servers_get_info_response.rs +src/models/provision_tunnel_get_tls_response.rs src/models/servers_build.rs src/models/servers_build_compression.rs src/models/servers_build_kind.rs diff --git a/sdks/api/full/rust/README.md b/sdks/api/full/rust/README.md index eedabdb45b..646d4e17cf 100644 --- a/sdks/api/full/rust/README.md +++ b/sdks/api/full/rust/README.md @@ -150,6 +150,7 @@ Class | Method | HTTP request | Description *ProvisionDatacentersApi* | [**provision_datacenters_get_servers**](docs/ProvisionDatacentersApi.md#provision_datacenters_get_servers) | **GET** /datacenters/{datacenter_id}/servers | *ProvisionDatacentersApi* | [**provision_datacenters_get_tls**](docs/ProvisionDatacentersApi.md#provision_datacenters_get_tls) | **GET** /datacenters/{datacenter_id}/tls | *ProvisionServersApi* | [**provision_servers_get_info**](docs/ProvisionServersApi.md#provision_servers_get_info) | **GET** /servers/{ip} | +*ProvisionTunnelApi* | [**provision_tunnel_get_tls**](docs/ProvisionTunnelApi.md#provision_tunnel_get_tls) | **GET** /tunnel/tls | *ServersApi* | [**servers_create**](docs/ServersApi.md#servers_create) | **POST** /games/{game_id}/environments/{environment_id}/servers | *ServersApi* | [**servers_destroy**](docs/ServersApi.md#servers_destroy) | **DELETE** /games/{game_id}/environments/{environment_id}/servers/{server_id} | *ServersApi* | [**servers_get**](docs/ServersApi.md#servers_get) | **GET** /games/{game_id}/environments/{environment_id}/servers/{server_id} | @@ -462,6 +463,7 @@ Class | Method | HTTP request | Description - [ProvisionPoolType](docs/ProvisionPoolType.md) - [ProvisionServer](docs/ProvisionServer.md) - [ProvisionServersGetInfoResponse](docs/ProvisionServersGetInfoResponse.md) + - [ProvisionTunnelGetTlsResponse](docs/ProvisionTunnelGetTlsResponse.md) - [ServersBuild](docs/ServersBuild.md) - [ServersBuildCompression](docs/ServersBuildCompression.md) - [ServersBuildKind](docs/ServersBuildKind.md) diff --git a/sdks/api/full/rust/docs/ProvisionTunnelApi.md b/sdks/api/full/rust/docs/ProvisionTunnelApi.md new file mode 100644 index 0000000000..eda228eade --- /dev/null +++ b/sdks/api/full/rust/docs/ProvisionTunnelApi.md @@ -0,0 +1,34 @@ +# \ProvisionTunnelApi + +All URIs are relative to *https://api.rivet.gg* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**provision_tunnel_get_tls**](ProvisionTunnelApi.md#provision_tunnel_get_tls) | **GET** /tunnel/tls | + + + +## provision_tunnel_get_tls + +> crate::models::ProvisionTunnelGetTlsResponse provision_tunnel_get_tls() + + +### Parameters + +This endpoint does not need any parameter. + +### Return type + +[**crate::models::ProvisionTunnelGetTlsResponse**](ProvisionTunnelGetTlsResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/api/full/rust/docs/ProvisionTunnelGetTlsResponse.md b/sdks/api/full/rust/docs/ProvisionTunnelGetTlsResponse.md new file mode 100644 index 0000000000..e79186563d --- /dev/null +++ b/sdks/api/full/rust/docs/ProvisionTunnelGetTlsResponse.md @@ -0,0 +1,13 @@ +# ProvisionTunnelGetTlsResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**cert_pem** | **String** | | +**root_ca_cert_pem** | **String** | | +**private_key_pem** | **String** | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/api/full/rust/src/apis/mod.rs b/sdks/api/full/rust/src/apis/mod.rs index b8ab9f2881..6136df0cb0 100644 --- a/sdks/api/full/rust/src/apis/mod.rs +++ b/sdks/api/full/rust/src/apis/mod.rs @@ -149,6 +149,7 @@ pub mod matchmaker_regions_api; pub mod portal_games_api; pub mod provision_datacenters_api; pub mod provision_servers_api; +pub mod provision_tunnel_api; pub mod servers_api; pub mod servers_builds_api; pub mod servers_datacenters_api; diff --git a/sdks/api/full/rust/src/apis/provision_tunnel_api.rs b/sdks/api/full/rust/src/apis/provision_tunnel_api.rs new file mode 100644 index 0000000000..6dde5f27fc --- /dev/null +++ b/sdks/api/full/rust/src/apis/provision_tunnel_api.rs @@ -0,0 +1,66 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + +use reqwest; + +use super::{configuration, Error}; +use crate::apis::ResponseContent; + +/// struct for typed errors of method [`provision_tunnel_get_tls`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ProvisionTunnelGetTlsError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +pub async fn provision_tunnel_get_tls( + configuration: &configuration::Configuration, +) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/tunnel/tls", local_var_configuration.base_path); + let mut local_var_req_builder = + local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = + local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = + serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { + status: local_var_status, + content: local_var_content, + entity: local_var_entity, + }; + Err(Error::ResponseError(local_var_error)) + } +} diff --git a/sdks/api/full/rust/src/models/mod.rs b/sdks/api/full/rust/src/models/mod.rs index 96d76869cb..5b72eb89d6 100644 --- a/sdks/api/full/rust/src/models/mod.rs +++ b/sdks/api/full/rust/src/models/mod.rs @@ -592,6 +592,8 @@ pub mod provision_server; pub use self::provision_server::ProvisionServer; pub mod provision_servers_get_info_response; pub use self::provision_servers_get_info_response::ProvisionServersGetInfoResponse; +pub mod provision_tunnel_get_tls_response; +pub use self::provision_tunnel_get_tls_response::ProvisionTunnelGetTlsResponse; pub mod servers_build; pub use self::servers_build::ServersBuild; pub mod servers_build_compression; diff --git a/sdks/api/full/rust/src/models/provision_tunnel_get_tls_response.rs b/sdks/api/full/rust/src/models/provision_tunnel_get_tls_response.rs new file mode 100644 index 0000000000..ec3d3f1661 --- /dev/null +++ b/sdks/api/full/rust/src/models/provision_tunnel_get_tls_response.rs @@ -0,0 +1,33 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ProvisionTunnelGetTlsResponse { + #[serde(rename = "cert_pem")] + pub cert_pem: String, + #[serde(rename = "root_ca_cert_pem")] + pub root_ca_cert_pem: String, + #[serde(rename = "private_key_pem")] + pub private_key_pem: String, +} + +impl ProvisionTunnelGetTlsResponse { + pub fn new( + cert_pem: String, + root_ca_cert_pem: String, + private_key_pem: String, + ) -> ProvisionTunnelGetTlsResponse { + ProvisionTunnelGetTlsResponse { + cert_pem, + root_ca_cert_pem, + private_key_pem, + } + } +} diff --git a/sdks/api/full/typescript/archive.tgz b/sdks/api/full/typescript/archive.tgz index e6455802f2..ee8a3bcea2 100644 --- a/sdks/api/full/typescript/archive.tgz +++ b/sdks/api/full/typescript/archive.tgz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dba3110c83024b23a70929ad5040aa11ee136c243450bcbbd58d0fa41832569 -size 540391 +oid sha256:b16eb054e82b60cb6a8a8e0cdbf5ec75a74e8fd876f7cb036d758171a99d6fa9 +size 543153 diff --git a/sdks/api/full/typescript/src/api/resources/provision/client/Client.ts b/sdks/api/full/typescript/src/api/resources/provision/client/Client.ts index 4a2ccb16e4..8389648bf0 100644 --- a/sdks/api/full/typescript/src/api/resources/provision/client/Client.ts +++ b/sdks/api/full/typescript/src/api/resources/provision/client/Client.ts @@ -6,6 +6,7 @@ import * as environments from "../../../../environments"; import * as core from "../../../../core"; import { Datacenters } from "../resources/datacenters/client/Client"; import { Servers } from "../resources/servers/client/Client"; +import { Tunnel } from "../resources/tunnel/client/Client"; export declare namespace Provision { interface Options { @@ -40,4 +41,10 @@ export class Provision { public get servers(): Servers { return (this._servers ??= new Servers(this._options)); } + + protected _tunnel: Tunnel | undefined; + + public get tunnel(): Tunnel { + return (this._tunnel ??= new Tunnel(this._options)); + } } diff --git a/sdks/api/full/typescript/src/api/resources/provision/resources/index.ts b/sdks/api/full/typescript/src/api/resources/provision/resources/index.ts index 7faf10c890..35db4d993a 100644 --- a/sdks/api/full/typescript/src/api/resources/provision/resources/index.ts +++ b/sdks/api/full/typescript/src/api/resources/provision/resources/index.ts @@ -1,4 +1,5 @@ export * as datacenters from "./datacenters"; export * as servers from "./servers"; +export * as tunnel from "./tunnel"; export * as common from "./common"; export * from "./common/types"; diff --git a/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/client/Client.ts b/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/client/Client.ts new file mode 100644 index 0000000000..60522212ed --- /dev/null +++ b/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/client/Client.ts @@ -0,0 +1,170 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments"; +import * as core from "../../../../../../core"; +import * as Rivet from "../../../../../index"; +import urlJoin from "url-join"; +import * as serializers from "../../../../../../serialization/index"; +import * as errors from "../../../../../../errors/index"; + +export declare namespace Tunnel { + interface Options { + environment?: core.Supplier; + token?: core.Supplier; + fetcher?: core.FetchFunction; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional headers to include in the request. */ + headers?: Record; + } +} + +export class Tunnel { + constructor(protected readonly _options: Tunnel.Options = {}) {} + + /** + * @param {Tunnel.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + * + * @example + * await client.provision.tunnel.getTls() + */ + public async getTls(requestOptions?: Tunnel.RequestOptions): Promise { + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + "/tunnel/tls" + ), + method: "GET", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + ...requestOptions?.headers, + }, + contentType: "application/json", + requestType: "json", + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return serializers.provision.tunnel.GetTlsResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError("Timeout exceeded when calling GET /tunnel/tls."); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/client/index.ts b/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/client/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/index.ts b/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/index.ts new file mode 100644 index 0000000000..c9240f83b4 --- /dev/null +++ b/sdks/api/full/typescript/src/api/resources/provision/resources/tunnel/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./client"; diff --git a/sdks/api/full/typescript/src/serialization/resources/provision/resources/index.ts b/sdks/api/full/typescript/src/serialization/resources/provision/resources/index.ts index 7faf10c890..35db4d993a 100644 --- a/sdks/api/full/typescript/src/serialization/resources/provision/resources/index.ts +++ b/sdks/api/full/typescript/src/serialization/resources/provision/resources/index.ts @@ -1,4 +1,5 @@ export * as datacenters from "./datacenters"; export * as servers from "./servers"; +export * as tunnel from "./tunnel"; export * as common from "./common"; export * from "./common/types"; diff --git a/sdks/api/full/typescript/src/serialization/resources/provision/resources/tunnel/index.ts b/sdks/api/full/typescript/src/serialization/resources/provision/resources/tunnel/index.ts new file mode 100644 index 0000000000..eea524d655 --- /dev/null +++ b/sdks/api/full/typescript/src/serialization/resources/provision/resources/tunnel/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/sdks/api/runtime/typescript/archive.tgz b/sdks/api/runtime/typescript/archive.tgz index 0d6888d834..9d8b45cab4 100644 --- a/sdks/api/runtime/typescript/archive.tgz +++ b/sdks/api/runtime/typescript/archive.tgz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a473944b34cf1275f6249e0096888c20237c1990540ff15e4ccfaeb118354cb3 -size 279718 +oid sha256:597e2bbd7572e5458b834a86bf4f5618c40e1f5c83a3c04afca15e78a8a6f813 +size 280111