Skip to content

Add agent API key scope to restrict access to user data #391

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

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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ website/vendor

# Binary
terraform-provider-coder

# direnv
.direnv
8 changes: 5 additions & 3 deletions docs/resources/agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ data "coder_workspace" "me" {
}

resource "coder_agent" "dev" {
os = "linux"
arch = "amd64"
dir = "/workspace"
os = "linux"
arch = "amd64"
dir = "/workspace"
api_key_scope = "all"
display_apps {
vscode = true
vscode_insiders = false
Expand Down Expand Up @@ -71,6 +72,7 @@ resource "kubernetes_pod" "dev" {

### Optional

- `api_key_scope` (String) Controls what API routes the agent token can access. Options: 'all' (full access) or 'no_user_data' (blocks /external-auth, /gitsshkey, and /gitauth routes)
- `auth` (String) The authentication type the agent will use. Must be one of: `"token"`, `"google-instance-identity"`, `"aws-instance-identity"`, `"azure-instance-identity"`.
- `connection_timeout` (Number) Time in seconds until the agent is marked as timed out when a connection with the server cannot be established. A value of zero never marks the agent as timed out.
- `dir` (String) The starting directory when a user creates a shell session. Defaults to `"$HOME"`.
Expand Down
7 changes: 4 additions & 3 deletions examples/resources/coder_agent/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ data "coder_workspace" "me" {
}

resource "coder_agent" "dev" {
os = "linux"
arch = "amd64"
dir = "/workspace"
os = "linux"
arch = "amd64"
dir = "/workspace"
api_key_scope = "all"
display_apps {
vscode = true
vscode_insiders = false
Expand Down
8 changes: 4 additions & 4 deletions flake.lock

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

6 changes: 3 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
description = "Terraform provider for Coder";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, flake-utils, ... }:
outputs = { nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
Expand All @@ -21,7 +21,7 @@
name = "devShell";
buildInputs = with pkgs; [
terraform
go_1_20
go_1_24
];
};
}
Expand Down
11 changes: 11 additions & 0 deletions provider/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ func agentResource() *schema.Resource {
return nil
},
Schema: map[string]*schema.Schema{
"api_key_scope": {
Type: schema.TypeString,
Optional: true,
Default: "all",
ForceNew: true,
Description: "Controls what API routes the agent token can access. Options: 'all' (full access) or 'no_user_data' (blocks /external-auth, /gitsshkey, and /gitauth routes)",
ValidateFunc: validation.StringInSlice([]string{
"all",
"no_user_data",
}, false),
},
"init_script": {
Type: schema.TypeString,
Computed: true,
Expand Down
91 changes: 91 additions & 0 deletions provider/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,5 +709,96 @@ func TestAgent_DisplayApps(t *testing.T) {
}},
})
})
}

// TestAgent_APIKeyScope tests valid states/transitions and invalid values for api_key_scope.
func TestAgent_APIKeyScope(t *testing.T) {
t.Parallel()

t.Run("ValidTransitions", func(t *testing.T) {
t.Parallel()

resourceName := "coder_agent.test_scope_valid"

resource.Test(t, resource.TestCase{
ProviderFactories: coderFactory(),
IsUnitTest: true,
Steps: []resource.TestStep{
// Step 1: Default value
{
Config: `
provider "coder" {
url = "https://example.com"
}
resource "coder_agent" "test_scope_valid" {
os = "linux"
arch = "amd64"
# api_key_scope is omitted, should default to "default"
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "api_key_scope", "all"),
),
},
// Step 2: Explicit "default"
{
Config: `
provider "coder" {
url = "https://example.com"
}
resource "coder_agent" "test_scope_valid" {
os = "linux"
arch = "amd64"
api_key_scope = "all"
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "api_key_scope", "all"),
),
},
// Step 3: Explicit "no_user_data"
{
Config: `
provider "coder" {
url = "https://example.com"
}
resource "coder_agent" "test_scope_valid" {
os = "linux"
arch = "amd64"
api_key_scope = "no_user_data"
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "api_key_scope", "no_user_data"),
),
},
},
})
})

t.Run("InvalidValue", func(t *testing.T) {
t.Parallel()

resource.Test(t, resource.TestCase{
ProviderFactories: coderFactory(),
IsUnitTest: true,
Steps: []resource.TestStep{
// Step 1: Invalid value check
{
Config: `
provider "coder" {
url = "https://example.com"
}
resource "coder_agent" "test_scope_invalid" { // Use unique name
os = "linux"
arch = "amd64"
api_key_scope = "invalid-scope"
}
`,
ExpectError: regexp.MustCompile(`expected api_key_scope to be one of \["all" "no_user_data"\], got invalid-scope`),
PlanOnly: true,
},
},
})
})
}
Loading