Skip to content

Instantly share code, notes, and snippets.

@akerouanton
Created June 7, 2016 10:02
Show Gist options
  • Save akerouanton/7d06eda2c640f4fd17badc5b3d7828ac to your computer and use it in GitHub Desktop.
Save akerouanton/7d06eda2c640f4fd17badc5b3d7828ac to your computer and use it in GitHub Desktop.
Cloudformation, ansible, SSH bastion & rsync
{
/** Should go into roles/your_playbook/files/bastion.json */
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"BastionInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"AvailabilityZone": "eu-central-1a",
"ImageId": "ami-ccc021a3",
"InstanceType": "t2.nano",
"KeyName": {"Ref": "KeyName"},
"NetworkInterfaces": [
{
"AssociatePublicIpAddress": true,
"DeviceIndex": 0,
"GroupSet": [{"Ref": "BastionSecurityGroup"}],
"SubnetId": {"Ref": "Subnet"}
}
],
"SourceDestCheck": true,
"Tags": [
{"Key": "EnvironmentTag", "Value": {"Ref": "EnvironmentTag"}}
],
"UserData": {"Fn::Base64": {"Fn::Join": ["", [
"#!/bin/bash -xe\n",
"echo '\nAllowAgentForwarding yes\n' >> /etc/ssh/sshd_config\n",
"systemctl restart ssh"
]]}}
}
},
"BastionSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Security group for bastion instances",
"VpcId": {"Ref": "VpcId"},
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": 22,
"ToPort": 22,
"CidrIp": "0.0.0.0/0"
}
],
"SecurityGroupEgress": [
{
"IpProtocol": "tcp",
"FromPort": 22,
"ToPort": 22,
"CidrIp": {"Ref": "VpcCidr"}
},
{
"IpProtocol": "tcp",
"FromPort": 53,
"ToPort": 53,
"CidrIp": "0.0.0.0/0"
}
]
}
}
},
"Parameters": {
"KeyName": {
"Type": "AWS::EC2::KeyPair::KeyName"
},
"Subnet": {
"Type": "AWS::EC2::Subnet::Id"
},
"VpcId": {
"Type": "AWS::EC2::VPC::Id"
},
"VpcCidr": {
"Type": "String"
}
},
"Outputs": {
"BastionInstance": {
"Value": {"Fn::GetAtt": ["BastionInstance", "PublicIp"]}
}
}
}
---
# Should go into roles/your_playbook/tasks/main.yml
- name: validate cloudformation template for bastion stack
shell: aws cloudformation validate-template --template-body file://{{ playbook_dir }}/roles/your_playbook/files/bastion.json
always_run: yes
- name: start/update bastion stack from cloudformation template
cloudformation:
stack_name: "bastion"
aws_region: "{{ region }}"
state: present
disable_rollback: "{{ disable_rollback }}"
template: roles/your_playbook/files/bastion.json
template_parameters:
KeyName: "{{ user_ssh_key_pair }}"
Subnet: "{{ networking_stack.stack_outputs.PublicSubnet }}"
VpcId: "{{ networking_stack.stack_outputs.VPC }}"
VpcCidr: "{{ vpc_cidr }}"
DebugMode: "{{ ansible_debug|default(false)|string }}"
ignore_errors: yes
register: bastion_stack
when: not ansible_check_mode
- name: show bastion stack outputs
debug: var=bastion_stack.stack_outputs
when: ansible_debug|default(false)
- name: Create a local ssh_config file for accessing private instances
template:
src: ssh_config.j2
dest: /tmp/ssh_config
when: not ansible_check_mode and not bastion_stack|failed and ansible_debug|default(false)
- name: delete failed bastion stack
cloudformation:
stack_name: "bastion"
aws_region: "{{ region }}"
state: absent
when: bastion_stack|failed and delete_on_failure
- name: wait for newly created bastion host to become ready
wait_for:
host: "{{ bastion_stack.stack_outputs.BastionInstance }}"
port: 22
when: not ansible_check_mode
- name: copy blabla.sh through an SSH Bastion
synchronize:
src: "/tmp/blabla.sh"
dest: "/usr/src/app"
rsync_opts: "-e 'ssh -F /tmp/ssh_config'"
# Should go into roles/your_playbook/templates/ssh_config.j2
Host bastion.your_domain
Hostname {{ bastion_stack.stack_outputs.BastionInstance }}
Host {{ bastion_stack.stack_outputs.BastionInstance }}
User admin
ForwardAgent yes
ControlMaster auto
ControlPath ~/.ssh/ansible-%r@%h:%p
ControlPersist 5m
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Host 10.0.2.*
User admin
ProxyCommand ssh -F /tmp/ssh_config -W %h:%p admin@{{ bastion_stack.stack_outputs.BastionInstance }}
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment