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:
1 parent
779d383a24
commit
a75b9bd310
7 files changed
+143
-98
No files matched your search
@@ -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`.
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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).
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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: {},
|
|
||||||
}
|
}
|
||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user