gcp: support projects with no default permissions (#3656)

* helm/gcp: use service account in operator and joinservice

* helm: format operator testdata

* terraform/iam: create additional service account for VMs

This service account is used in the following commits and is attached to the VMs

* config: pass VM service account from iam create to cluster create via config

* cli/iamcreate: limit name prefix length

* docs: add minimal gcp IAM permissions
This commit is contained in:
Leonard Cohnen 2025-03-25 14:13:38 +01:00 committed by GitHub
parent 83e08e3e37
commit 66815a4a47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 771 additions and 466 deletions

View File

@ -102,6 +102,7 @@ runs:
--tf-log=DEBUG \
--yes ${extraFlags}
# TODO(@3u13r): Replace deprecated --serviceAccountID with --prefix
- name: Constellation iam create gcp
shell: bash
if: inputs.cloudProvider == 'gcp'

View File

@ -258,6 +258,12 @@ runs:
run: |
uuid=$(uuidgen | tr "[:upper:]" "[:lower:]")
uuid=${uuid%%-*}
# GCP has a 6 character limit the additional uuid prefix since the full prefix length has a maximum of 24
if [[ ${{ inputs.cloudProvider }} == 'gcp' ]]; then
uuid=${uuid:0:6}
fi
echo "uuid=${uuid}" | tee -a $GITHUB_OUTPUT
echo "prefix=e2e-${{ github.run_id }}-${{ github.run_attempt }}-${uuid}" | tee -a $GITHUB_OUTPUT

View File

@ -91,6 +91,7 @@ type GCPIAMConfig struct {
Zone string
ProjectID string
ServiceAccountID string
NamePrefix string
}
// AzureIAMConfig holds the necessary values for Azure IAM configuration.
@ -141,6 +142,7 @@ func (c *IAMCreator) createGCP(ctx context.Context, cl tfIAMClient, opts *IAMCon
vars := terraform.GCPIAMVariables{
ServiceAccountID: opts.GCP.ServiceAccountID,
NamePrefix: opts.GCP.NamePrefix,
Project: opts.GCP.ProjectID,
Region: opts.GCP.Region,
Zone: opts.GCP.Zone,
@ -158,7 +160,8 @@ func (c *IAMCreator) createGCP(ctx context.Context, cl tfIAMClient, opts *IAMCon
return IAMOutput{
CloudProvider: cloudprovider.GCP,
GCPOutput: GCPIAMOutput{
ServiceAccountKey: iamOutput.GCP.SaKey,
ServiceAccountKey: iamOutput.GCP.SaKey,
IAMServiceAccountVM: iamOutput.GCP.ServiceAccountVMMailAddress,
},
}, nil
}
@ -232,7 +235,8 @@ type IAMOutput struct {
// GCPIAMOutput contains the output information of a GCP IAM configuration.
type GCPIAMOutput struct {
ServiceAccountKey string `json:"serviceAccountID,omitempty"`
ServiceAccountKey string `json:"serviceAccountID,omitempty"`
IAMServiceAccountVM string `json:"iamServiceAccountVM,omitempty"`
}
// AzureIAMOutput contains the output information of a Microsoft Azure IAM configuration.

View File

@ -22,6 +22,9 @@ import (
// UpgradeRequiresIAMMigration returns true if the given cloud provider requires an IAM migration.
func UpgradeRequiresIAMMigration(provider cloudprovider.Provider) bool {
switch provider {
case cloudprovider.GCP:
// TODO(@3u13r): remove this case after the v2.22.0 release
return true
default:
return false
}

View File

@ -231,6 +231,7 @@ func gcpTerraformVars(conf *config.Config, imageRef string) *terraform.GCPCluste
InternalLoadBalancer: conf.InternalLoadBalancer,
CCTechnology: ccTech,
AdditionalLabels: conf.Tags,
IAMServiceAccountVM: conf.Provider.GCP.IAMServiceAccountVM,
}
}
@ -240,6 +241,7 @@ func gcpTerraformIAMVars(conf *config.Config, oldVars terraform.GCPIAMVariables)
Region: conf.Provider.GCP.Region,
Zone: conf.Provider.GCP.Zone,
ServiceAccountID: oldVars.ServiceAccountID,
NamePrefix: oldVars.NamePrefix,
}
}

View File

@ -256,6 +256,7 @@ func TestValidateInputs(t *testing.T) {
ClientX509CertURL: "client_cert",
}))
cfg.Provider.GCP.ServiceAccountKeyPath = "saKey.json"
cfg.Provider.GCP.IAMServiceAccountVM = "example@example.com"
}
require.NoError(fh.WriteYAML(constants.ConfigFilename, cfg))

View File

@ -29,6 +29,9 @@ var (
regionRegex = regexp.MustCompile(`^\w+-\w+[0-9]$`)
// Source: https://cloud.google.com/resource-manager/reference/rest/v1/projects.
gcpIDRegex = regexp.MustCompile(`^[a-z][-a-z0-9]{4,28}[a-z0-9]$`)
// We currently append 6 characters to the prefix, therefore we remove 6 characters from the gcpIDRegex.
gcpPrefixRegex = regexp.MustCompile(`^[a-z][-a-z0-9]{4,22}[a-z0-9]$`)
)
// newIAMCreateCmd returns a new cobra.Command for the iam create parent command. It needs another verb, and does nothing on its own.

