1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
package gcputil
import (
"fmt"
"regexp"
"time"
"google.golang.org/api/compute/v1"
)
var gcpLabelRegex = regexp.MustCompile("^(?P<key>[a-z]([\\w-]+)?):(?P<value>[\\w-]*)$")
func ParseGcpLabels(labels []string) (parsed map[string]string, invalid []string) {
parsed = map[string]string{}
invalid = []string{}
for _, labelStr := range labels {
matches := gcpLabelRegex.FindStringSubmatch(labelStr)
if len(matches) == 0 {
invalid = append(invalid, labelStr)
continue
}
captureNames := gcpLabelRegex.SubexpNames()
var keyPtr, valPtr *string
for i, name := range captureNames {
if name == "key" {
keyPtr = &matches[i]
} else if name == "value" {
valPtr = &matches[i]
}
}
if keyPtr == nil || valPtr == nil || len(*keyPtr) < 1 {
invalid = append(invalid, labelStr)
continue
} else {
parsed[*keyPtr] = *valPtr
}
}
return parsed, invalid
}
type CustomJWTClaims struct {
Google *GoogleJWTClaims `json:"google,omitempty"`
}
type GoogleJWTClaims struct {
Compute *GCEIdentityMetadata `json:"compute_engine,omitempty"`
}
type GCEIdentityMetadata struct {
// ProjectId is the ID for the project where you created the instance.
ProjectId string `json:"project_id" structs:"project_id" mapstructure:"project_id"`
// ProjectNumber is the unique ID for the project where you created the instance.
ProjectNumber int64 `json:"project_number" structs:"project_number" mapstructure:"project_number"`
// Zone is the zone where the instance is located.
Zone string `json:"zone" structs:"zone" mapstructure:"zone"`
// InstanceId is the unique ID for the instance to which this token belongs. This ID is unique and never reused.
InstanceId string `json:"instance_id" structs:"instance_id" mapstructure:"instance_id"`
// InstanceName is the name of the instance to which this token belongs. This name can be reused by several
// instances over time, so use the instance_id value to identify a unique instance ID.
InstanceName string `json:"instance_name" structs:"instance_name" mapstructure:"instance_name"`
// CreatedAt is a unix timestamp indicating when you created the instance.
CreatedAt int64 `json:"instance_creation_timestamp" structs:"instance_creation_timestamp" mapstructure:"instance_creation_timestamp"`
}
// GetVerifiedInstance returns the Instance as described by the identity metadata or an error.
// If the instance has an invalid status or its creation timestamp does not match the metadata value,
// this will return nil and an error.
func (meta *GCEIdentityMetadata) GetVerifiedInstance(gceClient *compute.Service) (*compute.Instance, error) {
instance, err := gceClient.Instances.Get(meta.ProjectId, meta.Zone, meta.InstanceName).Do()
if err != nil {
return nil, fmt.Errorf("unable to find instance associated with token: %v", err)
}
if !IsValidInstanceStatus(instance.Status) {
return nil, fmt.Errorf("authenticating instance %s found but has invalid status '%s'", instance.Name, instance.Status)
}
// Parse the metadata CreatedAt into time.
metaTime := time.Unix(meta.CreatedAt, 0)
// Parse instance creationTimestamp into time.
actualTime, err := time.Parse(time.RFC3339Nano, instance.CreationTimestamp)
if err != nil {
return nil, fmt.Errorf("instance 'creationTimestamp' field could not be parsed into time: %s", instance.CreationTimestamp)
}
// Return an error if the metadata creation timestamp is before the instance creation timestamp.
delta := float64(metaTime.Sub(actualTime)) / float64(time.Second)
if delta < -1 {
return nil, fmt.Errorf("metadata instance_creation_timestamp %d is before instance's creation time %d", actualTime.Unix(), metaTime.Unix())
}
return instance, nil
}
var validInstanceStates map[string]struct{} = map[string]struct{}{
"PROVISIONING": struct{}{},
"RUNNING": struct{}{},
"STAGING": struct{}{},
}
func IsValidInstanceStatus(status string) bool {
_, ok := validInstanceStates[status]
return ok
}
|