Giter Club home page Giter Club logo

tf_aws_bastion_s3_keys's Introduction

tf_aws_bastion_s3_keys

A Terraform module for creating resilient bastion host using auto-scaling group (min=max=desired=1) and populate its ~/.ssh/authorized_keys with public keys fetched from S3 bucket.

This module can append public keys, setup cron to update them and run additional commands at the end of setup. Note that if it is set up to update the keys, removing a key from the bucket will also remove it from the bastion host.

Only SSH access is allowed to the bastion host.

Input variables:

  • name - Name (default, bastion)
  • instance_type - Instance type (default, t2.micro)
  • ami - AMI ID of Ubuntu (see samples/ami.tf)
  • region - Region (default, eu-west-1)
  • iam_instance_profile - IAM instance profile which is allowed to access S3 bucket (see samples/iam_s3_readonly.tf)
  • enable_monitoring - Whether to enable detailed monitoring (default, true)
  • s3_bucket_name - S3 bucket name which contains public keys (see samples/s3_ssh_public_keys.tf)
  • s3_bucket_uri โ€“ S3 URI which contains the public keys. If specified, s3_bucket_name will be ignored
  • vpc_id - VPC where bastion host should be created
  • subnet_ids - List of subnet IDs where auto-scaling should create instances
  • keys_update_frequency - How often to update keys. A cron timespec or an empty string to turn off (default)
  • additional_user_data_script - Additional user-data script to run at the end
  • user_data_file - Override whold user-data script to add some custom security policies or to add support for OS you prefer. If not specified (by default) standard script will be used.
  • associate_public_ip_address - Whether to auto-assign public IP to the instance (by default - false)
  • eip - EIP to put into EC2 tag (can be used with scripts like https://github.com/skymill/aws-ec2-assign-elastic-ip, default - empty value)
  • key_name - Launch configuration key name to be applied to created instance(s).
  • allowed_cidr - A list of CIDR Networks to allow ssh access to. Defaults to "0.0.0.0/0"
  • allowed_ipv6_cidr - A list of IPv6 CIDR Networks to allow ssh access to. Defaults to "::/0"
  • allowed_security_groups - A list of Security Group ID's to allow access to the bastion host (useful if bastion is deployed internally) Defaults to empty list
  • extra_tags - Optional a list of Key/Values Tags to be associated to the bastion host (see Interpolated Tags)

Outputs:

  • ssh_user - SSH user to login to bastion
  • security_group_id - ID of the security group the bastion host is launched in.

Terraform versions

For Terraform 0.12, use the version from master:

source  = "github.com/terraform-community-modules/tf_aws_bastion_s3_keys"

For Terraform 0.11, pin the module version to match v1.*. For e.g.:

source  = "github.com/terraform-community-modules/tf_aws_bastion_s3_keys?ref=v1.10.0"

Example

Basic example - In your terraform code add something like this:

module "bastion" {
  source                      = "github.com/terraform-community-modules/tf_aws_bastion_s3_keys"
  instance_type               = "t2.micro"
  ami                         = "ami-123456"
  region                      = "eu-west-1"
  iam_instance_profile        = "s3_readonly"
  s3_bucket_name              = "public-keys-demo-bucket"
  vpc_id                      = "vpc-123456"
  subnet_ids                  = ["subnet-123456", "subnet-6789123", "subnet-321321"]
  keys_update_frequency       = "5,20,35,50 * * * *"
  additional_user_data_script = "date"
}

If you want to assign EIP to instance launched by an auto-scaling group you can provide the desired eip as module input and then execute additional_user_data_script which sets EIP. This way you can use Route53 with EIP, which will always point to existing bastion instance. You will also need to add ec2:AssociateAddress permission to iam_instance_profile (see samples/iam_allow_associateaddress.tf):

module "bastion" {
  // see above
  eip = "${aws_eip.bastion.public_ip}"
  iam_instance_profile        = "s3_readonly-allow_associateaddress"
  additional_user_data_script = <<EOF
REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | grep region | awk -F\" '{print $4}')
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
aws ec2 associate-address --region $REGION --instance-id $INSTANCE_ID --allocation-id ${aws_eip.bastion.id}
EOF
}

resource "aws_eip" "bastion" {
  vpc = true
}

resource "aws_route53_record" "bastion" {
  zone_id = "..."
  name    = "bastion.example.com"
  type    = "A"
  ttl     = "3600"
  records = [aws_eip.bastion.public_ip]
}

After you run terraform apply you should be able to login to your bastion host like:

$ ssh ${module.bastion.ssh_user}@${aws_eip.bastion.public_ip}

or even like this:

PS: In some cases you may consider adding flag -A to ssh command to enable forwarding of the authentication agent connection.

Inputs

Name Description Type Default Required
additional_user_data_script string "" no
allowed_cidr A list of CIDR Networks to allow ssh access to. list(string) [ "0.0.0.0/0" ] no
allowed_ipv6_cidr A list of IPv6 CIDR Networks to allow ssh access to. list(string) [ "::/0" ] no
allowed_security_groups A list of Security Group ID's to allow access to. list(string) [] no
ami string n/a yes
apply_changes_immediately Whether to apply the changes at once and recreate auto-scaling group string "false" no
associate_public_ip_address string "false" no
eip string "" no
enable_hourly_cron_updates string "false" no
enable_monitoring string "true" no
extra_tags A list of tags to associate to the bastion instance. object [] no
iam_instance_profile string n/a yes
instance_type string "t2.micro" no
instance_volume_size_gb The root volume size, in gigabytes string "8" no
key_name string "" no
keys_update_frequency string "" no
name string "bastion" no
region string "eu-west-1" no
s3_bucket_name string n/a yes
s3_bucket_uri string "" no
security_group_ids Comma seperated list of security groups to apply to the bastion. string "" no
ssh_user string "ubuntu" no
subnet_ids A list of subnet ids list [] no
user_data_file string "user_data.sh" no
vpc_id string n/a yes

Outputs

Name Description
asg_id
security_group_id
ssh_user

Authors

Created and maintained by Anton Babenko.

License

Apache 2 Licensed. See LICENSE for full details.

tf_aws_bastion_s3_keys's People

Contributors

antonbabenko avatar aterreno avatar bdirito avatar blotto avatar bnordbo avatar charlyx avatar crumley avatar eliasdorneles avatar ethangunderson avatar gmembre-zenika avatar jhoblitt avatar jimihford avatar kchugalinskiy avatar lachlancooper avatar lukaspour avatar max-rocket-internet avatar michael-henderson avatar mieciu avatar nubs avatar spikeheap avatar steenblik avatar tfhartmann avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tf_aws_bastion_s3_keys's Issues

public_ip is not exported by the module

According to the documentation:

After you run terraform apply you should be able to login to your bastion host like:

$ ssh ${module.bastion.ssh_user}@${module.bastion.instance_ip}

However module.bastion.instance_ip is not actually exported by the module. Furthermore it does not look like autoscaling groups have a way to list the ips of the machines involved.
hashicorp/terraform-provider-aws#511

Thus at the minimum the documentation should be updated.

if there is no bastion key in s3 setup fails

The bucket exists but no ssh key exists in it (wasnt looking to use that part of the module).

from the logs:

/home/ubuntu/update_ssh_authorized_keys.sh: line 26: /home/ubuntu/pub_key_files//*: No such file or directory

ami: "us-west-1" = "ami-2d5c6d4d"
(official hvm:ebs-ssd 6.04 image)

Permission denied on public key.

I created a new bucket on s3 and add my public keys on this bucket.
But I'm getting a "Permission denied (publickey)".

My Module configuration

module "bastion" {
  source                = "github.com/terraform-community-modules/tf_aws_bastion_s3_keys"
  name                  = "foodlogiqBastion"
  instance_type         = "${var.instance_type}"
  ami                   = "${module.bastion_ami.ami_id}"
  region                = "${var.region}"
  iam_instance_profile  = "${aws_iam_instance_profile.s3_readonly.name}"
  s3_bucket_name        = "public-keys-bastion"
  vpc_id                = "${module.vpc.vpc_id}"
  subnet_ids            = ["${module.vpc.public_subnets}"]
  keys_update_frequency = "30 * * * *"
}

resource "aws_iam_instance_profile" "s3_readonly" {
  name  = "s3-readonly"
  roles = ["${aws_iam_role.s3_readonly.name}"]
}

resource "aws_iam_role" "s3_readonly" {
  name               = "s3-readonly-role"
  path               = "/"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "s3_readonly_policy" {
  name   = "s3-readonly-policy"
  role   = "${aws_iam_role.s3_readonly.id}"
  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1425916919000",
            "Effect": "Allow",
            "Action": [
                "s3:List*",
                "s3:Get*"
            ],
            "Resource": "*"
        }
    ]
}
EOF
}

