Skip to content

Commit

Permalink
Add ServiceAccount as extraspecs
Browse files Browse the repository at this point in the history
  • Loading branch information
fabi200123 committed Sep 23, 2024
1 parent 9498684 commit e9fe6e8
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 19 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ To this end, this provider supports the following extra specs schema:
"type": "string"
}
},
"service_accounts": {
"type": "array",
"description": "A list of service accounts to be attached to the instance",
"items": {
"$ref": "#/$defs/ServiceAccount"
}
},
"source_snapshot": {
"type": "string",
"description": "The source snapshot to create this disk."
Expand Down Expand Up @@ -185,11 +192,14 @@ An example of extra specs json would look like this:
"nic_type": "VIRTIO_NET",
"custom_labels": {"environment":"production","project":"myproject"},
"network_tags": ["web-server", "production"],
"service_accounts": [{"email":"[email protected]", "scopes":["https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/logging.write"]}],
"source_snapshot": "projects/garm-testing/global/snapshots/garm-snapshot",
"ssh_keys": ["username1:ssh_key1", "username2:ssh_key2"]
}
```

**NOTE**: Using the `service_accounts` extra specs when creating instances **introduces certain risks that must be carefully managed**. **Service accounts** grant access to specific resources, and if improperly configured, they can expose sensitive data or allow unauthorized actions. Misconfigured permissions or overly broad scopes can lead to privilege escalation, enabling attackers or unintended users to access critical resources. It's essential to follow the principle of least privilege, ensuring that service accounts only have the necessary permissions for their intended tasks. Regular audits and proper key management are also crucial to safeguard access and prevent potential security vulnerabilities.

**NOTE**: The `custom_labels` and `network_tags` must meet the [GCP requirements for labels](https://cloud.google.com/compute/docs/labeling-resources#requirements) and the [GCP requirements for network tags](https://cloud.google.com/vpc/docs/add-remove-network-tags#restrictions)!

**NOTE**: The `ssh_keys` add the option to [connect to an instance via SSH](https://cloud.google.com/compute/docs/instances/ssh) (either Linux or Windows). After you added the key as `username:ssh_public_key`, you can use the `private_key` to connect to the Linux/Windows instance via `ssh -i private_rsa username@instance_ip`. For **Windows** instances, the provider installs on the instance `google-compute-engine-ssh` and `enables ssh` if a `ssh_key` is added to extra-specs.
Expand Down
1 change: 1 addition & 0 deletions internal/client/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ func (g *GcpCli) CreateInstance(ctx context.Context, spec *spec.RunnerSpec) (*co
Tags: &computepb.Tags{
Items: spec.NetworkTags,
},
ServiceAccounts: spec.ServiceAccounts,
}

if !g.cfg.ExternalIPAccess {
Expand Down
28 changes: 17 additions & 11 deletions internal/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"maps"
"regexp"

"cloud.google.com/go/compute/apiv1/computepb"
"github.com/cloudbase/garm-provider-common/cloudconfig"
"github.com/cloudbase/garm-provider-common/params"
"github.com/cloudbase/garm-provider-common/util"
Expand Down Expand Up @@ -127,17 +128,18 @@ func (e *extraSpecs) Validate() error {
}

type extraSpecs struct {
DiskSize int64 `json:"disksize,omitempty" jsonschema:"description=The size of the root disk in GB. Default is 127 GB."`
DiskType string `json:"disktype,omitempty" jsonschema:"description=The type of the disk. Default is pd-standard."`
DisplayDevice bool `json:"display_device,omitempty" jsonschema:"description=Enable the display device on the VM."`
NetworkID string `json:"network_id,omitempty" jsonschema:"description=The name of the network attached to the instance."`
SubnetworkID string `json:"subnetwork_id,omitempty" jsonschema:"description=The name of the subnetwork attached to the instance."`
NicType string `json:"nic_type,omitempty" jsonschema:"description=The type of the network interface card. Default is VIRTIO_NET."`
CustomLabels map[string]string `json:"custom_labels,omitempty" jsonschema:"description=Custom labels to apply to the instance. Each label is a key-value pair where both key and value are strings."`
NetworkTags []string `json:"network_tags,omitempty" jsonschema:"description=A list of network tags to be attached to the instance"`
SourceSnapshot string `json:"source_snapshot,omitempty" jsonschema:"description=The source snapshot to create this disk."`
SSHKeys []string `json:"ssh_keys,omitempty" jsonschema:"description=A list of SSH keys to be added to the instance. The format is USERNAME:SSH_KEY"`
EnableBootDebug *bool `json:"enable_boot_debug,omitempty" jsonschema:"description=Enable boot debug on the VM."`
DiskSize int64 `json:"disksize,omitempty" jsonschema:"description=The size of the root disk in GB. Default is 127 GB."`
DiskType string `json:"disktype,omitempty" jsonschema:"description=The type of the disk. Default is pd-standard."`
DisplayDevice bool `json:"display_device,omitempty" jsonschema:"description=Enable the display device on the VM."`
NetworkID string `json:"network_id,omitempty" jsonschema:"description=The name of the network attached to the instance."`
SubnetworkID string `json:"subnetwork_id,omitempty" jsonschema:"description=The name of the subnetwork attached to the instance."`
NicType string `json:"nic_type,omitempty" jsonschema:"description=The type of the network interface card. Default is VIRTIO_NET."`
CustomLabels map[string]string `json:"custom_labels,omitempty" jsonschema:"description=Custom labels to apply to the instance. Each label is a key-value pair where both key and value are strings."`
NetworkTags []string `json:"network_tags,omitempty" jsonschema:"description=A list of network tags to be attached to the instance"`
ServiceAccounts []*computepb.ServiceAccount `json:"service_accounts,omitempty" jsonschema:"description=A list of service accounts to be attached to the instance"`
SourceSnapshot string `json:"source_snapshot,omitempty" jsonschema:"description=The source snapshot to create this disk."`
SSHKeys []string `json:"ssh_keys,omitempty" jsonschema:"description=A list of SSH keys to be added to the instance. The format is USERNAME:SSH_KEY"`
EnableBootDebug *bool `json:"enable_boot_debug,omitempty" jsonschema:"description=Enable boot debug on the VM."`
// The Cloudconfig struct from common package
cloudconfig.CloudConfigSpec
}
Expand Down Expand Up @@ -189,6 +191,7 @@ type RunnerSpec struct {
DiskType string
CustomLabels map[string]string
NetworkTags []string
ServiceAccounts []*computepb.ServiceAccount
SourceSnapshot string
SSHKeys string
EnableBootDebug bool
Expand Down Expand Up @@ -219,6 +222,9 @@ func (r *RunnerSpec) MergeExtraSpecs(extraSpecs *extraSpecs) {
if len(extraSpecs.NetworkTags) > 0 {
r.NetworkTags = extraSpecs.NetworkTags
}
if len(extraSpecs.ServiceAccounts) > 0 {
r.ServiceAccounts = extraSpecs.ServiceAccounts
}
if extraSpecs.SourceSnapshot != "" {
r.SourceSnapshot = extraSpecs.SourceSnapshot
}
Expand Down
40 changes: 32 additions & 8 deletions internal/spec/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import (
"fmt"
"testing"

"cloud.google.com/go/compute/apiv1/computepb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)

func TestJsonSchemaValidation(t *testing.T) {
Expand All @@ -43,6 +45,8 @@ func TestJsonSchemaValidation(t *testing.T) {
"example_label": "example_value"
},
"network_tags": ["example_tag"],
"service_accounts": [{"email": "email", "scopes": ["scope"]}],
"service_accounts": [{"email": "email", "scopes": ["scope", "scope2"]}, {"email": "email2", "scopes": ["scope2"]}],
"source_snapshot": "snapshot-id",
"ssh_keys": ["ssh-key", "ssh-key2"],
"enable_boot_debug": true,
Expand Down Expand Up @@ -108,6 +112,13 @@ func TestJsonSchemaValidation(t *testing.T) {
}`),
errString: "",
},
{
name: "Specs just with service_accounts",
input: json.RawMessage(`{
"service_accounts": [{"email": "email", "scopes": ["scope"]}]
}`),
errString: "",
},
{
name: "Specs just with source_snapshot",
input: json.RawMessage(`{
Expand Down Expand Up @@ -196,6 +207,13 @@ func TestJsonSchemaValidation(t *testing.T) {
}`),
errString: "schema validation failed: [network_tags: Invalid type. Expected: array, given: string]",
},
{
name: "Invalid input for service_accounts - wrong data type",
input: json.RawMessage(`{
"service_accounts": "email"
}`),
errString: "schema validation failed: [service_accounts: Invalid type. Expected: array, given: string]",
},
{
name: "Invalid input for ssh_keys - wrong data type",
input: json.RawMessage(`{
Expand Down Expand Up @@ -273,14 +291,20 @@ func TestMergeExtraSpecs(t *testing.T) {
{
name: "ValidExtraSpecs",
extraSpecs: &extraSpecs{
NetworkID: "projects/garm-testing/global/networks/garm-2",
SubnetworkID: "projects/garm-testing/regions/europe-west1/subnetworks/garm",
DisplayDevice: true,
DiskSize: 100,
DiskType: "projects/garm-testing/zones/europe-west1/diskTypes/pd-ssd",
NicType: "VIRTIO_NET",
CustomLabels: map[string]string{"key1": "value1"},
NetworkTags: []string{"tag1", "tag2"},
NetworkID: "projects/garm-testing/global/networks/garm-2",
SubnetworkID: "projects/garm-testing/regions/europe-west1/subnetworks/garm",
DisplayDevice: true,
DiskSize: 100,
DiskType: "projects/garm-testing/zones/europe-west1/diskTypes/pd-ssd",
NicType: "VIRTIO_NET",
CustomLabels: map[string]string{"key1": "value1"},
NetworkTags: []string{"tag1", "tag2"},
ServiceAccounts: []*computepb.ServiceAccount{
{
Email: proto.String("email"),
Scopes: []string{"scope"},
},
},
SourceSnapshot: "projects/garm-testing/global/snapshots/garm-snapshot",
SSHKeys: []string{"ssh-key1", "ssh-key2"},
EnableBootDebug: &enable_boot_debug,
Expand Down

0 comments on commit e9fe6e8

Please sign in to comment.