View File

@ -456,6 +456,7 @@ func TestIAMCreateGCP(t *testing.T) {
creator *stubIAMCreator
zoneFlag string
serviceAccountIDFlag string
namePrefixFlag string
projectIDFlag string
yesFlag bool
updateConfigFlag bool
@ -466,6 +467,14 @@ func TestIAMCreateGCP(t *testing.T) {
wantErr bool
}{
"iam create gcp": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
yesFlag: true,
},
"iam create gcp with deprecated serice account flag": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
@ -474,91 +483,91 @@ func TestIAMCreateGCP(t *testing.T) {
yesFlag: true,
},
"iam create gcp with existing config": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
yesFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
yesFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
},
"iam create gcp --update-config": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
updateConfigFlag: true,
yesFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
updateConfigFlag: true,
yesFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
},
"iam create gcp existing terraform dir": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
existingDirs: []string{constants.TerraformIAMWorkingDir},
yesFlag: true,
wantErr: true,
},
"iam create gcp invalid b64": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: invalidIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
yesFlag: true,
wantErr: true,
setupFs: defaultFs,
creator: &stubIAMCreator{id: invalidIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
yesFlag: true,
wantErr: true,
},
"interactive": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "yes\n",
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "yes\n",
},
"interactive update config": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "yes\n",
updateConfigFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "yes\n",
updateConfigFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
},
"interactive abort": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "no\n",
wantAbort: true,
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "no\n",
wantAbort: true,
},
"interactive abort update config": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "no\n",
wantAbort: true,
updateConfigFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
stdin: "no\n",
wantAbort: true,
updateConfigFlag: true,
existingConfigFiles: []string{constants.ConfigFilename},
},
"unwritable fs": {
setupFs: readOnlyFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
yesFlag: true,
updateConfigFlag: true,
wantErr: true,
setupFs: readOnlyFs,
creator: &stubIAMCreator{id: validIAMIDFile},
zoneFlag: "europe-west1-a",
namePrefixFlag: "constell-test",
projectIDFlag: "constell-1234",
yesFlag: true,
updateConfigFlag: true,
wantErr: true,
},
}
@ -590,6 +599,7 @@ func TestIAMCreateGCP(t *testing.T) {
flags: gcpIAMCreateFlags{
zone: tc.zoneFlag,
serviceAccountID: tc.serviceAccountIDFlag,
namePrefix: tc.serviceAccountIDFlag,
projectID: tc.projectIDFlag,
},
},

View File