Does not work with latest Debian-based images

The current code does not work with Debian buster or Ubuntu 18.04 LTS or later. This is because these distributions use python3 as the default python version. The fix suggested at PR #62 solves this issue, but is not backwards compatible. Perhaps someone can suggest something better?

Support for Terraform 0.12

Hello!

I tried using this module with Terraform 0.12 and I got errors due to the syntax change:

Error: Unsupported block type

  on .terraform/modules/bastion/main.tf line 6, in resource "aws_security_group" "bastion":
   6:   tags {

Blocks of type "tags" are not expected here. Did you mean to define argument
"tags"? If so, use the equals sign to assign it a value.


Error: Unsupported block type

  on .terraform/modules/bastion/main.tf line 51, in data "template_file" "user_data":
  51:   vars {

Blocks of type "vars" are not expected here. Did you mean to define argument
"vars"? If so, use the equals sign to assign it a value.

Apparently it's just a matter of adding the = sign ? (sorry, terraform noob here)

AWS binary not found when running update_ssh_authorized_keys.sh from crontab

Ubuntu 16.04 running on EC2.

aws binary lives in /usr/local/bin

$ which aws
/usr/local/bin/aws

script is running every 15 mins, but it is not syncing

$ crontab -l
5,20,35,50 * * * * /home/ubuntu/update_ssh_authorized_keys.sh

Logs reveal that aws command not found

$ cat /var/mail/ubuntu
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/home/ubuntu>
X-Cron-Env: <PATH=/usr/bin:/bin>  <<--- My path does not include /usr/local/bin
X-Cron-Env: <LOGNAME=ubuntu>
...
/home/ubuntu/update_ssh_authorized_keys.sh: line 23: aws: command not found
...

Probably this is an issue solved by properly setting up cron and PATH environment variable, however I have not been able to get this to work as advertised. My understanding is that since 12.04 cron should read in any variables set in /etc/environment. Not happening in my case.

$ cat /etc/environment 
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"

Error creating Security Group: InvalidGroup.Duplicate

  • aws_security_group.bastion: Error creating Security Group: InvalidGroup.Duplicate: The security group 'bastion.mydomain.com' already exists for VPC 'vpc-foo'

I've tried deleting it and reran but that doesn't help. I see it described in my state file as i'd expect after a run but it seems terraform is trying to add it even though it knows it's there...

Any ideas?

Disable creation of ipv6 bastion security group inbound rule

Currently the bastion security group will have at least 2 inbound rules: one ipv4 and one ipv6. There is no way to, for example, exclude the ipv6 inbound rule. In the case where there is no ipv6 address for the source defined for the ipv4 inbound rule, one can only workaround this by, for example, providing a ipv6 private address range (fc00::/7).
It seems to me this is a use case that should be addressed by either controlling creation of ipv6 inbound rule with an additional parameter, or changing the default behaviour of the allowed_ipv6_cidr parameter. The latter does not feel right, as it will not be consistent any more with allowed_ipv4_cidr parameter.

Unable to ssh to Bastion host

Hello,

After configuring the bastion host, I am unable to ssh to it.

Here is the config:

module "bastion" {
source = "github.com/terraform-community-modules/tf_aws_bastion_s3_keys"
ssh_user = "admin"
instance_type = "t2.micro"
ami = "${var.ami}"
region = "${var.aws_region}"
iam_instance_profile = "${module.iam.iam-instance-profile}"
s3_bucket_name = "${var.s3_bucket_name}"
#s3_bucket_uri = "${module.s3.bucket_domain_name}"
vpc_id = "${module.app_server.vpc_id}"
subnet_ids = ["${module.app_server.bastion_subnet1}", "${module.app_server.bastion_subnet2}"]
keys_update_frequency = "5,20,35,50 * * * *"
additional_user_data_script = "date"
}

I see that the bastion host gets created.

I also see the ssh keys..

aws s3 sync --delete $BUCKET_URI $PUB_KEYS_DIR
download: s3://my-s3-bastion-bucket-dev/admin.pub to ../../../../../../tmp/vv/admin.pub
download: s3://my-s3-bastion-bucket-dev/id_rsa.pub to ../../../../../../tmp/vv/id_rsa.pub

and ssh debug...

debug2: key: public_keys/admin (0x7fbf23d15010), explicit
debug3: send packet: type 5
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey
debug3: start over, passed a different list publickey
debug3: preferred publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: public_keys/admin
debug3: send_pubkey_test
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey
debug2: we did not send a packet, disable method
debug1: No more authentication methods to try.
Permission denied (publickey).

The user script that runs has these,

#!/usr/bin/env bash

set -e

BUCKET_NAME=my-s3-bastion-bucket-dev
BUCKET_URI=
SSH_USER=admin
MARKER="# KEYS_BELOW_WILL_BE_UPDATED_BY_TERRAFORM"
KEYS_FILE=/home/$SSH_USER/.ssh/authorized_keys
TEMP_KEYS_FILE=$(mktemp /tmp/authorized_keys.XXXXXX)
PUB_KEYS_DIR=/home/$SSH_USER/pub_key_files/
PATH=/usr/local/bin:$PATH

Not sure what is going wrong. Appreciate any input.

  • Shekar

Support multiple users

Currently this module adds multiple keys to a single user. I don't think it would be too hard to create a user for each key. What do you think? It would be a lot more usable.

Why autoscaling groups?

I'm curious as to why this module moved away from just making an AWS Instance and instead using an autoscaling group. I'd like to create a bastion that I can use as a jump box during the provisioning of certain private instances in private subnets as well as to SSH into those boxes after provisioning. I should be able to do that with just an aws_instance no? Is this meant for some other usage? Why create an autoscaling group with only one instance?

Question: Equivalent of elastic ip tricks but for ipv6 ?

I would like to be good net citizen and use a IPv6 for the bastion

is there a way to have this, so that I can associate the IPv6 to the dynamically created bastion instance, in order to then use this IPv6 to create a AAAA record bastion.example.com ?

(I've read that elastic ip is not supported for ipv6)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.