feat: Docs (#15)

* feat: Docs

- Add docs for GCP Dynamic Host Plugin
- Remove references of instances group since we do not currently support this
This commit is contained in:
Elim Tsiagbey authored and GitHub committed 2024-10-29 12:52:56 -04:00
1 parent 779d383a24
commit a75b9bd310
7 files changed
+143 -98

No files matched your search

+35 -71
View File
@@ -3,88 +3,52 @@
This repo contains the GCP plugin for [HashiCorp This repo contains the GCP plugin for [HashiCorp
Boundary](https://www.boundaryproject.io/). Boundary](https://www.boundaryproject.io/).
> [!IMPORTANT]
> This is a prototype. Not officially supported by HashiCorp.
## Credentials ## Credentials
This plugin uses [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/provide-credentials-adc) ### Service Account Credentials
to authenticate to Google and retrieve a list of instances and instance groups.
As a result, it does not support rotation of the credentials since they are inferred The plugin will authenticate using service account credential when the `secrets`
from the environment. object is set. By default, the plugin will attempt to rotate the credentials.
The given credentials will be used to create a new credential, and then the given
credential will be revoked. In this way, after rotation, only Boundary knows the
client secret in use by this plugin.
Credential rotation can be turned off by setting the `disable_credential_rotation`
attribute to true.
### Service Account Impersonation
The plugin will attempt to impersonate a service account when the `target_service_account_id`
field is supplied through the `attributes` object. The base service account will be
used to assume the identity and permissions of the target service account. A temporary
credential will be generated for authentication. The base service account requires the
[service account token creator](https://cloud.google.com/iam/docs/service-account-permissions#token-creator-role)
role to assume the role of the target service account.
By default, the credentials of the base service account will be rotated if
credential rotation is not disabled by setting the `disable_credential_rotation`
attribute.
### Application Default Credentials
The plugin uses [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/provide-credentials-adc)
for authentication when no `secrets` object is set. The plugin will attempt to
retrieve the credentials based on the environment.
## Dynamic Hosts ## Dynamic Hosts
This plugin supports dynamically sourcing instances from Google Compute Engine. This plugin supports dynamically sourcing instances from GCP Google Compute Engine.
Host sets created with this plugin define filters or instance groups Host sets created with this plugin define filters
which select and group like instances within Google; these host sets can in turn be which select and group like instances within GCP; these host sets can in turn be
added to targets within Boundary as host sources. added to targets within Boundary as host sources.
At creation, update or deletion of a host catalog of this type, configuration of the At creation, update or deletion of a host catalog of this type, configuration of the
plugin is performed via the attribute/secret values passed to the create, update, or plugin is performed via the attribute/secret values passed to the create, update, or
delete calls actions. The values passed in to the plugin here are the attributes set delete calls actions. The values passed in to the plugin here are the attributes set
on on a host catalog in Boundary. on a host catalog in Boundary.
### Google IAM permissions The plugin fetches hosts through the [Instances.List](https://cloud.google.com/compute/docs/reference/rest/v1/instances/list#filter)
call.
The plugin requires the following permissions: [Getting Started](plugin/service/host/README.md)
- `compute.instances.get`
- `compute.instances.list`
- `compute.instanceGroups.get`
- `compute.instanceGroups.list`
### Attributes
### Host Catalog
The following attributes are valid on a Google host catalog resource:
- `project` (string): required. Project ID of the instances you want to add to host catalog.
- `zone` (string): required. Zone of the instances you want to add to host catalog.
Example:
```shell
$ boundary host-catalogs create plugin -scope-id p_1234567890 -plugin-name google -attr zone=us-central1-a -attr project=$GOOGLE_PROJECT
```
### Host Set
The following attributes are valid on a Google host set resource:
- `filter` (string): Google Cloud [filter expression](https://cloud.google.com/sdk/gcloud/reference/topic/filters)
to filter instances.
- `instance_group` (string): Name of instance group to get a list of instances.
You can only set a `filter` or `instance_group` attribute, you cannot set both.
Example:
```shell
$ boundary host-sets create plugin -host-catalog-id $HOST_CATALOG_ID -name "filter-example" -description "example using filters" -attr filter="status=RUNNING"
$ boundary host-sets create plugin -host-catalog-id $HOST_CATALOG_ID -name "group-example" -description "example using instance groups" -attr instance_group="instance-group-name"
```
After generating the host set, create a target.
```shell
$ boundary targets create tcp -name google -description "Google Cloud" -scope-id p_1234567890 -default-port 22
$ boundary targets add-host-sources -id $TARGET_ID -host-source $HOST_SET_ID
```
## Setup
You will need to build your own Boundary binary, as Boundary imports the plugins as a dependency.
As a reference, you can review [this fork](https://github.com/joatmon08/boundary) for changes.
To build the fork, go into the repository.
1. Run `make build`.
1. Authenticate to Google Cloud using `gcloud auth application-default login`.
1. Run Boundary with `./bin/boundary dev`.
-4
View File
@@ -8,7 +8,6 @@ require (
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/googleapis/gax-go/v2 v2.13.0 github.com/googleapis/gax-go/v2 v2.13.0
github.com/hashicorp/boundary/sdk v0.0.47 github.com/hashicorp/boundary/sdk v0.0.47
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8
golang.org/x/oauth2 v0.22.0 golang.org/x/oauth2 v0.22.0
google.golang.org/api v0.196.0 google.golang.org/api v0.196.0
) )
@@ -25,9 +24,6 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/s2a-go v0.1.8 // indirect github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.5 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
+106
View File
@@ -0,0 +1,106 @@
## Getting Started
Refer to [Attributes and Secrets](#attributes-and-secrets) for more detail on
configuration options for host catalogs and sets.
To create a host catalog (using default scope created by `boundary dev`):
```sh
boundary host-catalogs create plugin \
-scope-id p_1234567890 \
-name "Example Plugin-Based Host Catalog" \
-description "Description for plugin-based host catalog" \
-plugin-name gcp \
-attr client_email=CLIENT_EMAIL \
-attr project_id=PROJECT_ID \
-attr zone=ZONE \
-secret private_key_id='PRIVATE_KEY_ID' \
-secret private_key='PRIVATE_KEY'
```
To create a host set, filtering the host set based on status value
of `RUNNING` and a label.
```sh
boundary host-sets create plugin \
-host-catalog-id HOST_CATALOG_ID \
-name "Example Plugin-Based Host Set" \
-description "Description for plugin-based host set" \
-attr filters=status=RUNNING
-attr filters=labels.env:prod
```
## Required IAM Permissions
The following IAM permissions, at the very least, are required to be attached to
a configured service account for this provider:
### List Instances
To list instances, the credential requires these permissions:
```
[
"compute.instances.list"
]
```
### Rotate Service Account Keys
When GCP service account keys are provided with credential rotation enabled, GCP
service account keys will be rotated when requests are made to the plugin. These
permissions are required to rotate the service account key:
```
[
"iam.serviceAccountKeys.create",
"iam.serviceAccountKeys.delete",
]
```
### Service Account Impersonation
For the base service account to authenticate by service account impersonation.
The base service account needs to have these roles:
```
[
"iam.serviceAccountTokenCreator"
]
```
## Attributes and Secrets
### Host Catalog
The following `attributes` are valid on a GCP host catalog resource:
- `zone` (string, required): The zone to configure the host catalog for. All host sets
in this catalog will be configured for this zone.
- `project_id` (string, required): The project ID associated with the service account. All host sets
in this catalog will be configured for this project.
- `disable_credential_rotation` (bool): If `true`, credential rotation will not
be performed. See the [Credentials](../../../README.md#credentials) readme for more information.
- `client_email` (string): The email address associated with the service account. The email address
used to uniquely identify the service account. It is required for authentication and authorization.
- `target_service_account_id` (string): The unique identifier for the service account that will be
impersonate. This is only required when authenticating with service account impersonation.
The following `secrets` are required on an GCP host catalog resource:
- `private_key_id` (string): The private key ID for the service account to use with this
host catalog.
- `private_key` (string): The private key for the service account to use
with this host catalog.
See the [Credentials](../../../README.md#credentials) readme for more information.
### Host Set
The following attributes are valid on a GCP host Set resource:
- `filters` (array of strings): Host Set filters are used to narrow down the list of hosts
returned by the plugin. The filter string is expected to be in the format "key operator value".
The operator is expected to be one of =, !=, >, <, <=, >=, :, eq, ne.
as per GCP API documentation:
[instances.list reference](https://cloud.google.com/compute/docs/reference/rest/v1/instances/list#filter).
-6
View File
@@ -70,12 +70,6 @@ func getSetAttributes(in *structpb.Struct) (*SetAttributes, error) {
badFields := make(map[string]string) badFields := make(map[string]string)
unknownFields := values.StructFields(in) unknownFields := values.StructFields(in)
if _, filter := unknownFields[ConstListInstancesFilter]; filter {
if _, ig := unknownFields[ConstInstanceGroup]; ig {
return nil, errors.InvalidArgumentError("Error in the attributes provided, cannot define both filter and instance group", nil)
}
}
delete(unknownFields, ConstListInstancesFilter) delete(unknownFields, ConstListInstancesFilter)
for a := range unknownFields { for a := range unknownFields {
-8
View File
@@ -103,14 +103,6 @@ func TestGetSetAttributes(t *testing.T) {
}, },
expectedErrContains: "attributes.bar: unrecognized field, attributes.foo: unrecognized field", expectedErrContains: "attributes.bar: unrecognized field, attributes.foo: unrecognized field",
}, },
{
name: "invalid, cannot have both instance filter and group defined",
in: map[string]any{
ConstListInstancesFilter: "test",
ConstInstanceGroup: "test",
},
expectedErrContains: "Error in the attributes provided, cannot define both filter and instance group",
},
} }
for _, tc := range cases { for _, tc := range cases {
-4
View File
@@ -6,12 +6,8 @@ package host
const ( const (
// ConstListInstancesFilter refers to a Google Cloud SDK filter to search for instances // ConstListInstancesFilter refers to a Google Cloud SDK filter to search for instances
ConstListInstancesFilter = "filters" ConstListInstancesFilter = "filters"
// ConstInstanceGroup refers to the name of a Google Cloud instance group
ConstInstanceGroup = "instance_group"
) )
var allowedSetFields = map[string]struct{}{ var allowedSetFields = map[string]struct{}{
ConstListInstancesFilter: {}, ConstListInstancesFilter: {},
ConstInstanceGroup: {},
} }
+2 -5
View File
@@ -452,7 +452,7 @@ func (p *HostPlugin) OnDeleteSet(ctx context.Context, req *pb.OnDeleteSetRequest
return &pb.OnDeleteSetResponse{}, nil return &pb.OnDeleteSetResponse{}, nil
} }
// ListHosts returns the list of instances based on filter or instance group name. // ListHosts returns the list of instances based on instance filter.
func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*pb.ListHostsResponse, error) { func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*pb.ListHostsResponse, error) {
catalog := req.GetCatalog() catalog := req.GetCatalog()
if catalog == nil { if catalog == nil {
@@ -588,11 +588,8 @@ func validateSet(s *hostsets.HostSet) error {
badFields := make(map[string]string) badFields := make(map[string]string)
_, filterSet := attrMap[ConstListInstancesFilter] _, filterSet := attrMap[ConstListInstancesFilter]
_, instanceGroupSet := attrMap[ConstInstanceGroup]
if instanceGroupSet && filterSet { if filterSet && len(attrs.Filters) == 0 {
badFields["attributes"] = "must set instance group or filter, cannot set both"
} else if filterSet && len(attrs.Filters) == 0 {
badFields[fmt.Sprintf("attributes.%s", ConstListInstancesFilter)] = "must not be empty" badFields[fmt.Sprintf("attributes.%s", ConstListInstancesFilter)] = "must not be empty"
} }