2016-03-06 18 views
9

UPDATE: State lavorando su questo fuori e su tra le altre cose. Non riesco a ottenere una configurazione funzionante con due sottoreti e un bastione SSH. Posizionamento di bounty per una configurazione completa di file .tf che: * crea due subnet private * crea un bastione * fila un'istanza ec2 su ogni subnet configurata tramite il bastione (eseguire un comando arbitrario della shell tramite il bastione) * ha un Internet gateway configurato * dispone di un gateway NAT per i padroni di casa sulle sottoreti private * ha percorsi e gruppi di protezione configurato di conseguenzaCome effettuare il routing tra due sottoreti in un AWPC VPC con Terraform?

Original post: sto cercando di imparare Terraform e costruire un prototipo. Ho un VPC AWS configurato tramite Terraform. Oltre a una sottorete DMZ, ho una sottorete pubblica "web" che riceve traffico da Internet. Ho una 'sub' sottorete privata che non è accessibile da internet. Sto cercando di configurare un host bastion in modo che terraform possa eseguire il provisioning delle istanze sulla subnet 'app' privata. Non sono ancora riuscito a farlo funzionare.

Quando eseguo l'accesso al bastione, non posso eseguire SSH dall'host del bastione a nessuna istanza all'interno della sottorete privata. Sospetto che ci sia un problema di routing. Ho costruito questo prototipo tramite diversi esempi disponibili e la documentazione. Molti degli esempi utilizzano tecniche leggermente diverse e definizioni di instradamento terraformale tramite il provider aws.

Qualcuno può fornire il modo ideale o appropriato per definire queste tre subnet ("web" pubblico, "dmz" pubblico/un bastione e "app" privata) in modo che le istanze sulla sottorete "Web" possano accedere la sottorete 'app' e che l'host bastion nella DMZ può eseguire il provisioning di istanze nella sottorete 'app' privata?

un elemento di cattura dei miei file di configurazione sono al di sotto:

resource "aws_subnet" "dmz" { 
    vpc_id = "${aws_vpc.vpc-poc.id}" 
    cidr_block = "${var.cidr_block_dmz}" 
} 

resource "aws_route_table" "dmz" { 
    vpc_id = "${aws_vpc.vpc-poc.id}" 
    route { 
     cidr_block = "0.0.0.0/0" 
     gateway_id = "${aws_internet_gateway.gateway.id}" 
    } 
} 

resource "aws_route_table_association" "dmz" { 
    subnet_id = "${aws_subnet.dmz.id}" 
    route_table_id = "${aws_route_table.dmz.id}" 
} 

resource "aws_subnet" "web" { 
    vpc_id = "${aws_vpc.vpc-poc.id}" 
    cidr_block = "10.200.2.0/24" 
} 

resource "aws_route_table" "web" { 
    vpc_id = "${aws_vpc.vpc-poc.id}" 
    route { 
     cidr_block = "0.0.0.0/0" 
     instance_id = "${aws_instance.bastion.id}" 
    } 
} 

resource "aws_route_table_association" "web" { 
    subnet_id = "${aws_subnet.web.id}" 
    route_table_id = "${aws_route_table.web.id}" 
} 

resource "aws_subnet" "app" { 
    vpc_id = "${aws_vpc.vpc-poc.id}" 
    cidr_block = "10.200.3.0/24" 
} 

resource "aws_route_table" "app" { 
    vpc_id = "${aws_vpc.vpc-poc.id}" 
    route { 
     cidr_block = "0.0.0.0/0" 
     instance_id = "${aws_instance.bastion.id}" 
    } 
} 

resource "aws_route_table_association" "app" { 
    subnet_id = "${aws_subnet.app.id}" 
    route_table_id = "${aws_route_table.app.id}" 
} 
+1

Se volete più aiuto con questo è necessario espandere il file TF dato per mostrare anche eventuali gruppi di protezione/NACLs ecc come non credo percorsi sono un problema qui (diverso dalla mancanza di uscita accesso web nelle sottoreti private a meno che il tuo bastione non funzioni anche come gateway NAT) – ydaetskcoR

risposta

3

