Skip to content

Commit

Permalink
feat: add hacky secondary ingress route for game lobbies (#567)
Browse files Browse the repository at this point in the history
<!-- Please make sure there is an issue that this PR is correlated to. -->

## Changes

<!-- If there are frontend changes, please include screenshots. -->
  • Loading branch information
MasterPtato committed Mar 19, 2024
1 parent 7191325 commit 8bb6bd6
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 49 deletions.
10 changes: 10 additions & 0 deletions infra/tf/dns/dns.tf
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ locals {
}
if server.pool_id == "gg"
],
[
for server_id, server in local.servers:
{
zone_id = local.cloudflare_zone_id_job
name = "lobby.${var.domain_job}"
server = server
proxied = false
}
if server.pool_id == "gg"
],

# Deprecated
var.dns_deprecated_subdomains ? [{
Expand Down
63 changes: 58 additions & 5 deletions svc/api/route/src/route/traefik/game_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use proto::backend::{self, pkg::*};
use redis::AsyncCommands;
use rivet_operation::prelude::*;
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{auth::Auth, route::traefik};

Expand Down Expand Up @@ -218,27 +219,33 @@ fn register_proxied_port(
// Insert the relevant router
match proxy_protocol {
ProxyProtocol::Http => {
let middlewares =
http_router_middlewares(run_id, proxied_port, &target_nomad_port_label, config);

config.http.routers.insert(
format!("job-run:{}:{}:http", run_id, target_nomad_port_label),
traefik::TraefikRouter {
entry_points: vec![format!("lb-{ingress_port}")],
rule: Some(format_http_rule(proxied_port)),
priority: None,
service: service_id,
middlewares: vec!["job-rate-limit".into(), "job-in-flight".into()],
service: service_id.clone(),
middlewares,
tls: None,
},
);
}
ProxyProtocol::Https => {
let middlewares =
http_router_middlewares(run_id, proxied_port, &target_nomad_port_label, config);

config.http.routers.insert(
format!("job-run:{}:{}:https", run_id, target_nomad_port_label),
traefik::TraefikRouter {
entry_points: vec![format!("lb-{ingress_port}")],
rule: Some(format_http_rule(proxied_port)),
priority: None,
service: service_id,
middlewares: vec!["job-rate-limit".into(), "job-in-flight".into()],
service: service_id.clone(),
middlewares,
tls: Some(traefik::TraefikTls::build(build_tls_domains(proxied_port)?)),
},
);
Expand Down Expand Up @@ -291,7 +298,15 @@ fn format_http_rule(proxied_port: &job::redis_job::run_proxied_ports::ProxiedPor
proxied_port
.ingress_hostnames
.iter()
.map(|x| format!("Host(`{}`)", x))
.map(|x| {
if let Ok(url) = Url::parse(&format!("https://{x}")) {
if let (true, Some(host)) = (url.path() != "/", url.host()) {
return format!("(Host(`{host}`) && PathPrefix(`{}`))", url.path());
}
}

format!("Host(`{x}`)")
})
.collect::<Vec<String>>()
.join(" || ")
}
Expand Down Expand Up @@ -333,3 +348,41 @@ fn build_tls_domains(

Ok(domains)
}

fn http_router_middlewares(
run_id: Uuid,
proxied_port: &job::redis_job::run_proxied_ports::ProxiedPort,
target_nomad_port_label: &str,
config: &mut traefik::TraefikConfigResponse,
) -> Vec<String> {
let ingress_port = proxied_port.ingress_port;

let mut middlewares = vec!["job-rate-limit".to_string(), "job-in-flight".to_string()];

// Check if any of the hostname values have paths
let paths = proxied_port
.ingress_hostnames
.iter()
.flat_map(|url| Url::parse(&format!("https://{url}")))
.filter(|url| url.path() != "/");

// Create strip prefix middleware
if paths.clone().count() != 0 {
let strip_prefix_id = format!(
"job-run-strip-prefix:{}:{}",
run_id, target_nomad_port_label,
);

config.http.middlewares.insert(
strip_prefix_id.clone(),
traefik::TraefikMiddlewareHttp::StripPrefix {
prefixes: paths.map(|url| url.path().to_string()).collect(),
force_slash: None,
},
);

middlewares.push(strip_prefix_id);
}

middlewares
}
2 changes: 1 addition & 1 deletion svc/api/route/src/route/traefik/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ pub enum TraefikMiddlewareHttp {
#[serde(rename = "stripPrefix", rename_all = "camelCase")]
StripPrefix {
prefixes: Vec<String>,
force_slash: bool,
force_slash: Option<bool>,
},
#[serde(rename = "addPrefix", rename_all = "camelCase")]
AddPrefix { prefix: String },
Expand Down
15 changes: 8 additions & 7 deletions svc/pkg/job-run/worker/src/workers/create/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,14 @@ async fn write_to_db_before_run(
"ingress host not provided"
);

for host in &proxied_port.ingress_hostnames {
ensure!(
host.chars()
.all(|x| x.is_alphanumeric() || x == '.' || x == '-'),
"invalid ingress host"
);
}
// TODO:
// for host in &proxied_port.ingress_hostnames {
// ensure!(
// host.chars()
// .all(|x| x.is_alphanumeric() || x == '.' || x == '-'),
// "invalid ingress host"
// );
// }

let ingress_port = choose_ingress_port(ctx.clone(), tx, proxied_port).await?;

Expand Down
105 changes: 69 additions & 36 deletions svc/pkg/mm/worker/src/workers/lobby_create/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,42 +638,9 @@ async fn create_docker_job(
port.proxy_kind == backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32
&& port.port_range.is_none()
})
.map(|port| {
let job_proxy_protocol = match unwrap!(
backend::matchmaker::lobby_runtime::ProxyProtocol::from_i32(port.proxy_protocol)
) {
backend::matchmaker::lobby_runtime::ProxyProtocol::Http => {
backend::job::ProxyProtocol::Http as i32
}
backend::matchmaker::lobby_runtime::ProxyProtocol::Https => {
backend::job::ProxyProtocol::Https as i32
}
backend::matchmaker::lobby_runtime::ProxyProtocol::Tcp => {
backend::job::ProxyProtocol::Tcp as i32
}
backend::matchmaker::lobby_runtime::ProxyProtocol::TcpTls => {
backend::job::ProxyProtocol::TcpTls as i32
}
backend::matchmaker::lobby_runtime::ProxyProtocol::Udp => {
backend::job::ProxyProtocol::Udp as i32
}
};

GlobalResult::Ok(backend::job::ProxiedPortConfig {
// Match the port label generated in mm-config-version-prepare
// and in api-matchmaker
target_nomad_port_label: Some(util_mm::format_nomad_port_label(&port.label)),
ingress_port: None,
ingress_hostnames: vec![format!(
"{}-{}.lobby.{}.{}",
lobby_id,
port.label,
region.name_id,
unwrap!(util::env::domain_job()),
)],
proxy_protocol: job_proxy_protocol,
ssl_domain_mode: backend::job::SslDomainMode::ParentWildcard as i32,
})
.flat_map(|port| {
std::iter::once(direct_proxied_port(lobby_id, region, port))
.chain(std::iter::once(path_proxied_port(lobby_id, region, port)))
})
.collect::<GlobalResult<Vec<_>>>()?;

Expand Down Expand Up @@ -889,3 +856,69 @@ async fn resolve_image_artifact_url(
}
}
}

fn direct_proxied_port(
lobby_id: Uuid,
region: &backend::region::Region,
port: &backend::matchmaker::lobby_runtime::Port,
) -> GlobalResult<backend::job::ProxiedPortConfig> {
Ok(backend::job::ProxiedPortConfig {
// Match the port label generated in mm-config-version-prepare
// and in api-matchmaker
target_nomad_port_label: Some(util_mm::format_nomad_port_label(&port.label)),
ingress_port: None,
ingress_hostnames: vec![format!(
"{}-{}.lobby.{}.{}",
lobby_id,
port.label,
region.name_id,
unwrap!(util::env::domain_job()),
)],
proxy_protocol: job_proxy_protocol(port.proxy_protocol)? as i32,
ssl_domain_mode: backend::job::SslDomainMode::ParentWildcard as i32,
})
}

fn path_proxied_port(
lobby_id: Uuid,
region: &backend::region::Region,
port: &backend::matchmaker::lobby_runtime::Port,
) -> GlobalResult<backend::job::ProxiedPortConfig> {
Ok(backend::job::ProxiedPortConfig {
// Match the port label generated in mm-config-version-prepare
// and in api-matchmaker
target_nomad_port_label: Some(util_mm::format_nomad_port_label(&port.label)),
ingress_port: None,
// TODO: Not just for hostnames anymore, change name?
ingress_hostnames: vec![format!(
"lobby.{}/{}/{}-{}",
unwrap!(util::env::domain_job()),
region.name_id,
lobby_id,
port.label,
)],
proxy_protocol: job_proxy_protocol(port.proxy_protocol)? as i32,
ssl_domain_mode: backend::job::SslDomainMode::Exact as i32,
})
}

fn job_proxy_protocol(proxy_protocol: i32) -> GlobalResult<backend::job::ProxyProtocol> {
let proxy_protocol = unwrap!(backend::matchmaker::lobby_runtime::ProxyProtocol::from_i32(
proxy_protocol
));
let job_proxy_protocol = match proxy_protocol {
backend::matchmaker::lobby_runtime::ProxyProtocol::Http => {
backend::job::ProxyProtocol::Http
}
backend::matchmaker::lobby_runtime::ProxyProtocol::Https => {
backend::job::ProxyProtocol::Https
}
backend::matchmaker::lobby_runtime::ProxyProtocol::Tcp => backend::job::ProxyProtocol::Tcp,
backend::matchmaker::lobby_runtime::ProxyProtocol::TcpTls => {
backend::job::ProxyProtocol::TcpTls
}
backend::matchmaker::lobby_runtime::ProxyProtocol::Udp => backend::job::ProxyProtocol::Udp,
};

Ok(job_proxy_protocol)
}

0 comments on commit 8bb6bd6

Please sign in to comment.