Skip to content
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
14 changes: 7 additions & 7 deletions registry/coder/modules/mux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Automatically install and run [Mux](https://github.com/coder/mux) in a Coder wor
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.0.8"
version = "1.1.0"
agent_id = coder_agent.main.id
}
```
Expand All @@ -37,7 +37,7 @@ module "mux" {
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.0.8"
version = "1.1.0"
agent_id = coder_agent.main.id
}
```
Expand All @@ -48,7 +48,7 @@ module "mux" {
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.0.8"
version = "1.1.0"
agent_id = coder_agent.main.id
# Default is "latest"; set to a specific version to pin
install_version = "0.4.0"
Expand All @@ -63,7 +63,7 @@ Start Mux with `mux server --add-project /path/to/project`:
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.0.8"
version = "1.1.0"
agent_id = coder_agent.main.id
add-project = "/path/to/project"
}
Expand All @@ -75,7 +75,7 @@ module "mux" {
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.0.8"
version = "1.1.0"
agent_id = coder_agent.main.id
port = 8080
}
Expand All @@ -89,7 +89,7 @@ Run an existing copy of Mux if found, otherwise install from npm:
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.0.8"
version = "1.1.0"
agent_id = coder_agent.main.id
use_cached = true
}
Expand All @@ -103,7 +103,7 @@ Run without installing from the network (requires Mux to be pre-installed):
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.0.8"
version = "1.1.0"
agent_id = coder_agent.main.id
install = false
}
Expand Down
25 changes: 22 additions & 3 deletions registry/coder/modules/mux/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ terraform {
source = "coder/coder"
version = ">= 2.5"
}
random = {
source = "hashicorp/random"
version = ">= 3.0"
}
}
}

Expand Down Expand Up @@ -113,6 +117,22 @@ variable "open_in" {
}
}

# Per-module auth token for cross-site request protection.
# We pass this token into each mux process at launch time (process-scoped env)
# and include it in the app URL query string (?token=...).
#
# Why process-scoped env instead of a shared coder_env value:
# multiple mux module instances can target the same agent (different slug/port).
# A single global MUX_SERVER_AUTH_TOKEN env key would cause collisions.
resource "random_password" "mux_auth_token" {
length = 64
special = false
}

locals {
mux_auth_token = random_password.mux_auth_token.result
}

resource "coder_script" "mux" {
agent_id = var.agent_id
display_name = var.display_name
Expand All @@ -125,6 +145,7 @@ resource "coder_script" "mux" {
INSTALL_PREFIX : var.install_prefix,
OFFLINE : !var.install,
USE_CACHED : var.use_cached,
AUTH_TOKEN : local.mux_auth_token,
})
run_on_start = true

Expand All @@ -140,7 +161,7 @@ resource "coder_app" "mux" {
agent_id = var.agent_id
slug = var.slug
display_name = var.display_name
url = "http://localhost:${var.port}"
url = "http://localhost:${var.port}?token=${local.mux_auth_token}"
icon = "/icon/mux.svg"
subdomain = var.subdomain
share = var.share
Expand All @@ -154,5 +175,3 @@ resource "coder_app" "mux" {
threshold = 6
}
}


53 changes: 48 additions & 5 deletions registry/coder/modules/mux/mux.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,62 @@ run "install_false_and_use_cached_conflict" {
]
}

# Needs command = apply because the URL contains random_password.result,
# which is unknown during plan.
run "custom_port" {
command = plan
command = apply

variables {
agent_id = "foo"
port = 8080
}

assert {
condition = resource.coder_app.mux.url == "http://localhost:8080"
error_message = "coder_app URL must use the configured port"
condition = startswith(resource.coder_app.mux.url, "http://localhost:8080?token=")
error_message = "coder_app URL must use the configured port and include auth token"
}

assert {
condition = trimprefix(resource.coder_app.mux.url, "http://localhost:8080?token=") == random_password.mux_auth_token.result
error_message = "URL token must match the generated auth token"
}
}

# Needs command = apply because random_password.result is unknown during plan.
run "auth_token_in_server_script" {
command = apply

variables {
agent_id = "foo"
}

assert {
condition = strcontains(resource.coder_script.mux.script, "MUX_SERVER_AUTH_TOKEN=")
error_message = "mux launch script must set MUX_SERVER_AUTH_TOKEN"
}

assert {
condition = strcontains(resource.coder_script.mux.script, random_password.mux_auth_token.result)
error_message = "mux launch script must use the generated auth token"
}
}

# Needs command = apply because random_password.result is unknown during plan.
run "auth_token_in_url" {
command = apply

variables {
agent_id = "foo"
}

assert {
condition = startswith(resource.coder_app.mux.url, "http://localhost:4000?token=")
error_message = "coder_app URL must include auth token query parameter"
}

assert {
condition = trimprefix(resource.coder_app.mux.url, "http://localhost:4000?token=") == random_password.mux_auth_token.result
error_message = "URL token must match the generated auth token"
}
}

Expand Down Expand Up @@ -62,5 +107,3 @@ run "use_cached_only_success" {
use_cached = true
}
}


4 changes: 3 additions & 1 deletion registry/coder/modules/mux/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ function run_mux() {
rm -f "$HOME/.mux/server.lock"

local port_value
local auth_token_value
port_value="${PORT}"
auth_token_value="${AUTH_TOKEN}"
if [ -z "$port_value" ]; then
port_value="4000"
fi
Expand All @@ -20,7 +22,7 @@ function run_mux() {
fi
echo "🚀 Starting mux server on port $port_value..."
echo "Check logs at ${LOG_PATH}!"
PORT="$port_value" "$MUX_BINARY" "$@" > "${LOG_PATH}" 2>&1 &
MUX_SERVER_AUTH_TOKEN="$auth_token_value" PORT="$port_value" "$MUX_BINARY" "$@" > "${LOG_PATH}" 2>&1 &
}

# Check if mux is already installed for offline mode
Expand Down