AB#2061 Self Documenting Config File (#143)

Move firewall up into root config, remove VPC config & autogenerate comments in config file.
This commit is contained in:
Fabian Kammel 2022-05-16 18:54:25 +02:00 committed by GitHub
parent cdfd962fcc
commit b905c28515
20 changed files with 538 additions and 348 deletions

View File

@ -11,7 +11,7 @@ import (
type gcpclient interface { type gcpclient interface {
GetState() (state.ConstellationState, error) GetState() (state.ConstellationState, error)
SetState(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 CreateFirewall(ctx context.Context, input gcpcl.FirewallInput) error
CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error
CreateServiceAccount(ctx context.Context, input gcpcl.ServiceAccountInput) (string, error) CreateServiceAccount(ctx context.Context, input gcpcl.ServiceAccountInput) (string, error)

View File

@ -261,7 +261,7 @@ func (c *fakeGcpClient) SetState(stat state.ConstellationState) error {
return nil 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.network = "network"
c.subnetwork = "subnetwork" c.subnetwork = "subnetwork"
return nil return nil
@ -377,7 +377,7 @@ func (c *stubGcpClient) SetState(state.ConstellationState) error {
return c.setStateErr 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 return c.createVPCsErr
} }

View File

@ -6,6 +6,7 @@ import (
"io" "io"
azurecl "github.com/edgelesssys/constellation/cli/azure/client" 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/cloudprovider"
"github.com/edgelesssys/constellation/cli/gcp" "github.com/edgelesssys/constellation/cli/gcp"
"github.com/edgelesssys/constellation/cli/gcp/client" "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: case cloudprovider.GCP:
cl, err := c.newGCPClient( cl, err := c.newGCPClient(
ctx, ctx,
*config.Provider.GCP.Project, config.Provider.GCP.Project,
*config.Provider.GCP.Zone, config.Provider.GCP.Zone,
*config.Provider.GCP.Region, config.Provider.GCP.Region,
name, name,
) )
if err != nil { 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) return c.createGCP(ctx, cl, config, insType, coordCount, nodeCount)
case cloudprovider.Azure: case cloudprovider.Azure:
cl, err := c.newAzureClient( cl, err := c.newAzureClient(
*config.Provider.Azure.SubscriptionID, config.Provider.Azure.SubscriptionID,
*config.Provider.Azure.TenantID, config.Provider.Azure.TenantID,
name, name,
*config.Provider.Azure.Location, config.Provider.Azure.Location,
) )
if err != nil { if err != nil {
return state.ConstellationState{}, err 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) { ) (stat state.ConstellationState, retErr error) {
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerGCP{client: cl}) 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 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 return state.ConstellationState{}, err
} }
createInput := client.CreateInstancesInput{ createInput := client.CreateInstancesInput{
CountCoordinators: coordCount, CountCoordinators: coordCount,
CountNodes: nodeCount, CountNodes: nodeCount,
ImageId: *config.Provider.GCP.Image, ImageId: config.Provider.GCP.Image,
InstanceType: insType, InstanceType: insType,
StateDiskSizeGB: *config.StateDiskSizeGB, StateDiskSizeGB: config.StateDiskSizeGB,
KubeEnv: gcp.KubeEnv, KubeEnv: gcp.KubeEnv,
} }
if err := cl.CreateInstances(ctx, createInput); err != nil { 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 { if err := cl.CreateVirtualNetwork(ctx); err != nil {
return state.ConstellationState{}, err 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 return state.ConstellationState{}, err
} }
createInput := azurecl.CreateInstancesInput{ createInput := azurecl.CreateInstancesInput{
CountCoordinators: coordCount, CountCoordinators: coordCount,
CountNodes: nodeCount, CountNodes: nodeCount,
InstanceType: insType, InstanceType: insType,
StateDiskSizeGB: *config.StateDiskSizeGB, StateDiskSizeGB: config.StateDiskSizeGB,
Image: *config.Provider.Azure.Image, Image: config.Provider.Azure.Image,
UserAssingedIdentity: *config.Provider.Azure.UserAssignedIdentity, UserAssingedIdentity: config.Provider.Azure.UserAssignedIdentity,
} }
if err := cl.CreateInstances(ctx, createInput); err != nil { if err := cl.CreateInstances(ctx, createInput); err != nil {
return state.ConstellationState{}, err return state.ConstellationState{}, err

View File

@ -73,7 +73,7 @@ func (c *ServiceAccountCreator) createServiceAccountGCP(ctx context.Context, cl
} }
input := gcpcl.ServiceAccountInput{ input := gcpcl.ServiceAccountInput{
Roles: *config.Provider.GCP.ServiceAccountRoles, Roles: config.Provider.GCP.ServiceAccountRoles,
} }
serviceAccount, err := cl.CreateServiceAccount(ctx, input) serviceAccount, err := cl.CreateServiceAccount(ctx, input)
if err != nil { if err != nil {

View File

@ -68,19 +68,19 @@ func (v *Validators) updatePCR(pcrIndex uint32, encoded string) error {
func (v *Validators) setPCRs(config *config.Config) error { func (v *Validators) setPCRs(config *config.Config) error {
switch v.provider { switch v.provider {
case cloudprovider.GCP: case cloudprovider.GCP:
gcpPCRs := *config.Provider.GCP.Measurements gcpPCRs := config.Provider.GCP.Measurements
if err := v.checkPCRs(gcpPCRs); err != nil { if err := v.checkPCRs(gcpPCRs); err != nil {
return err return err
} }
v.pcrs = gcpPCRs v.pcrs = gcpPCRs
case cloudprovider.Azure: case cloudprovider.Azure:
azurePCRs := *config.Provider.Azure.Measurements azurePCRs := config.Provider.Azure.Measurements
if err := v.checkPCRs(azurePCRs); err != nil { if err := v.checkPCRs(azurePCRs); err != nil {
return err return err
} }
v.pcrs = azurePCRs v.pcrs = azurePCRs
case cloudprovider.QEMU: case cloudprovider.QEMU:
qemuPCRs := *config.Provider.QEMU.Measurements qemuPCRs := config.Provider.QEMU.Measurements
if err := v.checkPCRs(qemuPCRs); err != nil { if err := v.checkPCRs(qemuPCRs); err != nil {
return err return err
} }

View File

@ -66,18 +66,18 @@ func TestNewValidators(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
conf := &config.Config{Provider: &config.ProviderConfig{}} conf := &config.Config{Provider: config.ProviderConfig{}}
if tc.provider == cloudprovider.GCP { if tc.provider == cloudprovider.GCP {
measurements := config.Measurements(tc.pcrs) measurements := config.Measurements(tc.pcrs)
conf.Provider.GCP = &config.GCPConfig{Measurements: &measurements} conf.Provider.GCP = &config.GCPConfig{Measurements: measurements}
} }
if tc.provider == cloudprovider.Azure { if tc.provider == cloudprovider.Azure {
measurements := config.Measurements(tc.pcrs) measurements := config.Measurements(tc.pcrs)
conf.Provider.Azure = &config.AzureConfig{Measurements: &measurements} conf.Provider.Azure = &config.AzureConfig{Measurements: measurements}
} }
if tc.provider == cloudprovider.QEMU { if tc.provider == cloudprovider.QEMU {
measurements := config.Measurements(tc.pcrs) 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) validators, err := NewValidators(tc.provider, conf)

View File

@ -6,20 +6,14 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" 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" computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
type FirewallRule struct { type FirewallRule = config.FirewallRule
Name string
Description string
Protocol string
IPRange string
FromPort int
ToPort int
}
type Firewall []FirewallRule type Firewall config.Firewall
func (f Firewall) GCP() ([]*computepb.Firewall, error) { func (f Firewall) GCP() ([]*computepb.Firewall, error) {
var fw []*computepb.Firewall var fw []*computepb.Firewall

View File

@ -6,7 +6,7 @@ import (
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v3" "github.com/talos-systems/talos/pkg/machinery/config/encoder"
) )
func newConfigGenerateCmd() *cobra.Command { func newConfigGenerateCmd() *cobra.Command {
@ -38,7 +38,12 @@ func configGenerate(cmd *cobra.Command, fileHandler file.Handler) error {
} }
if flags.file == "-" { 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) return fileHandler.WriteYAML(flags.file, config.Default(), 0o644)

View File

@ -13,12 +13,6 @@ import (
"gopkg.in/yaml.v3" "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) { func TestConfigGenerateDefault(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
@ -28,9 +22,10 @@ func TestConfigGenerateDefault(t *testing.T) {
require.NoError(configGenerate(cmd, fileHandler)) 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.NoError(err)
assert.Equal(defaultConfigAsYAML(t), string(readYAML)) assert.Equal(*config.Default(), readConfig)
} }
func TestConfigGenerateDefaultExists(t *testing.T) { func TestConfigGenerateDefaultExists(t *testing.T) {
@ -66,5 +61,8 @@ func TestConfigGenerateStdOut(t *testing.T) {
require.NoError(configGenerate(cmd, fileHandler)) 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)
} }

View File

@ -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 // TODO: make min / max configurable and abstract autoscaling for different cloud providers
nodes = ScalingGroup{ nodes = ScalingGroup{
Instances: nodeInstances, 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 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 // TODO: make min / max configurable and abstract autoscaling for different cloud providers
nodes = ScalingGroup{ nodes = ScalingGroup{
Instances: nodeInstances, Instances: nodeInstances,
GroupID: azure.AutoscalingNodeGroup(stat.AzureNodesScaleSet, *config.AutoscalingNodeGroupsMin, *config.AutoscalingNodeGroupsMax), GroupID: azure.AutoscalingNodeGroup(stat.AzureNodesScaleSet, config.AutoscalingNodeGroupsMin, config.AutoscalingNodeGroupsMax),
} }
return return
} }

View File

@ -78,14 +78,8 @@ type FirewallInput struct {
Egress cloudtypes.Firewall Egress cloudtypes.Firewall
} }
// VPCsInput defines the VPC configuration.
type VPCsInput struct {
SubnetCIDR string
SubnetExtCIDR string
}
// CreateVPCs creates all necessary VPC networks. // 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 c.network = c.name + "-" + c.uid
op, err := c.createVPC(ctx, c.network) op, err := c.createVPC(ctx, c.network)
@ -96,7 +90,7 @@ func (c *Client) CreateVPCs(ctx context.Context, input VPCsInput) error {
return err return err
} }
if err := c.createSubnets(ctx, input.SubnetCIDR); err != nil { if err := c.createSubnets(ctx); err != nil {
return err return err
} }
@ -152,11 +146,11 @@ func (c *Client) terminateVPC(ctx context.Context, network string) (Operation, e
return c.networksAPI.Delete(ctx, req) 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.subnetwork = "node-net-" + c.uid
c.secondarySubnetworkRange = "net-ext" + 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 { if err != nil {
return err return err
} }
@ -164,12 +158,12 @@ func (c *Client) createSubnets(ctx context.Context, subnetCIDR string) error {
return c.waitForOperations(ctx, []Operation{op}) 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{ req := &computepb.InsertSubnetworkRequest{
Project: c.project, Project: c.project,
Region: c.region, Region: c.region,
SubnetworkResource: &computepb.Subnetwork{ SubnetworkResource: &computepb.Subnetwork{
IpCidrRange: proto.String(cidr), IpCidrRange: proto.String("192.168.178.0/24"),
Name: proto.String(name), Name: proto.String(name),
Network: proto.String("projects/" + c.project + "/global/networks/" + network), Network: proto.String("projects/" + c.project + "/global/networks/" + network),
SecondaryIpRanges: []*computepb.SubnetworkSecondaryRange{ SecondaryIpRanges: []*computepb.SubnetworkSecondaryRange{

View File

@ -13,11 +13,6 @@ import (
func TestCreateVPCs(t *testing.T) { func TestCreateVPCs(t *testing.T) {
someErr := errors.New("failed") someErr := errors.New("failed")
testInput := VPCsInput{
SubnetCIDR: "192.0.2.0/24",
SubnetExtCIDR: "198.51.100.0/24",
}
testCases := map[string]struct { testCases := map[string]struct {
operationGlobalAPI operationGlobalAPI operationGlobalAPI operationGlobalAPI
operationRegionAPI operationRegionAPI operationRegionAPI operationRegionAPI
@ -80,9 +75,9 @@ func TestCreateVPCs(t *testing.T) {
} }
if tc.wantErr { if tc.wantErr {
assert.Error(client.CreateVPCs(ctx, testInput)) assert.Error(client.CreateVPCs(ctx))
} else { } else {
assert.NoError(client.CreateVPCs(ctx, testInput)) assert.NoError(client.CreateVPCs(ctx))
assert.NotNil(client.network) assert.NotNil(client.network)
} }
}) })

View File

@ -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 // TODO: make min / max configurable and abstract autoscaling for different cloud providers
nodes = cmdc.ScalingGroup{ nodes = cmdc.ScalingGroup{
Instances: nodeInstances, 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 return

3
go.mod
View File

@ -77,6 +77,7 @@ require (
github.com/spf13/afero v1.8.2 github.com/spf13/afero v1.8.2
github.com/spf13/cobra v1.4.0 github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.7.1 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/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
github.com/willdonnelly/passwd v0.0.0-20141013001024-7935dab3074c github.com/willdonnelly/passwd v0.0.0-20141013001024-7935dab3074c
go.etcd.io/etcd/client/v3 v3.5.2 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/genproto v0.0.0-20220317150908-0efb43f6373e
google.golang.org/grpc v1.45.0 google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.27.1 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/api v0.24.0
k8s.io/apimachinery v0.24.0 k8s.io/apimachinery v0.24.0
k8s.io/cli-runtime v0.24.0 k8s.io/cli-runtime v0.24.0

5
go.sum
View File

@ -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-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-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/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/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/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= 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.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-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-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-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 h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=

View File

@ -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 package config
import ( import (
@ -5,267 +8,207 @@ import (
"fmt" "fmt"
"io/fs" "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/constants"
"github.com/edgelesssys/constellation/internal/deploy/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"google.golang.org/protobuf/proto"
) )
var ( // Config defines configuration used by CLI.
// 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.
type Config struct { type Config struct {
AutoscalingNodeGroupsMin *int `yaml:"autoscalingNodeGroupsMin,omitempty"` // description: |
AutoscalingNodeGroupsMax *int `yaml:"autoscalingNodeGroupsMax,omitempty"` // Minimum number of nodes in autoscaling group.
StateDiskSizeGB *int `yaml:"StateDisksizeGB,omitempty"` // worker nodes.
Provider *ProviderConfig `yaml:"provider,omitempty"` AutoscalingNodeGroupsMin int `yaml:"autoscalingNodeGroupsMin"`
SSHUsers []*ssh.UserKey `yaml:"sshUsers,omitempty"` // 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. // Default returns a struct with the default config.
func Default() *Config { func Default() *Config {
return &Config{ return &Config{
AutoscalingNodeGroupsMin: intPtr(1), AutoscalingNodeGroupsMin: 1,
AutoscalingNodeGroupsMax: intPtr(10), AutoscalingNodeGroupsMax: 10,
StateDiskSizeGB: intPtr(30), StateDiskSizeGB: 30,
Provider: &ProviderConfig{ IngressFirewall: Firewall{
EC2: &EC2Config{ {
Image: proto.String("ami-07d3864beb84157d3"), Name: "coordinator",
Tags: &[]ec2.Tag{ Description: "Coordinator default port",
{ Protocol: "tcp",
Key: "responsible", IPRange: "0.0.0.0/0",
Value: "cli", FromPort: constants.CoordinatorPort,
},
{
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,
},
},
},
}, },
{
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{ Azure: &AzureConfig{
SubscriptionID: proto.String("0d202bbb-4fa7-4af8-8125-58c269a05435"), SubscriptionID: "0d202bbb-4fa7-4af8-8125-58c269a05435",
TenantID: proto.String("adb650a8-5da3-4b15-b4b0-3daf65ff7626"), TenantID: "adb650a8-5da3-4b15-b4b0-3daf65ff7626",
Location: proto.String("North Europe"), Location: "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"), Image: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/CONSTELLATION-IMAGES/providers/Microsoft.Compute/galleries/Constellation/images/constellation-coreos/versions/0.0.1651150807",
NetworkSecurityGroupInput: &azureClient.NetworkSecurityGroupInput{ Measurements: azurePCRs,
Ingress: cloudtypes.Firewall{ UserAssignedIdentity: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.ManagedIdentity/userAssignedIdentities/constellation-dev-identity",
{
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"),
}, },
GCP: &GCPConfig{ GCP: &GCPConfig{
Project: proto.String("constellation-331613"), Project: "constellation-331613",
Region: proto.String("europe-west3"), Region: "europe-west3",
Zone: proto.String("europe-west3-b"), Zone: "europe-west3-b",
Image: proto.String("projects/constellation-images/global/images/constellation-coreos-1651150807"), Image: "projects/constellation-images/global/images/constellation-coreos-1651150807",
FirewallInput: &gcpClient.FirewallInput{ ServiceAccountRoles: []string{
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{
"roles/compute.instanceAdmin.v1", "roles/compute.instanceAdmin.v1",
"roles/compute.networkAdmin", "roles/compute.networkAdmin",
"roles/compute.securityAdmin", "roles/compute.securityAdmin",
"roles/storage.admin", "roles/storage.admin",
"roles/iam.serviceAccountUser", "roles/iam.serviceAccountUser",
}, },
Measurements: &gcpPCRs, Measurements: gcpPCRs,
}, },
QEMU: &QEMUConfig{ QEMU: &QEMUConfig{
Measurements: &qemuPCRs, Measurements: qemuPCRs,
}, },
}, },
} }
} }
// FromFile returns a default config that has been merged with a config file. // FromFile returns config file with `name` read from `fileHandler` by parsing
// If name is empty, the defaults are returned. // it as YAML.
// If name is empty, the default configuration is returned.
func FromFile(fileHandler file.Handler, name string) (*Config, error) { func FromFile(fileHandler file.Handler, name string) (*Config, error) {
conf := Default()
if name == "" { 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) { 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("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 nil, fmt.Errorf("could not load config from file %s: %w", name, err)
} }
return conf, nil return &emptyConf, 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
} }

View File

@ -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,
},
}
}

View File

@ -3,8 +3,6 @@ package config
import ( import (
"testing" "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/constants"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -19,48 +17,57 @@ func TestDefaultConfig(t *testing.T) {
} }
func TestFromFile(t *testing.T) { func TestFromFile(t *testing.T) {
someProviderConfig := &ProviderConfig{ testCases := map[string]struct {
GCP: &GCPConfig{ config *Config
FirewallInput: &client.FirewallInput{ configName string
Ingress: cloudtypes.Firewall{ wantResult *Config
{ wantErr bool
Name: "firstFirewallRule", }{
Description: "firstFirewallRule description", "default config from default file": {
Protocol: "tcp", config: Default(),
FromPort: 4444, configName: constants.ConfigFilename,
}, wantResult: Default(),
{ },
Name: "secondFirewallRule", "default config from different path": {
Description: "secondFirewallRule description", config: Default(),
Protocol: "udp", configName: "other-config.yaml",
FromPort: 5555, 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,
}, },
}, },
} "modify default config": {
config: func() *Config {
testCases := map[string]struct { conf := Default()
from *Config conf.Provider.GCP.Region = "eu-north1"
configName string conf.Provider.GCP.Zone = "eu-north1-a"
wantResultMutator func(c *Config) // mutates the Default() config to the expected result. return conf
wantErr bool }(),
}{ configName: constants.ConfigFilename,
"overwrite slices": { wantResult: func() *Config {
from: &Config{Provider: someProviderConfig}, conf := Default()
configName: constants.ConfigFilename, conf.Provider.GCP.Region = "eu-north1"
wantResultMutator: func(c *Config) { c.Provider = someProviderConfig }, conf.Provider.GCP.Zone = "eu-north1-a"
}, return conf
"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,
}, },
} }
@ -70,7 +77,9 @@ func TestFromFile(t *testing.T) {
require := require.New(t) require := require.New(t)
fileHandler := file.NewHandler(afero.NewMemMapFs()) 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) result, err := FromFile(fileHandler, tc.configName)
@ -78,14 +87,7 @@ func TestFromFile(t *testing.T) {
assert.Error(err) assert.Error(err)
} else { } else {
require.NoError(err) require.NoError(err)
wantResult := Default() assert.Equal(tc.wantResult, result)
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))
} }
}) })
} }

View File

@ -1,9 +1,35 @@
package config package config
import "encoding/base64" import (
"encoding/base64"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
)
type Measurements map[uint32][]byte 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) { func (m Measurements) MarshalYAML() (interface{}, error) {
base64Map := make(map[uint32]string) base64Map := make(map[uint32]string)

View File

@ -13,6 +13,7 @@ import (
"path" "path"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/talos-systems/talos/pkg/machinery/config/encoder"
"gopkg.in/yaml.v3" "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") err = errors.New("recovered from panic")
} }
}() }()
data, err := yaml.Marshal(content) data, err := encoder.NewEncoder(content).Encode()
if err != nil { if err != nil {
return err return err
} }