diff --git a/cli/cloud/cloudcmd/clients.go b/cli/cloud/cloudcmd/clients.go index 8fadcab2a..83f52abaf 100644 --- a/cli/cloud/cloudcmd/clients.go +++ b/cli/cloud/cloudcmd/clients.go @@ -11,7 +11,7 @@ import ( type gcpclient interface { GetState() (state.ConstellationState, error) SetState(state.ConstellationState) error - CreateVPCs(ctx context.Context, input gcpcl.VPCsInput) error + CreateVPCs(ctx context.Context) error CreateFirewall(ctx context.Context, input gcpcl.FirewallInput) error CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error CreateServiceAccount(ctx context.Context, input gcpcl.ServiceAccountInput) (string, error) diff --git a/cli/cloud/cloudcmd/clients_test.go b/cli/cloud/cloudcmd/clients_test.go index c72aeebc9..55076f94a 100644 --- a/cli/cloud/cloudcmd/clients_test.go +++ b/cli/cloud/cloudcmd/clients_test.go @@ -261,7 +261,7 @@ func (c *fakeGcpClient) SetState(stat state.ConstellationState) error { return nil } -func (c *fakeGcpClient) CreateVPCs(ctx context.Context, input gcpcl.VPCsInput) error { +func (c *fakeGcpClient) CreateVPCs(ctx context.Context) error { c.network = "network" c.subnetwork = "subnetwork" return nil @@ -377,7 +377,7 @@ func (c *stubGcpClient) SetState(state.ConstellationState) error { return c.setStateErr } -func (c *stubGcpClient) CreateVPCs(ctx context.Context, input gcpcl.VPCsInput) error { +func (c *stubGcpClient) CreateVPCs(ctx context.Context) error { return c.createVPCsErr } diff --git a/cli/cloud/cloudcmd/create.go b/cli/cloud/cloudcmd/create.go index d884453ba..30f15cb08 100644 --- a/cli/cloud/cloudcmd/create.go +++ b/cli/cloud/cloudcmd/create.go @@ -6,6 +6,7 @@ import ( "io" azurecl "github.com/edgelesssys/constellation/cli/azure/client" + "github.com/edgelesssys/constellation/cli/cloud/cloudtypes" "github.com/edgelesssys/constellation/cli/cloudprovider" "github.com/edgelesssys/constellation/cli/gcp" "github.com/edgelesssys/constellation/cli/gcp/client" @@ -41,9 +42,9 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c case cloudprovider.GCP: cl, err := c.newGCPClient( ctx, - *config.Provider.GCP.Project, - *config.Provider.GCP.Zone, - *config.Provider.GCP.Region, + config.Provider.GCP.Project, + config.Provider.GCP.Zone, + config.Provider.GCP.Region, name, ) if err != nil { @@ -53,10 +54,10 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c return c.createGCP(ctx, cl, config, insType, coordCount, nodeCount) case cloudprovider.Azure: cl, err := c.newAzureClient( - *config.Provider.Azure.SubscriptionID, - *config.Provider.Azure.TenantID, + config.Provider.Azure.SubscriptionID, + config.Provider.Azure.TenantID, name, - *config.Provider.Azure.Location, + config.Provider.Azure.Location, ) if err != nil { return state.ConstellationState{}, err @@ -71,19 +72,22 @@ func (c *Creator) createGCP(ctx context.Context, cl gcpclient, config *config.Co ) (stat state.ConstellationState, retErr error) { defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerGCP{client: cl}) - if err := cl.CreateVPCs(ctx, *config.Provider.GCP.VPCsInput); err != nil { + if err := cl.CreateVPCs(ctx); err != nil { return state.ConstellationState{}, err } - if err := cl.CreateFirewall(ctx, *config.Provider.GCP.FirewallInput); err != nil { + if err := cl.CreateFirewall(ctx, gcpcl.FirewallInput{ + Ingress: cloudtypes.Firewall(config.IngressFirewall), + Egress: cloudtypes.Firewall(config.EgressFirewall), + }); err != nil { return state.ConstellationState{}, err } createInput := client.CreateInstancesInput{ CountCoordinators: coordCount, CountNodes: nodeCount, - ImageId: *config.Provider.GCP.Image, + ImageId: config.Provider.GCP.Image, InstanceType: insType, - StateDiskSizeGB: *config.StateDiskSizeGB, + StateDiskSizeGB: config.StateDiskSizeGB, KubeEnv: gcp.KubeEnv, } if err := cl.CreateInstances(ctx, createInput); err != nil { @@ -103,16 +107,20 @@ func (c *Creator) createAzure(ctx context.Context, cl azureclient, config *confi if err := cl.CreateVirtualNetwork(ctx); err != nil { return state.ConstellationState{}, err } - if err := cl.CreateSecurityGroup(ctx, *config.Provider.Azure.NetworkSecurityGroupInput); err != nil { + + if err := cl.CreateSecurityGroup(ctx, azurecl.NetworkSecurityGroupInput{ + Ingress: cloudtypes.Firewall(config.IngressFirewall), + Egress: cloudtypes.Firewall(config.EgressFirewall), + }); err != nil { return state.ConstellationState{}, err } createInput := azurecl.CreateInstancesInput{ CountCoordinators: coordCount, CountNodes: nodeCount, InstanceType: insType, - StateDiskSizeGB: *config.StateDiskSizeGB, - Image: *config.Provider.Azure.Image, - UserAssingedIdentity: *config.Provider.Azure.UserAssignedIdentity, + StateDiskSizeGB: config.StateDiskSizeGB, + Image: config.Provider.Azure.Image, + UserAssingedIdentity: config.Provider.Azure.UserAssignedIdentity, } if err := cl.CreateInstances(ctx, createInput); err != nil { return state.ConstellationState{}, err diff --git a/cli/cloud/cloudcmd/serviceaccount.go b/cli/cloud/cloudcmd/serviceaccount.go index 9dd42466f..6b22ecee2 100644 --- a/cli/cloud/cloudcmd/serviceaccount.go +++ b/cli/cloud/cloudcmd/serviceaccount.go @@ -73,7 +73,7 @@ func (c *ServiceAccountCreator) createServiceAccountGCP(ctx context.Context, cl } input := gcpcl.ServiceAccountInput{ - Roles: *config.Provider.GCP.ServiceAccountRoles, + Roles: config.Provider.GCP.ServiceAccountRoles, } serviceAccount, err := cl.CreateServiceAccount(ctx, input) if err != nil { diff --git a/cli/cloud/cloudcmd/validators.go b/cli/cloud/cloudcmd/validators.go index 2127faf44..9d6eff237 100644 --- a/cli/cloud/cloudcmd/validators.go +++ b/cli/cloud/cloudcmd/validators.go @@ -68,19 +68,19 @@ func (v *Validators) updatePCR(pcrIndex uint32, encoded string) error { func (v *Validators) setPCRs(config *config.Config) error { switch v.provider { case cloudprovider.GCP: - gcpPCRs := *config.Provider.GCP.Measurements + gcpPCRs := config.Provider.GCP.Measurements if err := v.checkPCRs(gcpPCRs); err != nil { return err } v.pcrs = gcpPCRs case cloudprovider.Azure: - azurePCRs := *config.Provider.Azure.Measurements + azurePCRs := config.Provider.Azure.Measurements if err := v.checkPCRs(azurePCRs); err != nil { return err } v.pcrs = azurePCRs case cloudprovider.QEMU: - qemuPCRs := *config.Provider.QEMU.Measurements + qemuPCRs := config.Provider.QEMU.Measurements if err := v.checkPCRs(qemuPCRs); err != nil { return err } diff --git a/cli/cloud/cloudcmd/validators_test.go b/cli/cloud/cloudcmd/validators_test.go index 0a0de22e5..07dc1b699 100644 --- a/cli/cloud/cloudcmd/validators_test.go +++ b/cli/cloud/cloudcmd/validators_test.go @@ -66,18 +66,18 @@ func TestNewValidators(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - conf := &config.Config{Provider: &config.ProviderConfig{}} + conf := &config.Config{Provider: config.ProviderConfig{}} if tc.provider == cloudprovider.GCP { measurements := config.Measurements(tc.pcrs) - conf.Provider.GCP = &config.GCPConfig{Measurements: &measurements} + conf.Provider.GCP = &config.GCPConfig{Measurements: measurements} } if tc.provider == cloudprovider.Azure { measurements := config.Measurements(tc.pcrs) - conf.Provider.Azure = &config.AzureConfig{Measurements: &measurements} + conf.Provider.Azure = &config.AzureConfig{Measurements: measurements} } if tc.provider == cloudprovider.QEMU { measurements := config.Measurements(tc.pcrs) - conf.Provider.QEMU = &config.QEMUConfig{Measurements: &measurements} + conf.Provider.QEMU = &config.QEMUConfig{Measurements: measurements} } validators, err := NewValidators(tc.provider, conf) diff --git a/cli/cloud/cloudtypes/firewall.go b/cli/cloud/cloudtypes/firewall.go index 83c05ad95..939a7fa03 100644 --- a/cli/cloud/cloudtypes/firewall.go +++ b/cli/cloud/cloudtypes/firewall.go @@ -6,20 +6,14 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/edgelesssys/constellation/internal/config" computepb "google.golang.org/genproto/googleapis/cloud/compute/v1" "google.golang.org/protobuf/proto" ) -type FirewallRule struct { - Name string - Description string - Protocol string - IPRange string - FromPort int - ToPort int -} +type FirewallRule = config.FirewallRule -type Firewall []FirewallRule +type Firewall config.Firewall func (f Firewall) GCP() ([]*computepb.Firewall, error) { var fw []*computepb.Firewall diff --git a/cli/cmd/configgenerate.go b/cli/cmd/configgenerate.go index cf8034b4c..156cc638f 100644 --- a/cli/cmd/configgenerate.go +++ b/cli/cmd/configgenerate.go @@ -6,7 +6,7 @@ import ( "github.com/edgelesssys/constellation/internal/file" "github.com/spf13/afero" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" + "github.com/talos-systems/talos/pkg/machinery/config/encoder" ) func newConfigGenerateCmd() *cobra.Command { @@ -38,7 +38,12 @@ func configGenerate(cmd *cobra.Command, fileHandler file.Handler) error { } if flags.file == "-" { - return yaml.NewEncoder(cmd.OutOrStdout()).Encode(config.Default()) + content, err := encoder.NewEncoder(config.Default()).Encode() + if err != nil { + return err + } + _, err = cmd.OutOrStdout().Write(content) + return err } return fileHandler.WriteYAML(flags.file, config.Default(), 0o644) diff --git a/cli/cmd/configgenerate_test.go b/cli/cmd/configgenerate_test.go index 83925712c..6577e1bab 100644 --- a/cli/cmd/configgenerate_test.go +++ b/cli/cmd/configgenerate_test.go @@ -13,12 +13,6 @@ import ( "gopkg.in/yaml.v3" ) -func defaultConfigAsYAML(t *testing.T) string { - var readBuffer bytes.Buffer - require.NoError(t, yaml.NewEncoder(&readBuffer).Encode(config.Default())) - return readBuffer.String() -} - func TestConfigGenerateDefault(t *testing.T) { assert := assert.New(t) require := require.New(t) @@ -28,9 +22,10 @@ func TestConfigGenerateDefault(t *testing.T) { require.NoError(configGenerate(cmd, fileHandler)) - readYAML, err := fileHandler.Read(constants.ConfigFilename) + var readConfig config.Config + err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig) assert.NoError(err) - assert.Equal(defaultConfigAsYAML(t), string(readYAML)) + assert.Equal(*config.Default(), readConfig) } func TestConfigGenerateDefaultExists(t *testing.T) { @@ -66,5 +61,8 @@ func TestConfigGenerateStdOut(t *testing.T) { require.NoError(configGenerate(cmd, fileHandler)) - assert.Equal(defaultConfigAsYAML(t), outBuffer.String()) + var readConfig config.Config + require.NoError(yaml.NewDecoder(&outBuffer).Decode(&readConfig)) + + assert.Equal(*config.Default(), readConfig) } diff --git a/cli/cmd/init.go b/cli/cmd/init.go index 81f434e0e..21030ebe9 100644 --- a/cli/cmd/init.go +++ b/cli/cmd/init.go @@ -460,7 +460,7 @@ func getGCPInstances(stat state.ConstellationState, config *config.Config) (coor // TODO: make min / max configurable and abstract autoscaling for different cloud providers nodes = ScalingGroup{ Instances: nodeInstances, - GroupID: gcp.AutoscalingNodeGroup(stat.GCPProject, stat.GCPZone, stat.GCPNodeInstanceGroup, *config.AutoscalingNodeGroupsMin, *config.AutoscalingNodeGroupsMax), + GroupID: gcp.AutoscalingNodeGroup(stat.GCPProject, stat.GCPZone, stat.GCPNodeInstanceGroup, config.AutoscalingNodeGroupsMin, config.AutoscalingNodeGroupsMax), } return @@ -493,7 +493,7 @@ func getAzureInstances(stat state.ConstellationState, config *config.Config) (co // TODO: make min / max configurable and abstract autoscaling for different cloud providers nodes = ScalingGroup{ Instances: nodeInstances, - GroupID: azure.AutoscalingNodeGroup(stat.AzureNodesScaleSet, *config.AutoscalingNodeGroupsMin, *config.AutoscalingNodeGroupsMax), + GroupID: azure.AutoscalingNodeGroup(stat.AzureNodesScaleSet, config.AutoscalingNodeGroupsMin, config.AutoscalingNodeGroupsMax), } return } diff --git a/cli/gcp/client/network.go b/cli/gcp/client/network.go index ab4c2f4f0..849c5e7cc 100644 --- a/cli/gcp/client/network.go +++ b/cli/gcp/client/network.go @@ -78,14 +78,8 @@ type FirewallInput struct { Egress cloudtypes.Firewall } -// VPCsInput defines the VPC configuration. -type VPCsInput struct { - SubnetCIDR string - SubnetExtCIDR string -} - // CreateVPCs creates all necessary VPC networks. -func (c *Client) CreateVPCs(ctx context.Context, input VPCsInput) error { +func (c *Client) CreateVPCs(ctx context.Context) error { c.network = c.name + "-" + c.uid op, err := c.createVPC(ctx, c.network) @@ -96,7 +90,7 @@ func (c *Client) CreateVPCs(ctx context.Context, input VPCsInput) error { return err } - if err := c.createSubnets(ctx, input.SubnetCIDR); err != nil { + if err := c.createSubnets(ctx); err != nil { return err } @@ -152,11 +146,11 @@ func (c *Client) terminateVPC(ctx context.Context, network string) (Operation, e return c.networksAPI.Delete(ctx, req) } -func (c *Client) createSubnets(ctx context.Context, subnetCIDR string) error { +func (c *Client) createSubnets(ctx context.Context) error { c.subnetwork = "node-net-" + c.uid c.secondarySubnetworkRange = "net-ext" + c.uid - op, err := c.createSubnet(ctx, c.subnetwork, subnetCIDR, c.network, c.secondarySubnetworkRange) + op, err := c.createSubnet(ctx, c.subnetwork, c.network, c.secondarySubnetworkRange) if err != nil { return err } @@ -164,12 +158,12 @@ func (c *Client) createSubnets(ctx context.Context, subnetCIDR string) error { return c.waitForOperations(ctx, []Operation{op}) } -func (c *Client) createSubnet(ctx context.Context, name, cidr, network, secondaryRangeName string) (Operation, error) { +func (c *Client) createSubnet(ctx context.Context, name, network, secondaryRangeName string) (Operation, error) { req := &computepb.InsertSubnetworkRequest{ Project: c.project, Region: c.region, SubnetworkResource: &computepb.Subnetwork{ - IpCidrRange: proto.String(cidr), + IpCidrRange: proto.String("192.168.178.0/24"), Name: proto.String(name), Network: proto.String("projects/" + c.project + "/global/networks/" + network), SecondaryIpRanges: []*computepb.SubnetworkSecondaryRange{ diff --git a/cli/gcp/client/network_test.go b/cli/gcp/client/network_test.go index 303704718..1c8200d4a 100644 --- a/cli/gcp/client/network_test.go +++ b/cli/gcp/client/network_test.go @@ -13,11 +13,6 @@ import ( func TestCreateVPCs(t *testing.T) { someErr := errors.New("failed") - testInput := VPCsInput{ - SubnetCIDR: "192.0.2.0/24", - SubnetExtCIDR: "198.51.100.0/24", - } - testCases := map[string]struct { operationGlobalAPI operationGlobalAPI operationRegionAPI operationRegionAPI @@ -80,9 +75,9 @@ func TestCreateVPCs(t *testing.T) { } if tc.wantErr { - assert.Error(client.CreateVPCs(ctx, testInput)) + assert.Error(client.CreateVPCs(ctx)) } else { - assert.NoError(client.CreateVPCs(ctx, testInput)) + assert.NoError(client.CreateVPCs(ctx)) assert.NotNil(client.network) } }) diff --git a/debugd/cdbg/state/state.go b/debugd/cdbg/state/state.go index d8e32d880..d614b78a8 100644 --- a/debugd/cdbg/state/state.go +++ b/debugd/cdbg/state/state.go @@ -85,7 +85,7 @@ func getGCPInstances(stat state.ConstellationState, config *configc.Config) (coo // TODO: make min / max configurable and abstract autoscaling for different cloud providers nodes = cmdc.ScalingGroup{ Instances: nodeInstances, - GroupID: gcp.AutoscalingNodeGroup(stat.GCPProject, stat.GCPZone, stat.GCPNodeInstanceGroup, *config.AutoscalingNodeGroupsMin, *config.AutoscalingNodeGroupsMax), + GroupID: gcp.AutoscalingNodeGroup(stat.GCPProject, stat.GCPZone, stat.GCPNodeInstanceGroup, config.AutoscalingNodeGroupsMin, config.AutoscalingNodeGroupsMax), } return diff --git a/go.mod b/go.mod index 18575033c..f2ade0c6a 100644 --- a/go.mod +++ b/go.mod @@ -77,6 +77,7 @@ require ( github.com/spf13/afero v1.8.2 github.com/spf13/cobra v1.4.0 github.com/stretchr/testify v1.7.1 + github.com/talos-systems/talos/pkg/machinery v1.0.4 github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 github.com/willdonnelly/passwd v0.0.0-20141013001024-7935dab3074c go.etcd.io/etcd/client/v3 v3.5.2 @@ -90,7 +91,7 @@ require ( google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e google.golang.org/grpc v1.45.0 google.golang.org/protobuf v1.27.1 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 k8s.io/api v0.24.0 k8s.io/apimachinery v0.24.0 k8s.io/cli-runtime v0.24.0 diff --git a/go.sum b/go.sum index 175015c7d..c0226c5c5 100644 --- a/go.sum +++ b/go.sum @@ -1402,6 +1402,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/talos-systems/talos/pkg/machinery v1.0.4 h1:zUZgIRSxAXOI6LygMDUqgS0rtFTf4DpDCL35UpW/6s4= +github.com/talos-systems/talos/pkg/machinery v1.0.4/go.mod h1:cJ/031WJGDnGQLW+zp+0lwkEn47orpJdfsJDf0BQVGM= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= @@ -2280,8 +2282,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 h1:dbuHpmKjkDzSOMKAWl10QNlgaZUd3V1q99xc81tt2Kc= +gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= diff --git a/internal/config/config.go b/internal/config/config.go index f134962ef..220a45aba 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,3 +1,6 @@ +//go:generate docgen ./config.go ./config_doc.go Configuration +// This binary can be build from siderolabs/talos projects. Located at: +// https://github.com/siderolabs/talos/tree/master/hack/docgen package config import ( @@ -5,267 +8,207 @@ import ( "fmt" "io/fs" - azureClient "github.com/edgelesssys/constellation/cli/azure/client" - "github.com/edgelesssys/constellation/cli/cloud/cloudtypes" - "github.com/edgelesssys/constellation/cli/ec2" - awsClient "github.com/edgelesssys/constellation/cli/ec2/client" - gcpClient "github.com/edgelesssys/constellation/cli/gcp/client" - "github.com/edgelesssys/constellation/coordinator/attestation/vtpm" "github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/deploy/ssh" "github.com/edgelesssys/constellation/internal/file" - "google.golang.org/protobuf/proto" ) -var ( - // gcpPCRs is a map of the expected PCR values for a GCP Constellation node. - // TODO: Get a full list once we have stable releases. - gcpPCRs = Measurements{ - 0: {0x0F, 0x35, 0xC2, 0x14, 0x60, 0x8D, 0x93, 0xC7, 0xA6, 0xE6, 0x8A, 0xE7, 0x35, 0x9B, 0x4A, 0x8B, 0xE5, 0xA0, 0xE9, 0x9E, 0xEA, 0x91, 0x07, 0xEC, 0xE4, 0x27, 0xC4, 0xDE, 0xA4, 0xE4, 0x39, 0xCF}, - uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - } - - // azurePCRs is a map of the expected PCR values for an Azure Constellation node. - // TODO: Get a full list once we have a working setup with stable releases. - azurePCRs = Measurements{ - uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - } - - qemuPCRs = Measurements{ - uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - } -) - -// Config defines a configuration used by the CLI. -// All fields in this struct and its child structs have pointer types -// to ensure the default values of the actual type is not confused with an omitted value. +// Config defines configuration used by CLI. type Config struct { - AutoscalingNodeGroupsMin *int `yaml:"autoscalingNodeGroupsMin,omitempty"` - AutoscalingNodeGroupsMax *int `yaml:"autoscalingNodeGroupsMax,omitempty"` - StateDiskSizeGB *int `yaml:"StateDisksizeGB,omitempty"` - Provider *ProviderConfig `yaml:"provider,omitempty"` - SSHUsers []*ssh.UserKey `yaml:"sshUsers,omitempty"` + // description: | + // Minimum number of nodes in autoscaling group. + // worker nodes. + AutoscalingNodeGroupsMin int `yaml:"autoscalingNodeGroupsMin"` + // description: | + // Maximum number of nodes in autoscaling group. + // worker nodes. + AutoscalingNodeGroupsMax int `yaml:"autoscalingNodeGroupsMax"` + // description: | + // Size (in GB) of data disk used for nodes. + StateDiskSizeGB int `yaml:"stateDisksizeGB"` + // description: | + // Ingress firewall rules for node network. + IngressFirewall Firewall `yaml:"ingressFirewall,omitempty"` + // description: | + // Egress firewall rules for node network. + EgressFirewall Firewall `yaml:"egressFirewall,omitempty"` + // description: | + // Supported cloud providers & their specific configurations. + Provider ProviderConfig `yaml:"provider"` + // description: | + // Create SSH users on Constellation nodes. + SSHUsers []*ssh.UserKey `yaml:"sshUsers,omitempty"` +} + +type FirewallRule struct { + // description: | + // Name of rule. + Name string `yaml:"name"` + // description: | + // Description for rule. + Description string `yaml:"description"` + // description: | + // Protocol, such as 'udp' or 'tcp'. + Protocol string `yaml:"protocol"` + // description: | + // CIDR range for which this rule is applied. + IPRange string `yaml:"iprange"` + // description: | + // Port of start port of a range. + FromPort int `yaml:"fromport"` + // description: | + // End port of a range, or 0 if a single port is given by FromPort. + ToPort int `yaml:"toport"` +} + +type Firewall []FirewallRule + +// ProviderConfig are cloud-provider specific configuration values used by the CLI. +// Fields should remain pointer-types so custom specific configs can nil them +// if not required. +type ProviderConfig struct { + // description: | + // Configuration for Azure as provider. + Azure *AzureConfig `yaml:"azureConfig,omitempty"` + // description: | + // Configuration for Google Cloud as provider. + GCP *GCPConfig `yaml:"gcpConfig,omitempty"` + // description: | + // Configuration for QEMU as provider. + QEMU *QEMUConfig `yaml:"qemuConfig,omitempty"` +} + +// AzureConfig are Azure specific configuration values used by the CLI. +type AzureConfig struct { + // description: | + // Subscription ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription + SubscriptionID string `yaml:"subscription"` + // description: | + // Tenant ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-ad-tenant + TenantID string `yaml:"tenant"` + // description: | + // Azure datacenter region to be used. See: https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#azure-regions-with-availability-zones + Location string `yaml:"location"` + // description: | + // Machine image used to create Constellation nodes. + Image string `yaml:"image"` + // description: | + // Expected confidential VM measurements. + Measurements Measurements `yaml:"measurements"` + // description: | + // Authorize spawned VMs to access Azure API. See: https://constellation-docs.edgeless.systems/6c320851-bdd2-41d5-bf10-e27427398692/#/getting-started/install?id=azure + UserAssignedIdentity string `yaml:"userassignedIdentity"` +} + +// GCPConfig are GCP specific configuration values used by the CLI. +type GCPConfig struct { + // description: | + // GCP project. See: https://support.google.com/googleapi/answer/7014113?hl=en + Project string `yaml:"project"` + // description: | + // GCP datacenter region. See: https://cloud.google.com/compute/docs/regions-zones#available + Region string `yaml:"region"` + // description: | + // GCP datacenter zone. See: https://cloud.google.com/compute/docs/regions-zones#available + Zone string `yaml:"zone"` + // description: | + // Machine image used to create Constellation nodes. + Image string `yaml:"image"` + // description: | + // Roles added to service account. + ServiceAccountRoles []string `yaml:"serviceAccountRoles"` + // description: | + // Measurement used to enable measured boot. + Measurements Measurements `yaml:"measurements"` +} + +type QEMUConfig struct { + // description: | + // Measurement used to enable measured boot. + Measurements Measurements `yaml:"measurements"` } // Default returns a struct with the default config. func Default() *Config { return &Config{ - AutoscalingNodeGroupsMin: intPtr(1), - AutoscalingNodeGroupsMax: intPtr(10), - StateDiskSizeGB: intPtr(30), - Provider: &ProviderConfig{ - EC2: &EC2Config{ - Image: proto.String("ami-07d3864beb84157d3"), - Tags: &[]ec2.Tag{ - { - Key: "responsible", - Value: "cli", - }, - { - Key: "Name", - Value: "Constellation", - }, - }, - SecurityGroupInput: &awsClient.SecurityGroupInput{ - Inbound: cloudtypes.Firewall{ - { - Description: "Coordinator default port", - Protocol: "TCP", - IPRange: "0.0.0.0/0", - FromPort: constants.CoordinatorPort, - }, - { - Description: "Enclave SSH", - Protocol: "TCP", - IPRange: "0.0.0.0/0", - FromPort: constants.EnclaveSSHPort, - }, - { - Description: "WireGuard default port", - Protocol: "UDP", - IPRange: "0.0.0.0/0", - FromPort: constants.WireguardPort, - }, - { - Description: "SSH", - Protocol: "TCP", - IPRange: "0.0.0.0/0", - FromPort: constants.SSHPort, - }, - { - Description: "NVMe over TCP", - Protocol: "TCP", - IPRange: "0.0.0.0/0", - FromPort: constants.NVMEOverTCPPort, - }, - { - Description: "NodePort", - Protocol: "TCP", - IPRange: "0.0.0.0/0", - FromPort: constants.NodePortFrom, - ToPort: constants.NodePortTo, - }, - }, - }, + AutoscalingNodeGroupsMin: 1, + AutoscalingNodeGroupsMax: 10, + StateDiskSizeGB: 30, + IngressFirewall: Firewall{ + { + Name: "coordinator", + Description: "Coordinator default port", + Protocol: "tcp", + IPRange: "0.0.0.0/0", + FromPort: constants.CoordinatorPort, }, + { + Name: "wireguard", + Description: "WireGuard default port", + Protocol: "udp", + IPRange: "0.0.0.0/0", + FromPort: constants.WireguardPort, + }, + { + Name: "ssh", + Description: "SSH", + Protocol: "tcp", + IPRange: "0.0.0.0/0", + FromPort: constants.SSHPort, + }, + { + Name: "nodeport", + Description: "NodePort", + Protocol: "tcp", + IPRange: "0.0.0.0/0", + FromPort: constants.NodePortFrom, + ToPort: constants.NodePortTo, + }, + }, + Provider: ProviderConfig{ Azure: &AzureConfig{ - SubscriptionID: proto.String("0d202bbb-4fa7-4af8-8125-58c269a05435"), - TenantID: proto.String("adb650a8-5da3-4b15-b4b0-3daf65ff7626"), - Location: proto.String("North Europe"), - Image: proto.String("/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/CONSTELLATION-IMAGES/providers/Microsoft.Compute/galleries/Constellation/images/constellation-coreos/versions/0.0.1651150807"), - NetworkSecurityGroupInput: &azureClient.NetworkSecurityGroupInput{ - Ingress: cloudtypes.Firewall{ - { - Name: "coordinator", - Description: "Coordinator default port", - Protocol: "tcp", - IPRange: "0.0.0.0/0", - FromPort: constants.CoordinatorPort, - }, - { - Name: "wireguard", - Description: "WireGuard default port", - Protocol: "udp", - IPRange: "0.0.0.0/0", - FromPort: constants.WireguardPort, - }, - { - Name: "ssh", - Description: "SSH", - Protocol: "tcp", - IPRange: "0.0.0.0/0", - FromPort: constants.SSHPort, - }, - { - Name: "nodeport", - Description: "NodePort", - Protocol: "tcp", - IPRange: "0.0.0.0/0", - FromPort: constants.NodePortFrom, - ToPort: constants.NodePortTo, - }, - }, - }, - Measurements: &azurePCRs, - UserAssignedIdentity: proto.String("/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.ManagedIdentity/userAssignedIdentities/constellation-dev-identity"), + SubscriptionID: "0d202bbb-4fa7-4af8-8125-58c269a05435", + TenantID: "adb650a8-5da3-4b15-b4b0-3daf65ff7626", + Location: "North Europe", + Image: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/CONSTELLATION-IMAGES/providers/Microsoft.Compute/galleries/Constellation/images/constellation-coreos/versions/0.0.1651150807", + Measurements: azurePCRs, + UserAssignedIdentity: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.ManagedIdentity/userAssignedIdentities/constellation-dev-identity", }, GCP: &GCPConfig{ - Project: proto.String("constellation-331613"), - Region: proto.String("europe-west3"), - Zone: proto.String("europe-west3-b"), - Image: proto.String("projects/constellation-images/global/images/constellation-coreos-1651150807"), - FirewallInput: &gcpClient.FirewallInput{ - Ingress: cloudtypes.Firewall{ - { - Name: "coordinator", - Description: "Coordinator default port", - Protocol: "tcp", - FromPort: constants.CoordinatorPort, - }, - { - Name: "wireguard", - Description: "WireGuard default port", - Protocol: "udp", - FromPort: constants.WireguardPort, - }, - { - Name: "ssh", - Description: "SSH", - Protocol: "tcp", - FromPort: constants.SSHPort, - }, - { - Name: "nodeport", - Description: "NodePort", - Protocol: "tcp", - FromPort: constants.NodePortFrom, - ToPort: constants.NodePortTo, - }, - }, - }, - VPCsInput: &gcpClient.VPCsInput{ - SubnetCIDR: "192.168.178.0/24", - SubnetExtCIDR: "10.10.0.0/16", - }, - ServiceAccountRoles: &[]string{ + Project: "constellation-331613", + Region: "europe-west3", + Zone: "europe-west3-b", + Image: "projects/constellation-images/global/images/constellation-coreos-1651150807", + ServiceAccountRoles: []string{ "roles/compute.instanceAdmin.v1", "roles/compute.networkAdmin", "roles/compute.securityAdmin", "roles/storage.admin", "roles/iam.serviceAccountUser", }, - Measurements: &gcpPCRs, + Measurements: gcpPCRs, }, QEMU: &QEMUConfig{ - Measurements: &qemuPCRs, + Measurements: qemuPCRs, }, }, } } -// FromFile returns a default config that has been merged with a config file. -// If name is empty, the defaults are returned. +// FromFile returns config file with `name` read from `fileHandler` by parsing +// it as YAML. +// If name is empty, the default configuration is returned. func FromFile(fileHandler file.Handler, name string) (*Config, error) { - conf := Default() if name == "" { - return conf, nil + return Default(), nil } - if err := fileHandler.ReadYAML(name, conf); err != nil { + var emptyConf Config + if err := fileHandler.ReadYAML(name, &emptyConf); err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, fmt.Errorf("unable to find %s - use `constellation config generate` to generate it first", name) } return nil, fmt.Errorf("could not load config from file %s: %w", name, err) } - return conf, nil -} - -// ProviderConfig are cloud-provider specific configuration values used by the CLI. -type ProviderConfig struct { - EC2 *EC2Config `yaml:"ec2Config,omitempty"` - Azure *AzureConfig `yaml:"azureConfig,omitempty"` - GCP *GCPConfig `yaml:"gcpConfig,omitempty"` - QEMU *QEMUConfig `yaml:"qemuConfig,omitempty"` -} - -// EC2Config are AWS EC2 specific configuration values used by the CLI. -type EC2Config struct { - Image *string `yaml:"image,omitempty"` - Tags *[]ec2.Tag `yaml:"tags,omitempty"` - SecurityGroupInput *awsClient.SecurityGroupInput `yaml:"securityGroupInput,omitempty"` -} - -// AzureConfig are Azure specific configuration values used by the CLI. -type AzureConfig struct { - SubscriptionID *string `yaml:"subscription,omitempty"` // TODO: This will be user input - TenantID *string `yaml:"tenant,omitempty"` // TODO: This will be user input - Location *string `yaml:"location,omitempty"` // TODO: This will be user input - Image *string `yaml:"image,omitempty"` - NetworkSecurityGroupInput *azureClient.NetworkSecurityGroupInput `yaml:"networkSecurityGroupInput,omitempty"` - Measurements *Measurements `yaml:"measurements,omitempty"` - UserAssignedIdentity *string `yaml:"userassignedIdentity,omitempty"` -} - -// GCPConfig are GCP specific configuration values used by the CLI. -type GCPConfig struct { - Project *string `yaml:"project,omitempty"` // TODO: This will be user input - Region *string `yaml:"region,omitempty"` // TODO: This will be user input - Zone *string `yaml:"zone,omitempty"` // TODO: This will be user input - Image *string `yaml:"image,omitempty"` - FirewallInput *gcpClient.FirewallInput `yaml:"firewallInput,omitempty"` - VPCsInput *gcpClient.VPCsInput `yaml:"vpcsInput,omitempty"` - ServiceAccountRoles *[]string `yaml:"serviceAccountRoles,omitempty"` - Measurements *Measurements `yaml:"measurements,omitempty"` -} - -type QEMUConfig struct { - Measurements *Measurements `yaml:"measurements,omitempty"` -} - -// intPtr returns a pointer to the copied value of in. -func intPtr(in int) *int { - return &in + return &emptyConf, nil } diff --git a/internal/config/config_doc.go b/internal/config/config_doc.go new file mode 100644 index 000000000..8b8b50095 --- /dev/null +++ b/internal/config/config_doc.go @@ -0,0 +1,220 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Code generated by hack/docgen tool. DO NOT EDIT. + +package config + +import ( + "github.com/talos-systems/talos/pkg/machinery/config/encoder" +) + +var ( + ConfigDoc encoder.Doc + ProviderConfigDoc encoder.Doc + AzureConfigDoc encoder.Doc + GCPConfigDoc encoder.Doc + QEMUConfigDoc encoder.Doc +) + +func init() { + ConfigDoc.Type = "Config" + ConfigDoc.Comments[encoder.LineComment] = "Config defines configuration used by CLI." + ConfigDoc.Description = "Config defines configuration used by CLI." + ConfigDoc.Fields = make([]encoder.Doc, 4) + ConfigDoc.Fields[0].Name = "autoscalingNodeGroupsMin" + ConfigDoc.Fields[0].Type = "int" + ConfigDoc.Fields[0].Note = "" + ConfigDoc.Fields[0].Description = "Minimum number of nodes in autoscaling group.\nworker nodes." + ConfigDoc.Fields[0].Comments[encoder.LineComment] = "Minimum number of nodes in autoscaling group." + ConfigDoc.Fields[1].Name = "autoscalingNodeGroupsMax" + ConfigDoc.Fields[1].Type = "int" + ConfigDoc.Fields[1].Note = "" + ConfigDoc.Fields[1].Description = "Maximum number of nodes in autoscaling group.\nworker nodes." + ConfigDoc.Fields[1].Comments[encoder.LineComment] = "Maximum number of nodes in autoscaling group." + ConfigDoc.Fields[2].Name = "StateDisksizeGB" + ConfigDoc.Fields[2].Type = "int" + ConfigDoc.Fields[2].Note = "" + ConfigDoc.Fields[2].Description = "Size (in GB) of root disk used for nodes." + ConfigDoc.Fields[2].Comments[encoder.LineComment] = "Size (in GB) of root disk used for nodes." + ConfigDoc.Fields[3].Name = "provider" + ConfigDoc.Fields[3].Type = "ProviderConfig" + ConfigDoc.Fields[3].Note = "" + ConfigDoc.Fields[3].Description = "Supported cloud providers & their specific configurations." + ConfigDoc.Fields[3].Comments[encoder.LineComment] = "Supported cloud providers & their specific configurations." + + ProviderConfigDoc.Type = "ProviderConfig" + ProviderConfigDoc.Comments[encoder.LineComment] = "ProviderConfig are cloud-provider specific configuration values used by the CLI." + ProviderConfigDoc.Description = "ProviderConfig are cloud-provider specific configuration values used by the CLI." + ProviderConfigDoc.AppearsIn = []encoder.Appearance{ + { + TypeName: "Config", + FieldName: "provider", + }, + } + ProviderConfigDoc.Fields = make([]encoder.Doc, 3) + ProviderConfigDoc.Fields[0].Name = "azureConfig" + ProviderConfigDoc.Fields[0].Type = "AzureConfig" + ProviderConfigDoc.Fields[0].Note = "" + ProviderConfigDoc.Fields[0].Description = "Configuration for Azure as provider." + ProviderConfigDoc.Fields[0].Comments[encoder.LineComment] = "Configuration for Azure as provider." + ProviderConfigDoc.Fields[1].Name = "gcpConfig" + ProviderConfigDoc.Fields[1].Type = "GCPConfig" + ProviderConfigDoc.Fields[1].Note = "" + ProviderConfigDoc.Fields[1].Description = "Configuration for Google Cloud as provider." + ProviderConfigDoc.Fields[1].Comments[encoder.LineComment] = "Configuration for Google Cloud as provider." + ProviderConfigDoc.Fields[2].Name = "qemuConfig" + ProviderConfigDoc.Fields[2].Type = "QEMUConfig" + ProviderConfigDoc.Fields[2].Note = "" + ProviderConfigDoc.Fields[2].Description = "Configuration for QEMU as provider." + ProviderConfigDoc.Fields[2].Comments[encoder.LineComment] = "Configuration for QEMU as provider." + + AzureConfigDoc.Type = "AzureConfig" + AzureConfigDoc.Comments[encoder.LineComment] = "AzureConfig are Azure specific configuration values used by the CLI." + AzureConfigDoc.Description = "AzureConfig are Azure specific configuration values used by the CLI." + AzureConfigDoc.AppearsIn = []encoder.Appearance{ + { + TypeName: "ProviderConfig", + FieldName: "azureConfig", + }, + } + AzureConfigDoc.Fields = make([]encoder.Doc, 7) + AzureConfigDoc.Fields[0].Name = "subscription" + AzureConfigDoc.Fields[0].Type = "string" + AzureConfigDoc.Fields[0].Note = "" + AzureConfigDoc.Fields[0].Description = "Subscription ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription" + AzureConfigDoc.Fields[0].Comments[encoder.LineComment] = "Subscription ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription" + AzureConfigDoc.Fields[1].Name = "tenant" + AzureConfigDoc.Fields[1].Type = "string" + AzureConfigDoc.Fields[1].Note = "" + AzureConfigDoc.Fields[1].Description = "Tenant ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-ad-tenant" + AzureConfigDoc.Fields[1].Comments[encoder.LineComment] = "Tenant ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-ad-tenant" + AzureConfigDoc.Fields[2].Name = "location" + AzureConfigDoc.Fields[2].Type = "string" + AzureConfigDoc.Fields[2].Note = "" + AzureConfigDoc.Fields[2].Description = "Azure datacenter region to be used. See: https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#azure-regions-with-availability-zones" + AzureConfigDoc.Fields[2].Comments[encoder.LineComment] = "Azure datacenter region to be used. See: https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#azure-regions-with-availability-zones" + AzureConfigDoc.Fields[3].Name = "image" + AzureConfigDoc.Fields[3].Type = "string" + AzureConfigDoc.Fields[3].Note = "" + AzureConfigDoc.Fields[3].Description = "Machine image used to create Constellation nodes." + AzureConfigDoc.Fields[3].Comments[encoder.LineComment] = "Machine image used to create Constellation nodes." + AzureConfigDoc.Fields[4].Name = "networkSecurityGroupInput" + AzureConfigDoc.Fields[4].Type = "NetworkSecurityGroupInput" + AzureConfigDoc.Fields[4].Note = "" + AzureConfigDoc.Fields[4].Description = "Firewall rules." + AzureConfigDoc.Fields[4].Comments[encoder.LineComment] = "Firewall rules." + AzureConfigDoc.Fields[5].Name = "measurements" + AzureConfigDoc.Fields[5].Type = "Measurements" + AzureConfigDoc.Fields[5].Note = "" + AzureConfigDoc.Fields[5].Description = "Measurement used to enable measured boot." + AzureConfigDoc.Fields[5].Comments[encoder.LineComment] = "Measurement used to enable measured boot." + AzureConfigDoc.Fields[6].Name = "userassignedIdentity" + AzureConfigDoc.Fields[6].Type = "string" + AzureConfigDoc.Fields[6].Note = "" + AzureConfigDoc.Fields[6].Description = "Why is this needed? Docs only say that it is needed. (TODO) See: https://constellation-docs.edgeless.systems/6c320851-bdd2-41d5-bf10-e27427398692/#/getting-started/install?id=azure" + AzureConfigDoc.Fields[6].Comments[encoder.LineComment] = "Why is this needed? Docs only say that it is needed. (TODO) See: https://constellation-docs.edgeless.systems/6c320851-bdd2-41d5-bf10-e27427398692/#/getting-started/install?id=azure" + + GCPConfigDoc.Type = "GCPConfig" + GCPConfigDoc.Comments[encoder.LineComment] = "GCPConfig are GCP specific configuration values used by the CLI." + GCPConfigDoc.Description = "GCPConfig are GCP specific configuration values used by the CLI." + GCPConfigDoc.AppearsIn = []encoder.Appearance{ + { + TypeName: "ProviderConfig", + FieldName: "gcpConfig", + }, + } + GCPConfigDoc.Fields = make([]encoder.Doc, 8) + GCPConfigDoc.Fields[0].Name = "project" + GCPConfigDoc.Fields[0].Type = "string" + GCPConfigDoc.Fields[0].Note = "" + GCPConfigDoc.Fields[0].Description = "GCP project. See: https://support.google.com/googleapi/answer/7014113?hl=en" + GCPConfigDoc.Fields[0].Comments[encoder.LineComment] = "GCP project. See: https://support.google.com/googleapi/answer/7014113?hl=en" + GCPConfigDoc.Fields[1].Name = "region" + GCPConfigDoc.Fields[1].Type = "string" + GCPConfigDoc.Fields[1].Note = "" + GCPConfigDoc.Fields[1].Description = "GCP datacenter region. See: https://cloud.google.com/compute/docs/regions-zones#available" + GCPConfigDoc.Fields[1].Comments[encoder.LineComment] = "GCP datacenter region. See: https://cloud.google.com/compute/docs/regions-zones#available" + GCPConfigDoc.Fields[2].Name = "zone" + GCPConfigDoc.Fields[2].Type = "string" + GCPConfigDoc.Fields[2].Note = "" + GCPConfigDoc.Fields[2].Description = "GCP datacenter zone. See: https://cloud.google.com/compute/docs/regions-zones#available" + GCPConfigDoc.Fields[2].Comments[encoder.LineComment] = "GCP datacenter zone. See: https://cloud.google.com/compute/docs/regions-zones#available" + GCPConfigDoc.Fields[3].Name = "image" + GCPConfigDoc.Fields[3].Type = "string" + GCPConfigDoc.Fields[3].Note = "" + GCPConfigDoc.Fields[3].Description = "Machine image used to create Constellation nodes." + GCPConfigDoc.Fields[3].Comments[encoder.LineComment] = "Machine image used to create Constellation nodes." + GCPConfigDoc.Fields[4].Name = "firewallInput" + GCPConfigDoc.Fields[4].Type = "FirewallInput" + GCPConfigDoc.Fields[4].Note = "" + GCPConfigDoc.Fields[4].Description = "Firewall rules." + GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Firewall rules." + GCPConfigDoc.Fields[5].Name = "vpcsInput" + GCPConfigDoc.Fields[5].Type = "VPCsInput" + GCPConfigDoc.Fields[5].Note = "" + GCPConfigDoc.Fields[5].Description = "Virtual Private Cloud settings." + GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Virtual Private Cloud settings." + GCPConfigDoc.Fields[6].Name = "serviceAccountRoles" + GCPConfigDoc.Fields[6].Type = "[]string" + GCPConfigDoc.Fields[6].Note = "" + GCPConfigDoc.Fields[6].Description = "Roles added to service account." + GCPConfigDoc.Fields[6].Comments[encoder.LineComment] = "Roles added to service account." + GCPConfigDoc.Fields[7].Name = "measurements" + GCPConfigDoc.Fields[7].Type = "Measurements" + GCPConfigDoc.Fields[7].Note = "" + GCPConfigDoc.Fields[7].Description = "Measurement used to enable measured boot." + GCPConfigDoc.Fields[7].Comments[encoder.LineComment] = "Measurement used to enable measured boot." + + QEMUConfigDoc.Type = "QEMUConfig" + QEMUConfigDoc.Comments[encoder.LineComment] = "" + QEMUConfigDoc.Description = "" + QEMUConfigDoc.AppearsIn = []encoder.Appearance{ + { + TypeName: "ProviderConfig", + FieldName: "qemuConfig", + }, + } + QEMUConfigDoc.Fields = make([]encoder.Doc, 1) + QEMUConfigDoc.Fields[0].Name = "measurements" + QEMUConfigDoc.Fields[0].Type = "Measurements" + QEMUConfigDoc.Fields[0].Note = "" + QEMUConfigDoc.Fields[0].Description = "Measurement used to enable measured boot." + QEMUConfigDoc.Fields[0].Comments[encoder.LineComment] = "Measurement used to enable measured boot." +} + +func (_ Config) Doc() *encoder.Doc { + return &ConfigDoc +} + +func (_ ProviderConfig) Doc() *encoder.Doc { + return &ProviderConfigDoc +} + +func (_ AzureConfig) Doc() *encoder.Doc { + return &AzureConfigDoc +} + +func (_ GCPConfig) Doc() *encoder.Doc { + return &GCPConfigDoc +} + +func (_ QEMUConfig) Doc() *encoder.Doc { + return &QEMUConfigDoc +} + +// GetConfigurationDoc returns documentation for the file ./config_doc.go. +func GetConfigurationDoc() *encoder.FileDoc { + return &encoder.FileDoc{ + Name: "Configuration", + Description: "", + Structs: []*encoder.Doc{ + &ConfigDoc, + &ProviderConfigDoc, + &AzureConfigDoc, + &GCPConfigDoc, + &QEMUConfigDoc, + }, + } +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index c18beab22..fd3684b98 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -3,8 +3,6 @@ package config import ( "testing" - "github.com/edgelesssys/constellation/cli/cloud/cloudtypes" - "github.com/edgelesssys/constellation/cli/gcp/client" "github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/file" "github.com/spf13/afero" @@ -19,48 +17,57 @@ func TestDefaultConfig(t *testing.T) { } func TestFromFile(t *testing.T) { - someProviderConfig := &ProviderConfig{ - GCP: &GCPConfig{ - FirewallInput: &client.FirewallInput{ - Ingress: cloudtypes.Firewall{ - { - Name: "firstFirewallRule", - Description: "firstFirewallRule description", - Protocol: "tcp", - FromPort: 4444, - }, - { - Name: "secondFirewallRule", - Description: "secondFirewallRule description", - Protocol: "udp", - FromPort: 5555, - }, - }, + testCases := map[string]struct { + config *Config + configName string + wantResult *Config + wantErr bool + }{ + "default config from default file": { + config: Default(), + configName: constants.ConfigFilename, + wantResult: Default(), + }, + "default config from different path": { + config: Default(), + configName: "other-config.yaml", + wantResult: Default(), + }, + "default config when path empty": { + config: nil, + configName: "", + wantResult: Default(), + }, + "err when path not exist": { + config: nil, + configName: "wrong-name.yaml", + wantErr: true, + }, + "custom config from default file": { + config: &Config{ + AutoscalingNodeGroupsMin: 42, + AutoscalingNodeGroupsMax: 1337, + }, + configName: constants.ConfigFilename, + wantResult: &Config{ + AutoscalingNodeGroupsMin: 42, + AutoscalingNodeGroupsMax: 1337, }, }, - } - - testCases := map[string]struct { - from *Config - configName string - wantResultMutator func(c *Config) // mutates the Default() config to the expected result. - wantErr bool - }{ - "overwrite slices": { - from: &Config{Provider: someProviderConfig}, - configName: constants.ConfigFilename, - wantResultMutator: func(c *Config) { c.Provider = someProviderConfig }, - }, - "default with empty name": { - from: &Config{}, - configName: "", - wantResultMutator: func(c *Config) {}, - }, - "err with wrong name": { - from: &Config{}, - configName: "wrongName.json", - wantResultMutator: func(c *Config) {}, - wantErr: true, + "modify default config": { + config: func() *Config { + conf := Default() + conf.Provider.GCP.Region = "eu-north1" + conf.Provider.GCP.Zone = "eu-north1-a" + return conf + }(), + configName: constants.ConfigFilename, + wantResult: func() *Config { + conf := Default() + conf.Provider.GCP.Region = "eu-north1" + conf.Provider.GCP.Zone = "eu-north1-a" + return conf + }(), }, } @@ -70,7 +77,9 @@ func TestFromFile(t *testing.T) { require := require.New(t) fileHandler := file.NewHandler(afero.NewMemMapFs()) - require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, tc.from, file.OptNone)) + if tc.config != nil { + require.NoError(fileHandler.WriteYAML(tc.configName, tc.config, file.OptNone)) + } result, err := FromFile(fileHandler, tc.configName) @@ -78,14 +87,7 @@ func TestFromFile(t *testing.T) { assert.Error(err) } else { require.NoError(err) - wantResult := Default() - tc.wantResultMutator(wantResult) - assert.EqualValues(wantResult.AutoscalingNodeGroupsMin, result.AutoscalingNodeGroupsMin) - assert.EqualValues(wantResult.AutoscalingNodeGroupsMax, result.AutoscalingNodeGroupsMax) - require.NotNil(wantResult.Provider) - require.NotNil(wantResult.Provider.GCP, result.Provider.GCP) - require.NotNil(wantResult.Provider.GCP.FirewallInput, result.Provider.GCP.FirewallInput) - assert.Equal(len(wantResult.Provider.GCP.FirewallInput.Ingress), len(result.Provider.GCP.FirewallInput.Ingress)) + assert.Equal(tc.wantResult, result) } }) } diff --git a/internal/config/measurements.go b/internal/config/measurements.go index 2663f7ef7..e1c8a99eb 100644 --- a/internal/config/measurements.go +++ b/internal/config/measurements.go @@ -1,9 +1,35 @@ package config -import "encoding/base64" +import ( + "encoding/base64" + + "github.com/edgelesssys/constellation/coordinator/attestation/vtpm" +) type Measurements map[uint32][]byte +var ( + // gcpPCRs is a map of the expected PCR values for a GCP Constellation node. + // TODO: Get a full list once we have stable releases. + gcpPCRs = Measurements{ + 0: {0x0F, 0x35, 0xC2, 0x14, 0x60, 0x8D, 0x93, 0xC7, 0xA6, 0xE6, 0x8A, 0xE7, 0x35, 0x9B, 0x4A, 0x8B, 0xE5, 0xA0, 0xE9, 0x9E, 0xEA, 0x91, 0x07, 0xEC, 0xE4, 0x27, 0xC4, 0xDE, 0xA4, 0xE4, 0x39, 0xCF}, + uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + } + + // azurePCRs is a map of the expected PCR values for an Azure Constellation node. + // TODO: Get a full list once we have a working setup with stable releases. + azurePCRs = Measurements{ + uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + } + + qemuPCRs = Measurements{ + uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + } +) + func (m Measurements) MarshalYAML() (interface{}, error) { base64Map := make(map[uint32]string) diff --git a/internal/file/file.go b/internal/file/file.go index 4e222f9e3..68ed3457d 100644 --- a/internal/file/file.go +++ b/internal/file/file.go @@ -13,6 +13,7 @@ import ( "path" "github.com/spf13/afero" + "github.com/talos-systems/talos/pkg/machinery/config/encoder" "gopkg.in/yaml.v3" ) @@ -112,7 +113,7 @@ func (h *Handler) WriteYAML(name string, content any, options Option) (err error err = errors.New("recovered from panic") } }() - data, err := yaml.Marshal(content) + data, err := encoder.NewEncoder(content).Encode() if err != nil { return err }