diff --git a/cmd/infra.go b/cmd/infra.go index 1625fde8..16704f18 100644 --- a/cmd/infra.go +++ b/cmd/infra.go @@ -27,6 +27,7 @@ func newCreateCommand(logger *logrus.Logger) *cobra.Command { tfDir := viper.GetString("tf-dir") tfVarsDir := viper.GetString("tf-vars-dir") disableIPDetection := viper.GetBool("disable-ip-detection") + extraCIDRs := viper.GetString("extra-cidrs") logger.WithFields(logrus.Fields{ "BucketName": bucketName, @@ -40,7 +41,8 @@ func newCreateCommand(logger *logrus.Logger) *cobra.Command { sim.WithAttackRepo(attackRepo), sim.WithBucketName(bucketName), sim.WithoutIPDetection(disableIPDetection), - sim.WithTfVarsDir(tfVarsDir)) + sim.WithTfVarsDir(tfVarsDir), + sim.WithExtraCIDRs(extraCIDRs)) err := simulator.Create() if err != nil { diff --git a/cmd/root.go b/cmd/root.go index 07267711..16e196f0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -77,6 +77,12 @@ func newCmdRoot() *cobra.Command { panic(err) } + rootCmd.PersistentFlags().StringP("extra-cidrs", "e", "", + "Extra CIDRs that will be allowed to access to the bastion host. MUST be a valid CIDR and a list MUST be comma delimited") + if err := viper.BindPFlag("extra-cidrs", rootCmd.PersistentFlags().Lookup("extra-cidrs")); err != nil { + panic(err) + } + // TODO: (rem) this is also used to locate the perturb.sh script which may be // subsumed by this app rootCmd.PersistentFlags().StringP("scenarios-dir", "s", "./simulation-scripts", diff --git a/pkg/simulator/simulator.go b/pkg/simulator/simulator.go index 30a3efea..b4ba858b 100644 --- a/pkg/simulator/simulator.go +++ b/pkg/simulator/simulator.go @@ -29,6 +29,9 @@ type Simulator struct { ScenariosDir string // disableIPDetection enables IP checks used for cidr access. Enabled by default. DisableIPDetection bool + // Extra CIDRs to be added to the bastion security group to allow SSH from arbitrary + // locations + ExtraCIDRs string } // Option is a type used to configure a `Simulator` instance @@ -116,3 +119,11 @@ func WithoutIPDetection(disableIPDetection bool) Option { s.DisableIPDetection = disableIPDetection } } + +// WithExtraCIDRs returns a configurer for creating a `Simulator` instance with +// `NewSimulator` +func WithExtraCIDRs(extraCIDRs string) Option { + return func(s *Simulator) { + s.ExtraCIDRs = extraCIDRs + } +} diff --git a/pkg/simulator/terraform.go b/pkg/simulator/terraform.go index 082fd7cd..91b42615 100644 --- a/pkg/simulator/terraform.go +++ b/pkg/simulator/terraform.go @@ -91,8 +91,9 @@ func (s *Simulator) InitIfNeeded() error { "PublicKey": publickey, "AccessCIDR": accessCIDR, "BucketName": s.BucketName, + "ExtraCIDRs": s.ExtraCIDRs, }).Debug("Writing Terraform tfvars file") - err = EnsureLatestTfVarsFile(s.TfVarsDir, *publickey, accessCIDR, s.BucketName, s.AttackTag, s.AttackRepo) + err = EnsureLatestTfVarsFile(s.TfVarsDir, *publickey, accessCIDR, s.BucketName, s.AttackTag, s.AttackRepo, s.ExtraCIDRs) if err != nil { return errors.Wrap(err, "Error writing tfvars") } diff --git a/pkg/simulator/terraform_vars.go b/pkg/simulator/terraform_vars.go index 3d4b548b..db2912af 100644 --- a/pkg/simulator/terraform_vars.go +++ b/pkg/simulator/terraform_vars.go @@ -1,6 +1,8 @@ package simulator import ( + "strings" + "github.com/controlplaneio/simulator-standalone/pkg/util" ) @@ -12,32 +14,42 @@ type TfVars struct { BucketName string AttackTag string AttackRepo string + ExtraCIDRs string } // NewTfVars creates a TfVars struct with all the defaults -func NewTfVars(publicKey, accessCIDR, bucketName, attackTag, attackRepo string) TfVars { +func NewTfVars(publicKey, accessCIDR, bucketName, attackTag, attackRepo, extraCIDRs string) TfVars { return TfVars{ PublicKey: publicKey, AccessCIDR: accessCIDR, BucketName: bucketName, AttackTag: attackTag, AttackRepo: attackRepo, + ExtraCIDRs: extraCIDRs, } } func (tfv *TfVars) String() string { + if tfv.ExtraCIDRs != "" { + splitCIDRs := strings.Split(tfv.ExtraCIDRs, ",") + for i := range splitCIDRs { + splitCIDRs[i] = strings.TrimSpace(splitCIDRs[i]) + } + templatedCIDRs := strings.Join(splitCIDRs, "\", \"") + tfv.AccessCIDR = tfv.AccessCIDR + "\", \"" + templatedCIDRs + } + return "access_key = \"" + tfv.PublicKey + "\"\n" + - "access_cidr = \"" + tfv.AccessCIDR + "\"\n" + + "access_cidr = [\"" + tfv.AccessCIDR + "\"]\n" + "attack_container_tag = \"" + tfv.AttackTag + "\"\n" + "attack_container_repo = \"" + tfv.AttackRepo + "\"\n" + "state_bucket_name = \"" + tfv.BucketName + "\"\n" - } // EnsureLatestTfVarsFile always writes an tfvars file -func EnsureLatestTfVarsFile(tfVarsDir, publicKey, accessCIDR, bucket, attackTag, attackRepo string) error { +func EnsureLatestTfVarsFile(tfVarsDir, publicKey, accessCIDR, bucket, attackTag, attackRepo, extraCIDRs string) error { filename := tfVarsDir + "/settings/bastion.tfvars" - tfv := NewTfVars(publicKey, accessCIDR, bucket, attackTag, attackRepo) + tfv := NewTfVars(publicKey, accessCIDR, bucket, attackTag, attackRepo, extraCIDRs) return util.OverwriteFile(filename, tfv.String()) } diff --git a/pkg/simulator/terraform_vars_test.go b/pkg/simulator/terraform_vars_test.go index 8adbc3fe..88a88c5b 100644 --- a/pkg/simulator/terraform_vars_test.go +++ b/pkg/simulator/terraform_vars_test.go @@ -15,9 +15,9 @@ import ( func Test_TfVars_String(t *testing.T) { t.Parallel() tfv := simulator.NewTfVars("ssh-rsa", "10.0.0.1/16", "test-bucket", - "latest", "controlplane/simulator-attack") + "latest", "controlplane/simulator-attack", "10.0.0.1/16") expected := `access_key = "ssh-rsa" -access_cidr = "10.0.0.1/16" +access_cidr = ["10.0.0.1/16", "10.0.0.1/16"] attack_container_tag = "latest" attack_container_repo = "controlplane/simulator-attack" state_bucket_name = "test-bucket" @@ -36,10 +36,10 @@ func Test_Ensure_TfVarsFile_with_settings(t *testing.T) { require.NoError(t, err) err = simulator.EnsureLatestTfVarsFile(workDir, "ssh-rsa", "10.0.0.1/16", - "test-bucket", "latest", "controlplane/simulator-attack") + "test-bucket", "latest", "controlplane/simulator-attack", "10.0.0.1/16, 10.0.0.1/32") require.NoError(t, err) expected := `access_key = "ssh-rsa" -access_cidr = "10.0.0.1/16" +access_cidr = ["10.0.0.1/16", "10.0.0.1/16", "10.0.0.1/32"] attack_container_tag = "latest" attack_container_repo = "controlplane/simulator-attack" state_bucket_name = "test-bucket" diff --git a/terraform/deployments/AWS/variables.tf b/terraform/deployments/AWS/variables.tf index b6355fad..687435c4 100644 --- a/terraform/deployments/AWS/variables.tf +++ b/terraform/deployments/AWS/variables.tf @@ -7,6 +7,7 @@ variable "access_key" { variable "access_cidr" { description = "cidr range of client connection" + type = list(string) } // Variables below are to have defined defaults diff --git a/terraform/modules/AWS/SecurityGroups/main.tf b/terraform/modules/AWS/SecurityGroups/main.tf index dda784a4..08081172 100644 --- a/terraform/modules/AWS/SecurityGroups/main.tf +++ b/terraform/modules/AWS/SecurityGroups/main.tf @@ -11,20 +11,6 @@ resource "aws_security_group" "simulator_bastion_sg" { name = "simulator-bastion-sg-${random_uuid.unique.result}" vpc_id = var.vpc_id - ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = [var.access_cidr] - } - - egress { - protocol = -1 - from_port = 0 - to_port = 0 - cidr_blocks = ["0.0.0.0/0"] - } - tags = merge( var.default_tags, { @@ -33,6 +19,26 @@ resource "aws_security_group" "simulator_bastion_sg" { ) } +resource "aws_security_group_rule" "allow_all_egress" { + type = "egress" + from_port = 0 + to_port = 0 + protocol = -1 + cidr_blocks = ["0.0.0.0/0"] + + security_group_id = aws_security_group.simulator_bastion_sg.id +} + +resource "aws_security_group_rule" "allow_user_ip_ssh" { + type = "ingress" + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = var.access_cidr + + security_group_id = aws_security_group.simulator_bastion_sg.id +} + // Private subnet security group // Restricts ingress from public subnet using ssh // Egress open (via NAT for internet) diff --git a/terraform/modules/AWS/SecurityGroups/variables.tf b/terraform/modules/AWS/SecurityGroups/variables.tf index b19a1587..33946d41 100644 --- a/terraform/modules/AWS/SecurityGroups/variables.tf +++ b/terraform/modules/AWS/SecurityGroups/variables.tf @@ -1,5 +1,6 @@ variable "access_cidr" { description = "cidr range of client connection" + type = list(string) } variable "vpc_id" { @@ -18,4 +19,3 @@ variable "default_tags" { description = "Default tags for all resources" type = map(string) } -