@ -31,13 +31,19 @@ func newIAMCreateGCPCmd() *cobra.Command {
cmd.Flags().String("zone", "", "GCP zone the cluster will be deployed in (required)\n"+
"Find a list of available zones here: https://cloud.google.com/compute/docs/regions-zones#available")
must(cobra.MarkFlagRequired(cmd.Flags(), "zone"))
cmd.Flags().String("serviceAccountID", "", "ID for the service account that will be created (required)\n"+
"Must be 6 to 30 lowercase letters, digits, or hyphens.")
must(cobra.MarkFlagRequired(cmd.Flags(), "serviceAccountID"))
cmd.Flags().String("serviceAccountID", "", "[Deprecated use \"--prefix\"]ID for the service account that will be created (required)\n"+
"Must be 6 to 30 lowercase letters, digits, or hyphens. This flag is mutually exclusive with --prefix.")
cmd.Flags().String("prefix", "", "Prefix for the service account ID and VM ID that will be created (required)\n"+
"Must be letters, digits, or hyphens.")
cmd.Flags().String("projectID", "", "ID of the GCP project the configuration will be created in (required)\n"+
"Find it on the welcome screen of your project: https://console.cloud.google.com/welcome")
must(cobra.MarkFlagRequired(cmd.Flags(), "projectID"))
cmd.MarkFlagsMutuallyExclusive([]string{"prefix", "serviceAccountID"}...)
must(cmd.Flags().MarkDeprecated("serviceAccountID", "use --prefix instead"))
return cmd
}
@ -53,6 +59,7 @@ func runIAMCreateGCP(cmd *cobra.Command, _ []string) error {
type gcpIAMCreateFlags struct {
rootFlags
serviceAccountID string
namePrefix string
zone string
region string
projectID string
@ -91,9 +98,18 @@ func (f *gcpIAMCreateFlags) parse(flags *pflag.FlagSet) error {
if err != nil {
return fmt.Errorf("getting 'serviceAccountID' flag: %w", err)
}
if !gcpIDRegex.MatchString(f.serviceAccountID) {
if f.serviceAccountID != "" && !gcpIDRegex.MatchString(f.serviceAccountID) {
return fmt.Errorf("serviceAccountID %q doesn't match %s", f.serviceAccountID, gcpIDRegex)
}
f.namePrefix, err = flags.GetString("prefix")
if err != nil {
return fmt.Errorf("getting 'prefix' flag: %w", err)
}
if f.namePrefix != "" && !gcpPrefixRegex.MatchString(f.namePrefix) {
return fmt.Errorf("prefix %q doesn't match %s", f.namePrefix, gcpIDRegex)
}
return nil
}
@ -109,13 +125,19 @@ func (c *gcpIAMCreator) getIAMConfigOptions() *cloudcmd.IAMConfigOptions {
Region: c.flags.region,
ProjectID: c.flags.projectID,
ServiceAccountID: c.flags.serviceAccountID,
NamePrefix: c.flags.namePrefix,
},
}
}
func (c *gcpIAMCreator) printConfirmValues(cmd *cobra.Command) {
cmd.Printf("Project ID:\t\t%s\n", c.flags.projectID)
cmd.Printf("Service Account ID:\t%s\n", c.flags.serviceAccountID)
if c.flags.namePrefix != "" {
cmd.Printf("Name Prefix:\t\t%s\n", c.flags.namePrefix)
}
if c.flags.serviceAccountID != "" {
cmd.Printf("Service Account ID:\t%s\n", c.flags.serviceAccountID)
}
cmd.Printf("Region:\t\t\t%s\n", c.flags.region)
cmd.Printf("Zone:\t\t\t%s\n\n", c.flags.zone)
}
@ -127,11 +149,12 @@ func (c *gcpIAMCreator) printOutputValues(cmd *cobra.Command, _ cloudcmd.IAMOutp
cmd.Printf("serviceAccountKeyPath:\t%s\n\n", c.flags.pathPrefixer.PrefixPrintablePath(constants.GCPServiceAccountKeyFilename))
}
func (c *gcpIAMCreator) writeOutputValuesToConfig(conf *config.Config, _ cloudcmd.IAMOutput) {
func (c *gcpIAMCreator) writeOutputValuesToConfig(conf *config.Config, out cloudcmd.IAMOutput) {
conf.Provider.GCP.Project = c.flags.projectID
conf.Provider.GCP.ServiceAccountKeyPath = constants.GCPServiceAccountKeyFilename // File was created in workspace, so only the filename is needed.
conf.Provider.GCP.Region = c.flags.region
conf.Provider.GCP.Zone = c.flags.zone
conf.Provider.GCP.IAMServiceAccountVM = out.GCPOutput.IAMServiceAccountVM
for groupName, group := range conf.NodeGroups {
group.Zone = c.flags.zone
conf.NodeGroups[groupName] = group

View File

@ -539,6 +539,7 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
conf.Provider.GCP.Project = "test-project"
conf.Provider.GCP.Zone = "test-zone"
conf.Provider.GCP.ServiceAccountKeyPath = "test-key-path"
conf.Provider.GCP.IAMServiceAccountVM = "example@example.com"
conf.Attestation.GCPSEVSNP.Measurements[4] = measurements.WithAllBytes(0x44, measurements.Enforce, measurements.PCRMeasurementLength)
conf.Attestation.GCPSEVSNP.Measurements[9] = measurements.WithAllBytes(0x11, measurements.Enforce, measurements.PCRMeasurementLength)
conf.Attestation.GCPSEVSNP.Measurements[12] = measurements.WithAllBytes(0xcc, measurements.Enforce, measurements.PCRMeasurementLength)

View File

@ -103,9 +103,18 @@ func (c *Client) ShowIAM(ctx context.Context, provider cloudprovider.Provider) (
if !ok {
return IAMOutput{}, errors.New("invalid type in service_account_key output: not a string")
}
IAMServiceAccountVMOutputRaw, ok := tfState.Values.Outputs["service_account_mail_vm"]
if !ok {
return IAMOutput{}, errors.New("no service_account_mail_vm output found")
}
IAMServiceAccountVMOutput, ok := IAMServiceAccountVMOutputRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in service_account_mail_vm output: not a string")
}
return IAMOutput{
GCP: GCPIAMOutput{
SaKey: saKeyOutput,
SaKey: saKeyOutput,
ServiceAccountVMMailAddress: IAMServiceAccountVMOutput,
},
}, nil
case cloudprovider.Azure:
@ -539,7 +548,8 @@ type IAMOutput struct {
// GCPIAMOutput contains the output information of the Terraform IAM operation on GCP.
type GCPIAMOutput struct {
SaKey string
SaKey string
ServiceAccountVMMailAddress string
}
// AzureIAMOutput contains the output information of the Terraform IAM operation on Microsoft Azure.

View File

@ -120,6 +120,7 @@ func TestPrepareIAM(t *testing.T) {
Region: "europe-west1",
Zone: "europe-west1-a",
ServiceAccountID: "const-test-case",
NamePrefix: "test_iam",
}
azureVars := &AzureIAMVariables{
Location: "westus",
@ -509,6 +510,9 @@ func TestCreateIAM(t *testing.T) {
"service_account_key": {
Value: "12345678_abcdefg",
},
"service_account_mail_vm": {
Value: "test_iam_service_account_vm",
},
"subscription_id": {
Value: "test_subscription_id",
},
@ -581,7 +585,7 @@ func TestCreateIAM(t *testing.T) {
vars: gcpVars,
tf: &stubTerraform{showState: newTestState()},
fs: afero.NewMemMapFs(),
want: IAMOutput{GCP: GCPIAMOutput{SaKey: "12345678_abcdefg"}},
want: IAMOutput{GCP: GCPIAMOutput{SaKey: "12345678_abcdefg", ServiceAccountVMMailAddress: "test_iam_service_account_vm"}},
},
"gcp init fails": {
pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"),
@ -614,7 +618,25 @@ func TestCreateIAM(t *testing.T) {
tf: &stubTerraform{
showState: &tfjson.State{
Values: &tfjson.StateValues{
Outputs: map[string]*tfjson.StateOutput{},
Outputs: map[string]*tfjson.StateOutput{
"service_account_mail_vm": {Value: "test_iam_service_account_vm"},
},
},
},
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
"gcp no service_account_mail_vm": {
pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"),
provider: cloudprovider.GCP,
vars: gcpVars,
tf: &stubTerraform{
showState: &tfjson.State{
Values: &tfjson.StateValues{
Outputs: map[string]*tfjson.StateOutput{
"service_account_key": {Value: "12345678_abcdefg"},
},
},
},
},
@ -1129,7 +1151,8 @@ func TestShowIAM(t *testing.T) {
"GCP success": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"service_account_key": "key",
"service_account_key": "key",
"service_account_mail_vm": "example@example.com",
}),
},
csp: cloudprovider.GCP,
@ -1137,7 +1160,8 @@ func TestShowIAM(t *testing.T) {
"GCP wrong data type": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"service_account_key": map[string]any{},
"service_account_key": map[string]any{},
"service_account_mail_vm": "example@example.com",
}),
},
csp: cloudprovider.GCP,
@ -1145,7 +1169,9 @@ func TestShowIAM(t *testing.T) {
},
"GCP missing key": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{}),
showState: getTfjsonState(map[string]any{
"service_account_mail_vm": "example@example.com",
}),
},
csp: cloudprovider.GCP,
wantErr: true,

View File

@ -141,6 +141,8 @@ type GCPClusterVariables struct {
InternalLoadBalancer bool `hcl:"internal_load_balancer" cty:"internal_load_balancer"`
// CCTechnology is the confidential computing technology to use on the VMs. (`SEV` or `SEV_SNP`)
CCTechnology string `hcl:"cc_technology" cty:"cc_technology"`
// IAMServiceAccountControlPlane is the IAM service account mail address to attach to VMs.
IAMServiceAccountVM string `hcl:"iam_service_account_vm" cty:"iam_service_account_vm"`
// AdditionalLables are (optional) additional labels that should be applied to created resources.
AdditionalLabels cloudprovider.Tags `hcl:"additional_labels" cty:"additional_labels"`
}
@ -182,6 +184,9 @@ type GCPIAMVariables struct {
Zone string `hcl:"zone" cty:"zone"`
// ServiceAccountID is the ID of the service account to use.
ServiceAccountID string `hcl:"service_account_id" cty:"service_account_id"`
// IAMServiceAccountVM is the ID of the service account to attach to VMs.
// TODO(@3u13r): Eventually remove this field after v2.22 has been released.
NamePrefix string `hcl:"name_prefix,optional" cty:"name_prefix"`
}
// String returns a string representation of the IAM-specific variables, formatted as Terraform variables.

View File

@ -122,8 +122,9 @@ func TestGCPClusterVariables(t *testing.T) {
DiskType: "pd-ssd",
},
},
CustomEndpoint: "example.com",
CCTechnology: "SEV_SNP",
CustomEndpoint: "example.com",
CCTechnology: "SEV_SNP",
IAMServiceAccountVM: "example@example.com",
}
// test that the variables are correctly rendered
@ -151,10 +152,11 @@ node_groups = {
zone = "eu-central-1b"
}
}
custom_endpoint = "example.com"
internal_load_balancer = false
cc_technology = "SEV_SNP"
additional_labels = null
custom_endpoint = "example.com"
internal_load_balancer = false
cc_technology = "SEV_SNP"
iam_service_account_vm = "example@example.com"
additional_labels = null
`
got := vars.String()
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
@ -173,9 +175,27 @@ func TestGCPIAMVariables(t *testing.T) {
region = "eu-central-1"
zone = "eu-central-1a"
service_account_id = "my-service-account"
name_prefix = ""
`
got := vars.String()
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
vars = GCPIAMVariables{
Project: "my-project",
Region: "eu-central-1",
Zone: "eu-central-1a",
NamePrefix: "my-prefix",
}
// test that the variables are correctly rendered
want = `project_id = "my-project"
region = "eu-central-1"
zone = "eu-central-1a"
service_account_id = ""
name_prefix = "my-prefix"
`
got = vars.String()
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestAzureClusterVariables(t *testing.T) {

View File

@ -102,7 +102,7 @@ If you encounter any problem with the following steps, make sure to use the [lat
<TabItem value="gcp" label="GCP">
```bash
constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --serviceAccountID=constell-test --update-config
constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --prefix=constell-test --update-config
```
This command creates IAM configuration in the GCP project `yourproject-12345` on the GCP zone `europe-west2-a` creating a new service account `constell-test`. It also updates the configuration file `constellation-conf.yaml` in your current directory with the IAM values filled in.

View File

@ -234,6 +234,9 @@ Enable the [Compute Engine API](https://console.cloud.google.com/apis/library/co
To [create the IAM configuration](../workflows/config.md#creating-an-iam-configuration) for Constellation, you need the following permissions:
* `iam.roles.create`
* `iam.roles.delete`
* `iam.roles.get`
* `iam.serviceAccountKeys.create`
* `iam.serviceAccountKeys.delete`
* `iam.serviceAccountKeys.get`

View File

@ -685,13 +685,13 @@ constellation iam create gcp [flags]
### Options
```
-h, --help help for gcp
--projectID string ID of the GCP project the configuration will be created in (required)
Find it on the welcome screen of your project: https://console.cloud.google.com/welcome
--serviceAccountID string ID for the service account that will be created (required)
Must be 6 to 30 lowercase letters, digits, or hyphens.
--zone string GCP zone the cluster will be deployed in (required)
Find a list of available zones here: https://cloud.google.com/compute/docs/regions-zones#available
-h, --help help for gcp
--prefix string Prefix for the service account ID and VM ID that will be created (required)
Must be letters, digits, or hyphens.
--projectID string ID of the GCP project the configuration will be created in (required)
Find it on the welcome screen of your project: https://console.cloud.google.com/welcome
--zone string GCP zone the cluster will be deployed in (required)
Find a list of available zones here: https://cloud.google.com/compute/docs/regions-zones#available
```
### Options inherited from parent commands

View File

@ -210,7 +210,7 @@ Paste the output into the corresponding fields of the `constellation-conf.yaml`
You must be authenticated with the [GCP CLI](https://cloud.google.com/sdk/gcloud) in the shell session with a user that has the [required permissions for IAM creation](../getting-started/install.md#set-up-cloud-credentials).
```bash
constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --serviceAccountID=constell-test
constellation iam create gcp --projectID=yourproject-12345 --zone=europe-west2-a --prefix=constell-test
```
This command creates IAM configuration in the GCP project `yourproject-12345` on the GCP zone `europe-west2-a` creating a new service account `constell-test`.

View File

@ -188,6 +188,9 @@ type GCPConfig struct {
// Path of service account key file. For required service account roles, see https://docs.edgeless.systems/constellation/getting-started/install#authorization
ServiceAccountKeyPath string `yaml:"serviceAccountKeyPath" validate:"required"`
// description: |
// GCP service account mail address. This is being attached to the VMs for authorization.
IAMServiceAccountVM string `yaml:"IAMServiceAccountVM"`
// description: |
// Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage
DeployCSIDriver *bool `yaml:"deployCSIDriver" validate:"required"`
// description: |
@ -349,6 +352,7 @@ func Default() *Config {
Region: "",
Zone: "",
ServiceAccountKeyPath: "",
IAMServiceAccountVM: "",
DeployCSIDriver: toPtr(true),
UseMarketplaceImage: toPtr(false),
},

View File

@ -241,7 +241,7 @@ func init() {
FieldName: "gcp",
},
}
GCPConfigDoc.Fields = make([]encoder.Doc, 6)
GCPConfigDoc.Fields = make([]encoder.Doc, 7)
GCPConfigDoc.Fields[0].Name = "project"
GCPConfigDoc.Fields[0].Type = "string"
GCPConfigDoc.Fields[0].Note = ""
@ -262,16 +262,21 @@ func init() {
GCPConfigDoc.Fields[3].Note = ""
GCPConfigDoc.Fields[3].Description = "Path of service account key file. For required service account roles, see https://docs.edgeless.systems/constellation/getting-started/install#authorization"
GCPConfigDoc.Fields[3].Comments[encoder.LineComment] = "Path of service account key file. For required service account roles, see https://docs.edgeless.systems/constellation/getting-started/install#authorization"
GCPConfigDoc.Fields[4].Name = "deployCSIDriver"
GCPConfigDoc.Fields[4].Type = "bool"
GCPConfigDoc.Fields[4].Name = "IAMServiceAccountVM"
GCPConfigDoc.Fields[4].Type = "string"
GCPConfigDoc.Fields[4].Note = ""
GCPConfigDoc.Fields[4].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
GCPConfigDoc.Fields[5].Name = "useMarketplaceImage"
GCPConfigDoc.Fields[4].Description = "GCP service account mail address. This is being attached to the VMs for authorization."
GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "GCP service account mail address. This is being attached to the VMs for authorization."
GCPConfigDoc.Fields[5].Name = "deployCSIDriver"
GCPConfigDoc.Fields[5].Type = "bool"
GCPConfigDoc.Fields[5].Note = ""
GCPConfigDoc.Fields[5].Description = "Use the specified GCP Marketplace image offering."
GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Use the specified GCP Marketplace image offering."
GCPConfigDoc.Fields[5].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
GCPConfigDoc.Fields[6].Name = "useMarketplaceImage"
GCPConfigDoc.Fields[6].Type = "bool"
GCPConfigDoc.Fields[6].Note = ""
GCPConfigDoc.Fields[6].Description = "Use the specified GCP Marketplace image offering."
GCPConfigDoc.Fields[6].Comments[encoder.LineComment] = "Use the specified GCP Marketplace image offering."
OpenStackConfigDoc.Type = "OpenStackConfig"
OpenStackConfigDoc.Comments[encoder.LineComment] = "OpenStackConfig holds config information for OpenStack based Constellation deployments."

View File

@ -464,6 +464,7 @@ func TestValidate(t *testing.T) {
gcp.Project = "test-project"
gcp.Zone = "test-zone"
gcp.ServiceAccountKeyPath = "test-key-path"
gcp.IAMServiceAccountVM = "example@example.com"
cnf.Provider = ProviderConfig{}
cnf.Provider.GCP = gcp
cnf.Attestation.GCPSEVSNP.Measurements = measurements.M{

View File

@ -40,6 +40,9 @@ spec:
- --cloud-provider={{ .Values.csp }}
- --key-service-endpoint=key-service.{{ .Release.Namespace }}:{{ .Values.global.keyServicePort }}
- --attestation-variant={{ .Values.attestationVariant }}
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
volumeMounts:
- mountPath: {{ .Values.global.serviceBasePath | quote }}
name: config
@ -47,6 +50,9 @@ spec:
- mountPath: /etc/kubernetes
name: kubeadm
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
ports:
- containerPort: {{ .Values.joinServicePort }}
name: tcp
@ -54,6 +60,10 @@ spec:
securityContext:
privileged: true
volumes:
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: config
projected:
sources:

View File

@ -42,6 +42,8 @@ spec:
value: {{ .Values.csp | quote }}
- name: constellation-uid
value: {{ .Values.constellationUID | quote }}
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
image: {{ .Values.controllerManager.manager.image | quote }}
livenessProbe:
httpGet:
@ -72,6 +74,9 @@ spec:
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
@ -109,6 +114,10 @@ spec:
name: gceconf
optional: true
name: gceconf
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock

View File

@ -37,58 +37,63 @@ spec:
kubectl.kubernetes.io/default-container: manager
spec:
containers:
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: GCP
- name: constellation-uid
value: "42424242424242"
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: GCP
- name: constellation-uid
value: "42424242424242"
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
nodeSelector:
node-role.kubernetes.io/control-plane: ""
securityContext:
@ -96,34 +101,38 @@ spec:
serviceAccountName: constellation-operator-controller-manager
terminationGracePeriodSeconds: 10
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
volumes:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
name: gceconf
optional: true
name: gceconf
optional: true
name: gceconf
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket

View File

@ -40,6 +40,9 @@ spec:
- --cloud-provider=AWS
- --key-service-endpoint=key-service.testNamespace:9000
- --attestation-variant=aws-nitro-tpm
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
volumeMounts:
- mountPath: /var/config
name: config
@ -47,6 +50,9 @@ spec:
- mountPath: /etc/kubernetes
name: kubeadm
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
ports:
- containerPort: 9090
name: tcp
@ -54,6 +60,10 @@ spec:
securityContext:
privileged: true
volumes:
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: config
projected:
sources:

View File

@ -37,58 +37,63 @@ spec:
kubectl.kubernetes.io/default-container: manager
spec:
containers:
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: Azure
- name: constellation-uid
value: "42424242424242"
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: Azure
- name: constellation-uid
value: "42424242424242"
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
nodeSelector:
node-role.kubernetes.io/control-plane: ""
securityContext:
@ -96,34 +101,38 @@ spec:
serviceAccountName: constellation-operator-controller-manager
terminationGracePeriodSeconds: 10
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
volumes:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
name: gceconf
optional: true
name: gceconf
optional: true
name: gceconf
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket

View File

@ -40,6 +40,9 @@ spec:
- --cloud-provider=Azure
- --key-service-endpoint=key-service.testNamespace:9000
- --attestation-variant=azure-sev-snp
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
volumeMounts:
- mountPath: /var/config
name: config
@ -47,6 +50,9 @@ spec:
- mountPath: /etc/kubernetes
name: kubeadm
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
ports:
- containerPort: 9090
name: tcp
@ -54,6 +60,10 @@ spec:
securityContext:
privileged: true
volumes:
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: config
projected:
sources:

View File

@ -37,58 +37,63 @@ spec:
kubectl.kubernetes.io/default-container: manager
spec:
containers:
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: GCP
- name: constellation-uid
value: "42424242424242"
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: GCP
- name: constellation-uid
value: "42424242424242"
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
nodeSelector:
node-role.kubernetes.io/control-plane: ""
securityContext:
@ -96,34 +101,38 @@ spec:
serviceAccountName: constellation-operator-controller-manager
terminationGracePeriodSeconds: 10
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
volumes:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
name: gceconf
optional: true
name: gceconf
optional: true
name: gceconf
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket

View File

@ -40,6 +40,9 @@ spec:
- --cloud-provider=GCP
- --key-service-endpoint=key-service.testNamespace:9000
- --attestation-variant=gcp-sev-es
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
volumeMounts:
- mountPath: /var/config
name: config
@ -47,6 +50,9 @@ spec:
- mountPath: /etc/kubernetes
name: kubeadm
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
ports:
- containerPort: 9090
name: tcp
@ -54,6 +60,10 @@ spec:
securityContext:
privileged: true
volumes:
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: config
projected:
sources:

View File

@ -37,58 +37,63 @@ spec:
kubectl.kubernetes.io/default-container: manager
spec:
containers:
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: GCP
- name: constellation-uid
value: "42424242424242"
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
- args:
- --health-probe-bind-address=:8081
- --metrics-bind-address=:8080
- --leader-elect
command:
- /node-operator
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: cluster.local
- name: CONSTEL_CSP
value: GCP
- name: constellation-uid
value: "42424242424242"
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
image: constellationOperatorImage
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: manager
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
- mountPath: /host/usr/lib/os-release
name: usr-lib-os-release
- mountPath: /etc/os-release
name: etc-os-release
- mountPath: /etc/azure
name: azureconfig
readOnly: true
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
nodeSelector:
node-role.kubernetes.io/control-plane: ""
securityContext:
@ -96,34 +101,38 @@ spec:
serviceAccountName: constellation-operator-controller-manager
terminationGracePeriodSeconds: 10
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
volumes:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
- hostPath:
path: /etc/kubernetes/pki/etcd
type: Directory
name: etcd-certs
- hostPath:
path: /usr/lib/os-release
type: File
name: usr-lib-os-release
- hostPath:
path: /etc/os-release
type: File
name: etc-os-release
- name: azureconfig
secret:
optional: true
secretName: azureconfig
- configMap:
name: gceconf
optional: true
name: gceconf
optional: true
name: gceconf
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock
type: Socket

View File

@ -40,6 +40,9 @@ spec:
- --cloud-provider=OpenStack
- --key-service-endpoint=key-service.testNamespace:9000
- --attestation-variant=qemu-vtpm
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
volumeMounts:
- mountPath: /var/config
name: config
@ -47,6 +50,9 @@ spec:
- mountPath: /etc/kubernetes
name: kubeadm
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
ports:
- containerPort: 9090
name: tcp
@ -54,6 +60,10 @@ spec:
securityContext:
privileged: true
volumes:
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: config
projected:
sources:

View File

@ -50,6 +50,8 @@ spec:
value: QEMU
- name: constellation-uid
value: "42424242424242"
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
image: constellationOperatorImage
livenessProbe:
httpGet:
@ -86,6 +88,9 @@ spec:
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
@ -123,6 +128,10 @@ spec:
name: gceconf
optional: true
name: gceconf
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock

View File

@ -40,6 +40,9 @@ spec:
- --cloud-provider=QEMU
- --key-service-endpoint=key-service.testNamespace:9000
- --attestation-variant=qemu-vtpm
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
volumeMounts:
- mountPath: /var/config
name: config
@ -47,6 +50,9 @@ spec:
- mountPath: /etc/kubernetes
name: kubeadm
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
ports:
- containerPort: 9090
name: tcp
@ -54,6 +60,10 @@ spec:
securityContext:
privileged: true
volumes:
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: config
projected:
sources:

View File

@ -31,6 +31,9 @@ spec:
- /manager
args:
- --leader-elect
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
image: controller:latest
name: manager
securityContext:
@ -60,6 +63,9 @@ spec:
- mountPath: /etc/gce
name: gceconf
readOnly: true
- mountPath: /var/secrets/google
name: gcekey
readOnly: true
- mountPath: /etc/constellation-upgrade-agent.sock
name: upgrade-agent-socket
readOnly: true
@ -91,6 +97,10 @@ spec:
configMap:
name: gceconf
optional: true
- name: gcekey
secret:
secretName: gcekey
optional: true
- name: upgrade-agent-socket
hostPath:
path: /run/constellation-upgrade-agent.sock

View File

@ -45,11 +45,11 @@ resource "random_bytes" "measurement_salt" {
module "gcp_iam" {
// replace $VERSION with the Constellation version you want to use, e.g., v2.14.0
source = "https://github.com/edgelesssys/constellation/releases/download/$VERSION/terraform-module.zip//terraform-module/iam/gcp"
project_id = local.project_id
service_account_id = "${local.name}-sa"
zone = local.zone
region = local.region
source = "https://github.com/edgelesssys/constellation/releases/download/$VERSION/terraform-module.zip//terraform-module/iam/gcp"
project_id = local.project_id
name_prefix = local.name
zone = local.zone
region = local.region
}
module "gcp_infrastructure" {
@ -81,6 +81,7 @@ module "gcp_infrastructure" {
project = local.project_id
internal_load_balancer = false
cc_technology = local.cc_technology
iam_service_account_vm = module.gcp_iam.service_account_mail_vm
}
data "constellation_attestation" "foo" {

View File

@ -161,28 +161,29 @@ resource "google_compute_firewall" "firewall_internal_pods" {
}
module "instance_group" {
source = "./modules/instance_group"
for_each = var.node_groups
base_name = local.name
node_group_name = each.key
role = each.value.role
zone = each.value.zone
uid = local.uid
instance_type = each.value.instance_type
initial_count = each.value.initial_count
image_id = var.image_id
disk_size = each.value.disk_size
disk_type = each.value.disk_type
network = google_compute_network.vpc_network.id
subnetwork = google_compute_subnetwork.vpc_subnetwork.id
alias_ip_range_name = google_compute_subnetwork.vpc_subnetwork.secondary_ip_range[0].range_name
kube_env = local.kube_env
debug = var.debug
named_ports = each.value.role == "control-plane" ? local.control_plane_named_ports : []
labels = local.labels
init_secret_hash = local.init_secret_hash
custom_endpoint = var.custom_endpoint
cc_technology = var.cc_technology
source = "./modules/instance_group"
for_each = var.node_groups
base_name = local.name
node_group_name = each.key
role = each.value.role
zone = each.value.zone
uid = local.uid
instance_type = each.value.instance_type
initial_count = each.value.initial_count
image_id = var.image_id
disk_size = each.value.disk_size
disk_type = each.value.disk_type
network = google_compute_network.vpc_network.id
subnetwork = google_compute_subnetwork.vpc_subnetwork.id
alias_ip_range_name = google_compute_subnetwork.vpc_subnetwork.secondary_ip_range[0].range_name
kube_env = local.kube_env
debug = var.debug
named_ports = each.value.role == "control-plane" ? local.control_plane_named_ports : []
labels = local.labels
init_secret_hash = local.init_secret_hash
custom_endpoint = var.custom_endpoint
cc_technology = var.cc_technology
iam_service_account_vm = var.iam_service_account_vm
}
resource "google_compute_address" "loadbalancer_ip_internal" {

View File

@ -77,17 +77,11 @@ resource "google_compute_instance_template" "template" {
on_host_maintenance = "TERMINATE"
}
# Define all IAM access via the service account and not via scopes:
# See: https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance_template#nested_service_account
service_account {
scopes = [
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/servicecontrol",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring.write",
"https://www.googleapis.com/auth/trace.append",
"https://www.googleapis.com/auth/cloud-platform",
]
email = var.iam_service_account_vm
scopes = ["cloud-platform"]
}
shielded_instance_config {

View File

@ -108,3 +108,9 @@ variable "cc_technology" {
error_message = "The confidential computing technology has to be 'SEV' or 'SEV_SNP'."
}
}
variable "iam_service_account_vm" {
type = string
default = ""
description = "IAM service account used for the VMs"
}

View File

@ -76,6 +76,11 @@ variable "additional_labels" {
description = "Additional labels that should be given to created recources."
}
variable "iam_service_account_vm" {
type = string
default = ""
description = "IAM service account used for the VMs"
}
variable "emergency_ssh" {
type = bool
default = false

View File

@ -13,8 +13,19 @@ provider "google" {
zone = var.zone
}
locals {
sa_name = var.name_prefix == "" ? var.service_account_id : "${var.name_prefix}-sa"
sa_vm_name = var.name_prefix == "" ? "${var.service_account_id}-vm" : "${var.name_prefix}-sa-vm"
}
resource "google_service_account" "vm" {
account_id = local.sa_vm_name
display_name = "Constellation service account for VMs"
description = "Service account used by the VMs"
}
resource "google_service_account" "service_account" {
account_id = var.service_account_id
account_id = local.sa_name
display_name = "Constellation service account"
description = "Service account used inside Constellation"
}
@ -65,6 +76,30 @@ resource "google_project_iam_member" "iam_service_account_user_role" {
depends_on = [null_resource.delay]
}
resource "google_project_iam_custom_role" "vm" {
# role_id must not contain dashes
role_id = replace("${local.sa_vm_name}-role", "-", "_")
title = "Constellation IAM role for VMs"
description = "Constellation IAM role for VMs"
permissions = [
"compute.instances.get",
"compute.instances.list",
"compute.subnetworks.get",
"compute.globalForwardingRules.list",
"compute.zones.list",
]
}
resource "google_project_iam_binding" "custom_role_vm_to_service_account_vm" {
project = var.project_id
role = "projects/${var.project_id}/roles/${google_project_iam_custom_role.vm.role_id}"
members = [
"serviceAccount:${google_service_account.vm.email}",
]
depends_on = [null_resource.delay]
}
resource "google_service_account_key" "service_account_key" {
service_account_id = google_service_account.service_account.name
depends_on = [null_resource.delay]

View File

@ -3,3 +3,9 @@ output "service_account_key" {
description = "Private key of the service account."
sensitive = true
}
output "service_account_mail_vm" {
value = google_service_account.vm.email
description = "Mail address of the service account to be attached to the VMs"
sensitive = false
}

View File

@ -5,7 +5,12 @@ variable "project_id" {
variable "service_account_id" {
type = string
description = "ID for the service account being created. Must match ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$."
description = "[DEPRECATED use var.name_prefix] ID for the service account being created. Must match ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$."
}
variable "name_prefix" {
type = string
description = "Prefix to be used for all resources created by this module."
}
variable "region" {