Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add ip whitelist to tunnels #930

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions infra/tf/k8s_infra/traefik_tunnel.tf
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ resource "helm_release" "traefik_tunnel" {
enabled = true
options = "ingress-tunnel"
}

# Created in svc/api/traefik-provider/src/route/tunnel.rs
middlewares = [
"tunnel-ip-allowlist@http"
]
}
}

Expand Down Expand Up @@ -138,6 +143,11 @@ resource "helm_release" "traefik_tunnel" {
}
} : null

additionalArguments = [
"--providers.http.endpoint=http://rivet-api-internal-monolith.rivet-service.svc.cluster.local/traefik-provider/config/tunnel?token=${module.traefik_secrets.values["rivet/api_traefik_provider/token"]}",
"--providers.http.pollInterval=2.5s",
]

logs = {
# general = {
# level = "DEBUG"
Expand Down
2 changes: 1 addition & 1 deletion infra/tf/tls/tunnel_server.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ resource "tls_locally_signed_cert" "locally_signed_tunnel_server" {
ca_key_algorithm = "RSA"
ca_private_key_pem = tls_private_key.root_ca.private_key_pem
ca_cert_pem = tls_self_signed_cert.root_ca.cert_pem

validity_period_hours = 8760 # 1 year

allowed_uses = [
Expand Down
1 change: 1 addition & 0 deletions svc/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions svc/api/traefik-provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,20 @@ util-cdn = { package = "rivet-util-cdn", path = "../../pkg/cdn/util" }
util-job = { package = "rivet-util-job", path = "../../pkg/job/util" }
uuid = { version = "1", features = ["v4"] }

cluster-server-list = { path = "../../pkg/cluster/ops/server-list" }

[dev-dependencies]
rivet-connection = { path = "../../../lib/connection" }
rivet-route = { path = "../../../lib/smithy-output/api-traefik-provider/rust" }
base64 = "0.13"
reqwest = "0.11"

cdn-namespace-domain-create = { path = "../../pkg/cdn/ops/namespace-domain-create" }
cdn-namespace-auth-user-update = { path = "../../pkg/cdn/ops/namespace-auth-user-update" }
faker-game = { path = "../../pkg/faker/ops/game" }
cdn-namespace-domain-create = { path = "../../pkg/cdn/ops/namespace-domain-create" }
faker-cdn-site = { path = "../../pkg/faker/ops/cdn-site" }
faker-game = { path = "../../pkg/faker/ops/game" }
faker-game-namespace = { path = "../../pkg/faker/ops/game-namespace" }
faker-job-run = { path = "../../pkg/faker/ops/job-run" }
faker-game-version = { path = "../../pkg/faker/ops/game-version" }
faker-job-run = { path = "../../pkg/faker/ops/job-run" }
faker-region = { path = "../../pkg/faker/ops/region" }
game-get = { path = "../../pkg/game/ops/get" }
28 changes: 16 additions & 12 deletions svc/api/traefik-provider/src/route/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ pub async fn config(
) -> GlobalResult<types::TraefikConfigResponseNullified> {
ctx.auth().token(&token).await?;

let mut config = types::TraefikConfigResponse::default();

// Fetch configs and catch any errors
let config = build_cdn(&ctx).await?;
build_cdn(&ctx, &mut config).await?;

// tracing::info!(
// http_services = ?config.http.services.len(),
Expand All @@ -47,6 +49,13 @@ pub async fn config(
// "traefik config"
// );

tracing::info!(
services = ?config.http.services.len(),
routers = config.http.routers.len(),
middlewares = ?config.http.middlewares.len(),
"cdn traefik config"
);

Ok(types::TraefikConfigResponseNullified {
http: config.http.nullified(),
tcp: config.tcp.nullified(),
Expand All @@ -56,16 +65,18 @@ pub async fn config(

/// Builds configuration for CDN routes.
#[tracing::instrument(skip(ctx))]
pub async fn build_cdn(ctx: &Ctx<Auth>) -> GlobalResult<types::TraefikConfigResponse> {
let mut config = types::TraefikConfigResponse::default();
pub async fn build_cdn(
ctx: &Ctx<Auth>,
config: &mut types::TraefikConfigResponse,
) -> GlobalResult<()> {
let s3_client = s3_util::Client::from_env("bucket-cdn").await?;

let redis_cdn = ctx.op_ctx().redis_cdn().await?;
let cdn_fetch = fetch_cdn(redis_cdn).await?;

// Process namespaces
for ns in &cdn_fetch {
let register_res = register_namespace(ns, &mut config, &s3_client);
let register_res = register_namespace(ns, config, &s3_client);
match register_res {
Ok(_) => {}
Err(err) => tracing::error!(?err, ?ns, "failed to register namespace route"),
Expand Down Expand Up @@ -149,14 +160,7 @@ pub async fn build_cdn(ctx: &Ctx<Auth>) -> GlobalResult<types::TraefikConfigResp
},
);

tracing::info!(
services = ?config.http.services.len(),
routers = config.http.routers.len(),
middlewares = ?config.http.middlewares.len(),
"cdn traefik config"
);

Ok(config)
Ok(())
}

#[tracing::instrument(skip(redis_cdn))]
Expand Down
8 changes: 8 additions & 0 deletions svc/api/traefik-provider/src/route/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use hyper::{Body, Request, Response};

pub mod core;
pub mod game_guard;
pub mod tunnel;

pub async fn handle(
shared_client: chirp_client::SharedClientHandle,
Expand All @@ -26,6 +27,13 @@ define_router! {
opt_auth: true,
),
},
"config" / "tunnel": {
GET: tunnel::config(
query: tunnel::ConfigQuery,
internal_endpoint: true,
opt_auth: true,
),
},
"config" / "game-guard": {
GET: game_guard::config(
query: game_guard::ConfigQuery,
Expand Down
70 changes: 70 additions & 0 deletions svc/api/traefik-provider/src/route/tunnel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use api_helper::{anchor::WatchIndexQuery, ctx::Ctx};
use proto::backend;
use rivet_operation::prelude::*;
use serde::{Deserialize, Serialize};

use crate::{auth::Auth, types};

#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigQuery {
token: String,
}

#[tracing::instrument(skip(ctx))]
pub async fn config(
ctx: Ctx<Auth>,
_watch_index: WatchIndexQuery,
ConfigQuery { token }: ConfigQuery,
) -> GlobalResult<types::TraefikConfigResponseNullified> {
ctx.auth().token(&token).await?;

let mut config = types::TraefikConfigResponse::default();

build_ip_allowlist(&ctx, &mut config).await?;

tracing::info!(
services = ?config.tcp.services.len(),
routers = config.tcp.routers.len(),
middlewares = ?config.tcp.middlewares.len(),
"tunnel traefik config"
);

Ok(types::TraefikConfigResponseNullified {
http: config.http.nullified(),
tcp: config.tcp.nullified(),
udp: config.udp.nullified(),
})
}

/// Builds configuration for GG edge node routes.
#[tracing::instrument(skip(ctx))]
pub async fn build_ip_allowlist(
ctx: &Ctx<Auth>,
config: &mut types::TraefikConfigResponse,
) -> GlobalResult<()> {
let servers_res = op!([ctx] cluster_server_list {
filter: Some(backend::cluster::ServerFilter {
pool_types: vec![backend::cluster::PoolType::Gg as i32],
..Default::default()
}),
include_destroyed: false,
})
.await?;

let public_ips = servers_res
.servers
.iter()
.filter_map(|server| server.public_ip.clone())
.collect::<Vec<_>>();

config.tcp.middlewares.insert(
"tunnel-ip-allowlist".to_owned(),
types::TraefikMiddlewareHttp::IpAllowList {
source_range: public_ips,
ip_strategy: None,
},
);

Ok(())
}
4 changes: 2 additions & 2 deletions svc/api/traefik-provider/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ pub struct TraefikTlsDomain {
pub enum TraefikMiddlewareHttp {
#[serde(rename = "chain", rename_all = "camelCase")]
Chain { middlewares: Vec<String> },
#[serde(rename = "ipWhiteList", rename_all = "camelCase")]
IpWhiteList {
#[serde(rename = "ipAllowList", rename_all = "camelCase")]
IpAllowList {
source_range: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
ip_strategy: Option<IpStrategy>,
Expand Down
Loading