Ecco uno snippet che può essere d'aiuto. Questo non è stato verificato ma è stato estratto da uno dei miei file terraform in cui provisioning VM in una sottorete privata. So che questo funziona con una sottorete privata, ho provato ad implementare due qui come la tua domanda originale.

Passando attraverso le mie istanze NAT per colpire e fornire caselle di sottorete private con Terraform. Se i gruppi di sicurezza sono impostati correttamente, funziona. Ci sono voluti alcuni esperimenti per me.

/* VPC creation */ 
resource "aws_vpc" "vpc_poc" { 
    cidr_block = "10.200.0.0/16" 
} 

/* Internet gateway for the public subnets */ 
resource "aws_internet_gateway" "gateway" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
} 

/* DMZ subnet - public */ 
resource "aws_subnet" "dmz" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
    cidr_block = "10.200.1.0/24" 
    /* may help to be explicit here */ 
    map_public_ip_on_launch = true 
    /* this is recommended in the docs */ 
    depends_on = ["aws_internet_gateway.gateway"] 
} 

resource "aws_route_table" "dmz" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
    route { 
     cidr_block = "0.0.0.0/0" 
     gateway_id = "${aws_internet_gateway.gateway.id}" 
    } 
} 

resource "aws_route_table_association" "dmz" { 
    subnet_id = "${aws_subnet.dmz.id}" 
    route_table_id = "${aws_route_table.dmz.id}" 
} 

/* Web subnet - public */ 
resource "aws_subnet" "web" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
    cidr_block = "10.200.2.0/24" 
    map_public_ip_on_launch = true 
    depends_on = ["aws_internet_gateway.gateway"] 
} 

resource "aws_route_table" "web" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
    route { 
     cidr_block = "0.0.0.0/0" 
     /* your public web subnet needs access to the gateway */ 
     /* this was set to bastion before so you had a circular arg */ 
     gateway_id = "${aws_internet_gateway.gateway.id}" 
    } 
} 

resource "aws_route_table_association" "web" { 
    subnet_id = "${aws_subnet.web.id}" 
    route_table_id = "${aws_route_table.web.id}" 
} 

/* App subnet - private */ 
resource "aws_subnet" "app" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
    cidr_block = "10.200.3.0/24" 
} 

/* Create route for DMZ Bastion */ 
resource "aws_route_table" "app" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
    route { 
     cidr_block = "0.0.0.0/0" 
     /* this send traffic to the bastion to pass off */ 
     instance_id = "${aws_instance.nat_dmz.id}" 
    } 
} 

/* Create route for App Bastion */ 
resource "aws_route_table" "app" { 
    vpc_id = "${aws_vpc.vpc_poc.id}" 
    route { 
     cidr_block = "0.0.0.0/0" 
     /* this send traffic to the bastion to pass off */ 
     instance_id = "${aws_instance.nat_web.id}" 
    } 
} 

resource "aws_route_table_association" "app" { 
    subnet_id = "${aws_subnet.app.id}" 
    route_table_id = "${aws_route_table.app.id}" 
} 

/* Default security group */ 
resource "aws_security_group" "default" { 
    name = "default-sg" 
    description = "Default security group that allows inbound and outbound traffic from all instances in the VPC" 
    vpc_id = "${aws_vpc.vpc_poc.id}" 

    ingress { 
    from_port = "0" 
    to_port  = "0" 
    protocol = "-1" 
    self  = true 
    } 

    egress { 
    from_port = "0" 
    to_port  = "0" 
    protocol = "-1" 
    self  = true 
    } 
} 

/* Security group for the nat server */ 
resource "aws_security_group" "nat" { 
    name  = "nat-sg" 
    description = "Security group for nat instances that allows SSH and VPN traffic from internet. Also allows outbound HTTP[S]" 
    vpc_id  = "${aws_vpc.vpc_poc.id}" 

    ingress { 
    from_port = 80 
    to_port  = 80 
    protocol = "tcp" 
    /* this your private subnet cidr */ 
    cidr_blocks = ["10.200.3.0/24"] 
    } 
    ingress { 
    from_port = 443 
    to_port  = 443 
    protocol = "tcp" 
    /* this is your private subnet cidr */ 
    cidr_blocks = ["10.200.3.0/24"] 
    } 
    ingress { 
    from_port = 22 
    to_port  = 22 
    protocol = "tcp" 
    cidr_blocks = ["0.0.0.0/0"] 
    } 
    ingress { 
    from_port = -1 
    to_port  = -1 
    protocol = "icmp" 
    cidr_blocks = ["0.0.0.0/0"] 
    } 

    egress { 
    from_port = 80 
    to_port  = 80 
    protocol = "tcp" 
    cidr_blocks = ["0.0.0.0/0"] 
    } 
    egress { 
    from_port = 443 
    to_port  = 443 
    protocol = "tcp" 
    cidr_blocks = ["0.0.0.0/0"] 
    } 
    egress { 
    from_port = 22 
    to_port  = 22 
    protocol = "tcp" 
    /* this is the vpc cidr block */ 
    cidr_blocks = ["10.200.0.0/16"] 
    } 
    egress { 
    from_port = -1 
    to_port  = -1 
    protocol = "icmp" 
    cidr_blocks = ["0.0.0.0/0"] 
    } 
} 

/* Security group for the web */ 
resource "aws_security_group" "web" { 
    name = "web-sg" 
    description = "Security group for web that allows web traffic from internet" 
    vpc_id = "${aws_vpc.vpc_poc.id}" 

    ingress { 
    from_port = 80 
    to_port = 80 
    protocol = "tcp" 
    cidr_blocks = ["0.0.0.0/0"] 
    } 

    ingress { 
    from_port = 443 
    to_port = 443 
    protocol = "tcp" 
    cidr_blocks = ["0.0.0.0/0"] 
    } 
} 

/* Install deploy key for use with all of our provisioners */ 
resource "aws_key_pair" "deployer" { 
    key_name = "deployer-key" 
    public_key = "${file("~/.ssh/id_rsa")}" 
} 

/* Setup NAT in DMZ subnet */ 
resource "aws_instance" "nat_dmz" { 
    ami    = "ami-67a54423" 
    availability_zone = "us-west-1a" 
    instance_type  = "m1.small" 
    key_name   = "${aws_key_pair.deployer.id}" 
    /* Notice we are assigning the security group here */ 
    security_groups = ["${aws_security_group.nat.id}"] 

    /* this puts the instance in your public subnet, but translate to the private one */ 
    subnet_id   = "${aws_subnet.dmz.id}" 

    /* this is really important for nat instance */ 
    source_dest_check = false 
    associate_public_ip_address = true 
} 

/* Give NAT EIP In DMZ */ 
resource "aws_eip" "nat_dmz" { 
    instance = "${aws_instance.nat_dmz.id}" 
    vpc  = true 
} 

/* Setup NAT in Web subnet */ 
resource "aws_instance" "nat_web" { 
    ami    = "ami-67a54423" 
    availability_zone = "us-west-1a" 
    instance_type  = "m1.small" 
    key_name   = "${aws_key_pair.deployer.id}" 
    /* Notice we are assigning the security group here */ 
    security_groups = ["${aws_security_group.nat.id}"] 

    /* this puts the instance in your public subnet, but translate to the private one */ 
    subnet_id   = "${aws_subnet.web.id}" 

    /* this is really important for nat instance */ 
    source_dest_check = false 
    associate_public_ip_address = true 
} 

/* Give NAT EIP In DMZ */ 
resource "aws_eip" "nat_web" { 
    instance = "${aws_instance.nat_web.id}" 
    vpc  = true 
} 

/* Install server in private subnet and jump host to it with terraform */ 
resource "aws_instance" "private_box" { 
    ami   = "ami-d1315fb1" 
    instance_type = "t2.large" 
    key_name  = "${aws_key_pair.deployer.id}" 
    subnet_id  = "${aws_subnet.api.id}" 
    associate_public_ip_address = false 

    /* this is what gives the box access to talk to the nat */ 
    security_groups = ["${aws_security_group.nat.id}"] 

    connection { 
    /* connect through the nat instance to reach this box */ 
    bastion_host = "${aws_eip.nat_dmz.public_ip}" 
    bastion_user = "ec2-user" 
    bastion_private_key = "${file("keys/terraform_rsa")}" 

    /* connect to box here */ 
    user = "ec2-user" 
    host = "${self.private_ip}" 
    private_key = "${file("~/.ssh/id_rsa")}" 
    } 
} 
3

A meno che bastion host funge anche da un NAT (io non vi consiglio di coniugare i ruoli sulla stessa istanza) poi nella Rete e app sottoreti non avrà alcun accesso al Web in uscita, ma per il resto sembra che il routing sia corretto, poiché TF aggiungerà automaticamente un record di routing locale per il VPC.

Finché si dispone di quel record di routing locale che copre il proprio intervallo VPC, il routing dovrebbe andare bene. Prendendo il tuo file di configurazione di Terraform (e aggiungendo le risorse minime necessarie) mi consente di creare alcune istanze di base in tutte e 3 le subnet e instradare correttamente tra di loro in modo tale che probabilmente manchi qualcos'altro come gruppi di sicurezza o NACL.

+0

Grazie. Di nuovo, il problema che ho è che terraform non può SSH nell'istanza (s) sulla sottorete privata tramite il bastione. Ho pensato che fosse routing, ma scaverò più a fondo e postare aggiornamenti qui. – n8gard

2

Non hai dato il tuo Terraform completo, ma sarà necessario consentire SSH nelle vostre istanze 'app' VPC sia da bastione IP o il blocco CIDR del bastion host, in modo simile a questo:

resource "aws_security_group" "allow_ssh" { 
    name = "allow_ssh" 
    description = "Allow inbound SSH traffic" 

    ingress { 
     from_port = 22 
     to_port = 22 
     protocol = "tcp" 
     cidr_blocks = ["${aws_instance.bastion.private_ip}/32"] 
    } 
} 

Poi nelle risorse esempio 'app', ti consigliamo di aggiungere il gruppo di protezione:

... 
vpc_security_group_ids = ["${aws_security_group.allow_ssh.id}"] 
... 

https://www.terraform.io/docs/providers/aws/r/security_group_rule.html

+0

Questo è stato utile perché mi ha mostrato un nuovo modo di esprimerlo ma, purtroppo, non era la soluzione. Deve esserci qualcos'altro mancante. – n8gard

+0

Se si desidera una guida specifica, fornire il resto del modello. Non c'è abbastanza contesto qui. – Nathan

2

si dovrebbe controllare i problemi di rete con tcpdump e altri strumenti di debug. Verificare che:

  1. Ips sono raggiungibili e le impostazioni di rete sono corrette (ad esempio 10.200.2.X può raggiungere ip del bastion host)
  2. che iptables/un altro firewall non blocchi il traffico del server
  3. uno ssh è ascolta (ssh ad un IP di coloro host da quelli host)
  4. Hai giusta sicurezza i gruppi per gli host (si può vedere questo in proterties di istanze EC2)
  5. Prova a fiutare il traffico con tcpdump
2

non vedo una ragione per bastion host.

Ho qualcosa di simile usando saltstack, ho solo il controllo del resto utilizzando un server master all'interno di un VPC, assegnare ad esso un gruppo di sicurezza specifico per consentire l'accesso.

CIDR X/24 
subnetX.0/26- subnet for control server. <aster server ip EC2-subnet1/32 
subnetX.64/26 - private minions 
subentX.128/26 - public minions 
subnetX.192/26- private minions 

quindi creare una tabella di route per ogni sottorete per il vostro amore di isolamento Collegare ogni percorso di sottorete individuale. Per esempio.

rt-1 - subnetX.0/26 
rt-2 - subnetX.64/26 
rt-3 - subnetX.128/26 
rt-4 - subnetX.192/26 

assicurarsi che la tabella di route ha qualcosa come questo in modo che il percorso è possibile per rt-1 esempio la connessione a tutti

destination: CIDR X/24 Target: local 

Poi limitare la connettività attraverso gruppo di protezione inbound.e.g. consentire SSH da subnet EC2/32

Una volta eseguito tutto il lavoro con il server di controllo, posso eliminare il percorso specifico che dice CIDR X/24 Destinazione: locale nella mia sottorete pubblica, quindi non è più in grado instradare il traffico al mio CIDR locale.

Non c'è motivo per me di creare un bastione complicato poiché ho già dato il potere di rimuovere il percorso nel server di controllo.

Problemi correlati