This folder shows an example of how to use the single-server module to launch a
single EC2 instance that is meant to serve as a bastion host. A bastion host is a security best practice where it is
the only server exposed to the public. You must connect to it (e.g. via SSH) before you can connect to any of your
other servers, which are in private subnets. This way, you minimize the surface area you expose to attackers, and can
focus all your efforts on locking down just a single server.
Quick start
To try these templates out you must have Terraform installed (minimum version: 0.6.11):
Open variables.tf, set the environment variables specified at the top of the file, and fill in any other variables that
don't have a default.
Run terraform get.
Run terraform plan.
If the plan looks good, run terraform apply.
Why a Bastion Host?
Your team will need the ability to SSH directly to EC2 Instances. Should we then make these EC2 Instances publicly
accessible to the Internet?
No, because then we have multiple servers that represent potential attack vectors into your app. Instead, the best
practice is to have a single server that's exposed to the public -- a "bastion host" -- on which we can focus all our
efforts for locking down. It is a "bastion" or fortress on which we focus all our security resources versus diluting
our efforts across multiple servers.
As a result, we place the bastion host in the public subnet, and all other servers should be located in private subnets.
Once connected to the bastion host, you can then SSH to any other EC2 Instance.
Bastion AMI
The bastion host can run any reasonably secure Linux distro. In this example, the default ami value is set to an
Ubuntu AMI.
It's possible to undertake more aggressive measures for securing the Bastion Host. For example, you could implement
port knocking,
however there are downsides to that approach.
Or we could install Intrustion Detection Software, or prevent most SSH users from seeing
common directories like /etc/. But each of these has downsides as well because with most features we introduce, we
also add management overhead.
Therefore, we keep our default Bastion Host as simple as possible.
SSH access
Once you are logged into the Bastion Host, you are effectively "in the network" and you can then SSH from the Bastion
Host to any other EC2 instance in the account, including those in the private subnets of the VPC.
Since it takes two "hops" to SSH to your servers, the Bastion Host is often called an "SSH Jump Host" or "Jump Box." The
tricky part with jump hosts is that, while you have your SSH keys on your local computer, the bastion host does not. To
avoid copying keys around (which is a huge security risk!), the best way to work with a jump host is to use
SSH Agent.
ssh-agent runs on your local machine and is responsible for keeping all your private SSH keys in memory. When you SSH
to another instance, if ssh-agent is running and you don't explicitly specify a local key file to use for
authentication, your ssh-agent will automatically try each of your keys one by one to log into the instance. Don't
worry, your private keys are never transmitted; only used to participate in the SSH authentication process.
A companion tool to ssh-agent is ssh-add. You can use ssh-add to add some or all of your local SSH Keys to local
memory (note: if you add too many keys, you may get an error like "ran out of authentication methods to try" before it
finds your key). Then, when you connect to the jump host with the -A option, your SSH keys will also be available on
the jump host.
For example, let's say bastion-v1.pem was your Key Pair (AWS's term for SSH key) for the bastion host and
stg-ec2-instances-v1.pem was your Key Pair for the EC2 instances in the Stage VPC. Here is how you could use SSH
Agent to connect to an ec2 instance in the Stage VPC:
chmod 400 'bastion-v1.pem' # You only need to do this once after downloading the Key Pair from AWS
chmod 400 'stg-ec2-instances-v1.pem' # You only need to do this once after downloading the Key Pair from AWS
ssh-add 'stg-ec2-instances-v1.pem'
ssh -A -i 'bastion-v1.pem' ubuntu@<BASTION-IP-ADDRESS>
# Now you're on the bastion host
ssh ec2-user@<EC2-INSTANCE-PRIVATE-IP-ADDRESS>
# Now you're on the EC2 instance
Note: If you're using ssh-iam from the
Security Package, then you'll be able to SSH using your own
public key (instead of a Key Pair file) and your IAM username (instead of a shared account like ec2-user or ubuntu).
This simplifies the above commands to:
ssh -A <YOUR-USERNAME>@<BASTION-IP-ADDRESS>
ssh <YOUR-USERNAME>@<EC2-INSTANCE-PRIVATE-IP-ADDRESS>
Known errors
When you run terraform apply on these templates the first time, you may see the following error:
* aws_iam_instance_profile.bastion: diffs didn't match during apply. This is a bug with Terraform and should be reported as a GitHub Issue.
As the error implies, this is a Terraform bug, but fortunately, it's a harmless one related to the fact that AWS is
eventually consistent, and Terraform occasionally tries to use a recently-created resource that isn't yet available.
Just re-run terraform apply and the error should go away.
Using Port Forwarding with Your Bastion Host
Local Port Forwarding
When you connect via SSH to your Bastion Host, you may opt to expose certain ports using SSH Local Port
Forwarding. For example, you could SSH to the Bastion Host with
ssh -L 8500:vault-instance.com:8500 mylogin@bastion-host.com, which will open a listener at localhost:8500 which is
routed to the Bastion Host where it is then further routed to vault-instance.com:8500from the Bastion Host. As a
result, just by connecting to http://localhost:8500 from your local machine, you could view, for example, the Consul UI
running on port 8500 on the private Vault instance.
SOCKS Proxy
What if you want to route all your web browsing through the Bastion Host? This is useful to appear to websites like you're
coming from a different IP address or different country. It's also useful for ensuring all your web traffic is encrypted
since all requests, even to non-HTTPS sites, are tunneled through the Bastion Host.
To setup the Bastion Host as a SOCKS Proxy, simply run ssh -D 5000 mylogin@bastion-host.com. You now have a SOCKS Proxy
running at localhost:5000. Now any app which is written to leverage a SOCKS Proxy can route all its requests through the
SOCKS Proxy instead of your local network connection.
For example, Firefox supports a SOCKS Proxy. See one
example of how
to configure it.
Questions? Ask away.
We're here to talk about our services, answer any questions, give advice, or just to chat.
{"treedata":{"name":"root","toggled":true,"children":[{"name":".circleci","children":[{"name":"config.yml","path":".circleci/config.yml","sha":"29cbc9b5b1f31782b07e88689458566b02a1e7ac"},{"name":"post-upgrade-test-results.sh","path":".circleci/post-upgrade-test-results.sh","sha":"a4867e8fbdc334b7a90259568ee41ea577fbe764"},{"name":"set-upgrade-test-vars.sh","path":".circleci/set-upgrade-test-vars.sh","sha":"9fa789c75021b87a65e658ac4ffc655511d42412"}]},{"name":".github","children":[{"name":"ISSUE_TEMPLATE","children":[{"name":"bug_report.md","path":".github/ISSUE_TEMPLATE/bug_report.md","sha":"d2e87e27c601e423865ed660ec697082470ca60f"},{"name":"feature_request.md","path":".github/ISSUE_TEMPLATE/feature_request.md","sha":"023a33099be2336476930c96e17ff1ba5dc55348"}]},{"name":"pull_request_template.md","path":".github/pull_request_template.md","sha":"6b100e40e323b5b07f40ed30616277c51c9f4b9e"}]},{"name":".gitignore","path":".gitignore","sha":"1b77db107bd9abb565bd5adafce570dd59adf016"},{"name":".pre-commit-config.yaml","path":".pre-commit-config.yaml","sha":"6c5b735f0db5a0b8d732e9fc612255e3f181d7d5"},{"name":"CODEOWNERS","path":"CODEOWNERS","sha":"f57844eeef3b0fe197ef48e443834a06baf91d28"},{"name":"LICENSE.txt","path":"LICENSE.txt","sha":"f4e3d9bd4717a044ed31ad847a300eee74371a78"},{"name":"README.adoc","path":"README.adoc","sha":"65a8d37206bb86a091fdbb58b59492e2e45e3805"},{"name":"_docs","children":[{"name":"aws-ec2.png","path":"_docs/aws-ec2.png","sha":"861e17a7d1df585b37d10e3211f71f6a6e182115"},{"name":"single-ec2-instance-architecture.png","path":"_docs/single-ec2-instance-architecture.png","sha":"c44f7efdb936c53fc4988f286ddcf31be8a6fdc9"}]},{"name":"core-concepts.md","path":"core-concepts.md","sha":"2e184e97784600b3c64345559cec828baebf71f9"},{"name":"examples","children":[{"name":"attach-eni","children":[{"name":"README.md","path":"examples/attach-eni/README.md","sha":"8af05ff614cd9330635dcfb2e4364e8ddcdc1418"},{"name":"main.tf","path":"examples/attach-eni/main.tf","sha":"4fa734efe5515ae3cef244d6e0c1b901e8adfe3b"},{"name":"outputs.tf","path":"examples/attach-eni/outputs.tf","sha":"1cd6ad4b2aa0a56a16203589a2f19ee0ab2117de"},{"name":"packer","children":[{"name":"build.pkr.hcl","path":"examples/attach-eni/packer/build.pkr.hcl","sha":"f76fa391a6a5c1df8699e8d2f2f8f98d7cc871f2"}]},{"name":"user-data","children":[{"name":"user-data-1.sh","path":"examples/attach-eni/user-data/user-data-1.sh","sha":"270432275db472b6cd3a84b1d0d1e661914727e6"},{"name":"user-data-2.sh","path":"examples/attach-eni/user-data/user-data-2.sh","sha":"ec638362d287eba3912efe647d296ac8b0e9a177"}]},{"name":"variables.tf","path":"examples/attach-eni/variables.tf","sha":"729680fb75f8962c03f5530e4162e2df63345434"}]},{"name":"bastion-host","children":[{"name":"README.md","path":"examples/bastion-host/README.md","sha":"8d42b76ced130f2a046869af393203069a8c6ce4","toggled":true},{"name":"main.tf","path":"examples/bastion-host/main.tf","sha":"ac9060610f30d24e80b28136c6f18eb5b4d55279"},{"name":"outputs.tf","path":"examples/bastion-host/outputs.tf","sha":"5f0c2d739b9646c39e19bfd05cacb852c6080c0c"},{"name":"user-data.sh","path":"examples/bastion-host/user-data.sh","sha":"40484d6463cf88d4d5b174cbc3ca759548b9d788"},{"name":"variables.tf","path":"examples/bastion-host/variables.tf","sha":"4d505a6e737712acccbdc710a1059033009725e6"}],"toggled":true},{"name":"ec2-backup","children":[{"name":"README.md","path":"examples/ec2-backup/README.md","sha":"782667373dc4d7cf602fea0e05d6b78ed47d2c42"},{"name":"main.tf","path":"examples/ec2-backup/main.tf","sha":"71c9de93c49b7b26e89756c426b6383286eec2c8"},{"name":"outputs.tf","path":"examples/ec2-backup/outputs.tf","sha":"6023311f87f6757e60e5d47600095cbc6dce324a"},{"name":"variables.tf","path":"examples/ec2-backup/variables.tf","sha":"e6e9fa9520608ef0a5cc0aadc70d118775543b03"}]},{"name":"ec2-instance-on-dedicated-host","children":[{"name":"README.md","path":"examples/ec2-instance-on-dedicated-host/README.md","sha":"b806952a5dd8f8ce81290e088c2971d2bffea814"},{"name":"main.tf","path":"examples/ec2-instance-on-dedicated-host/main.tf","sha":"ab28789d1f464895b72722c2ac92181f18e40a1c"},{"name":"variables.tf","path":"examples/ec2-instance-on-dedicated-host/variables.tf","sha":"94f2883ec78b1d52f350b59c17f63fc2d7096a60"}]},{"name":"ec2-instance-with-custom-iam-role","children":[{"name":"README.md","path":"examples/ec2-instance-with-custom-iam-role/README.md","sha":"c55193f734cec595162ecd6b80fc79563bb06ab1"},{"name":"main.tf","path":"examples/ec2-instance-with-custom-iam-role/main.tf","sha":"e31f653c0886028ea8515506d6f7d1bf20b13472"},{"name":"variables.tf","path":"examples/ec2-instance-with-custom-iam-role/variables.tf","sha":"729d4d107700c91ce9bf3957dbf4781b7b477f0a"}]},{"name":"override-default-eni","children":[{"name":"README.md","path":"examples/override-default-eni/README.md","sha":"1793e4fc9adf7d9fd7e8fd850f725333590fd761"},{"name":"main.tf","path":"examples/override-default-eni/main.tf","sha":"18f1a2ed568bce3d85f86d00791daca755b42eed"},{"name":"outputs.tf","path":"examples/override-default-eni/outputs.tf","sha":"58332f65c35948f297b649e14ae2d398c084837f"},{"name":"variables.tf","path":"examples/override-default-eni/variables.tf","sha":"d806a01b3f9919283060378a17026db6a5425d81"}]},{"name":"persistent-ebs-volume","children":[{"name":"README.md","path":"examples/persistent-ebs-volume/README.md","sha":"86e01a93a843b5498157d96b44f34eba2ec91b0d"},{"name":"main.tf","path":"examples/persistent-ebs-volume/main.tf","sha":"3ff0402f0430e58cd96225e71b465866413340b1"},{"name":"outputs.tf","path":"examples/persistent-ebs-volume/outputs.tf","sha":"54f33c96b796373b12b9702f46d30a1c85043f65"},{"name":"packer","children":[{"name":"build.pkr.hcl","path":"examples/persistent-ebs-volume/packer/build.pkr.hcl","sha":"321e8c08e1a045e60fea31b2fb51cfa950a7a96f"}]},{"name":"user-data","children":[{"name":"user-data.sh","path":"examples/persistent-ebs-volume/user-data/user-data.sh","sha":"9c5d08f440596a978418d09aea275d8c0ffc16fb"}]},{"name":"variables.tf","path":"examples/persistent-ebs-volume/variables.tf","sha":"96cc0689e82ce10128c70f1dafad5c34cc173bc2"}]},{"name":"route53-helpers","children":[{"name":"README.md","path":"examples/route53-helpers/README.md","sha":"0aee10984ea4e5426d1e000ebaa235eb8e7ca15b"},{"name":"main.tf","path":"examples/route53-helpers/main.tf","sha":"c224666fc92e8a656aa70235421513858662fa2e"},{"name":"outputs.tf","path":"examples/route53-helpers/outputs.tf","sha":"4c3c3ccccf964155ffbce7546fcbc9ef6e886de6"},{"name":"packer","children":[{"name":"build.pkr.hcl","path":"examples/route53-helpers/packer/build.pkr.hcl","sha":"4b0e2203595650f334c95a979ff8ae330d1fbb15"}]},{"name":"user-data","children":[{"name":"user-data.sh","path":"examples/route53-helpers/user-data/user-data.sh","sha":"fcd5070b46e6428d81dbb1e5ba5f4bef3b5b8c0c"}]},{"name":"variables.tf","path":"examples/route53-helpers/variables.tf","sha":"2df92a355955f27e81e1aed8a1306c0bbb1a49f1"}]},{"name":"simple-server","children":[{"name":"README.md","path":"examples/simple-server/README.md","sha":"de31cd49735376d18f52a4a4a9a4588c9d13afac"},{"name":"main.tf","path":"examples/simple-server/main.tf","sha":"61d7624e42023cd7f44a8ea24f8ccca3950c2be0"},{"name":"outputs.tf","path":"examples/simple-server/outputs.tf","sha":"54636d69ec91b8f2a787ba6c6963ec7285bda735"},{"name":"user-data.sh","path":"examples/simple-server/user-data.sh","sha":"0e411aae1513610ca785acb3b5afde93e639d9fc"},{"name":"variables.tf","path":"examples/simple-server/variables.tf","sha":"0d36d5040aeebf8602fff9629ba5f05ec10888a4"}]}],"toggled":true},{"name":"modules","children":[{"name":"attach-eni","children":[{"name":"README.md","path":"modules/attach-eni/README.md","sha":"d300c61ab08d811abd5647a4bd95689be67c66ca"},{"name":"bin","children":[{"name":"attach-eni","path":"modules/attach-eni/bin/attach-eni","sha":"dd7412195755b86e7392e0ea4f41b9af6dd2e95d"}]},{"name":"install.sh","path":"modules/attach-eni/install.sh","sha":"542cbd93ca08fd9cbe6f121f4d8fea2b1d76ca8b"}]},{"name":"disable-instance-metadata","children":[{"name":"README.md","path":"modules/disable-instance-metadata/README.md","sha":"eacec50007d78aec20335d85b05a9582200c9a66"},{"name":"bin","children":[{"name":"disable-instance-metadata","path":"modules/disable-instance-metadata/bin/disable-instance-metadata","sha":"a2b5e85b40225bc693a46978dd2ca2ea9406283f"}]},{"name":"install.sh","path":"modules/disable-instance-metadata/install.sh","sha":"bdce5d29adc4041375ffc9bf7eabb93a0059583c"}]},{"name":"ec2-backup","children":[{"name":"README.md","path":"modules/ec2-backup/README.md","sha":"e5f22ab6bdd9f34dacf71428bc04fef1d5415bad"},{"name":"main.tf","path":"modules/ec2-backup/main.tf","sha":"61d55c083546643fcd25188a316e27778ff71389"},{"name":"outputs.tf","path":"modules/ec2-backup/outputs.tf","sha":"19b7bd464c3c0916e03bff6f4f2cf69b671041ed"},{"name":"variables.tf","path":"modules/ec2-backup/variables.tf","sha":"82e2457bc5a01ab1f8bbe52a00902d0993124f75"}]},{"name":"persistent-ebs-volume","children":[{"name":"README.md","path":"modules/persistent-ebs-volume/README.md","sha":"7c9ea421cab052038900eb0f406c971c44ff335a"},{"name":"bin","children":[{"name":"mount-ebs-volume","path":"modules/persistent-ebs-volume/bin/mount-ebs-volume","sha":"faee5e21fc59560f76dddc1e6a3bc694e9214e8b"},{"name":"unmount-ebs-volume","path":"modules/persistent-ebs-volume/bin/unmount-ebs-volume","sha":"cd0103907161ebab9ef5d95cc4f2ad73dcf3671c"}]},{"name":"install.sh","path":"modules/persistent-ebs-volume/install.sh","sha":"e0ce5862cd6975992dc011a6dfe94fdf14a9b607"}]},{"name":"require-instance-metadata-service-version","children":[{"name":"README.md","path":"modules/require-instance-metadata-service-version/README.md","sha":"e44bae312f899c7b88cb7745e0732cf71f28fe8e"},{"name":"bin","children":[{"name":"require-instance-metadata-service-version","path":"modules/require-instance-metadata-service-version/bin/require-instance-metadata-service-version","sha":"49481dbbd668025409ae6e9fa7903276f3145948"}]},{"name":"install.sh","path":"modules/require-instance-metadata-service-version/install.sh","sha":"b819422b6cd7475666945112202fb71adbc6bb60"}]},{"name":"route53-helpers","children":[{"name":"README.md","path":"modules/route53-helpers/README.md","sha":"52d0b942bba658065589567e9c9d78c98b4ca028"},{"name":"bin","children":[{"name":"add-dns-a-record","path":"modules/route53-helpers/bin/add-dns-a-record","sha":"2fd418252934a48c78f910a739b5d681d1585941"}]},{"name":"install.sh","path":"modules/route53-helpers/install.sh","sha":"535ed35d64611c5f12e9924b3cb8df0a77180ab8"}]},{"name":"single-server","children":[{"name":"README.md","path":"modules/single-server/README.md","sha":"b3c4c20bdacd23061c068e159db70190b8c11f76"},{"name":"main.tf","path":"modules/single-server/main.tf","sha":"3b8da34b20390222572950b13bd7c6e9a6d41889"},{"name":"outputs.tf","path":"modules/single-server/outputs.tf","sha":"0b43a44ea00862a8cf386da69e3645c57f3f24fa"},{"name":"variables.tf","path":"modules/single-server/variables.tf","sha":"132c36be976d6d2294dd16071b41bea424cb9d26"}]}]},{"name":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","path":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","sha":"ae586c0fe830819580e1009d41a9074f16e65bed"},{"name":"test","children":[{"name":"README.md","path":"test/README.md","sha":"535b2717ac8c2d1bf416a66188df08106bdd1f57"},{"name":"bastion","children":[{"name":"bastion_host_test.go","path":"test/bastion/bastion_host_test.go","sha":"6bd2b91ea8c445712cc501e39197fcbfe64670a0"}]},{"name":"ec2","children":[{"name":"ec2_backup_test.go","path":"test/ec2/ec2_backup_test.go","sha":"f4864f133d2bb2b7ca12c97d6610f5e6f8aaed60"},{"name":"ec2_custom_iam_role_test.go","path":"test/ec2/ec2_custom_iam_role_test.go","sha":"02f12141dba641d4b899ed66ebac7045084b2715"},{"name":"ec2_dedicated_host_test.go","path":"test/ec2/ec2_dedicated_host_test.go","sha":"1ad1abe5c6ad6531d9cac0b3621ee67b22f6ce9d"}]},{"name":"eni","children":[{"name":"attach_eni_test.go","path":"test/eni/attach_eni_test.go","sha":"5162041143e42e52dcf961865ad9d55989cc77b5"},{"name":"override_default_eni_test.go","path":"test/eni/override_default_eni_test.go","sha":"fdf975db49cdf7ec0b34f9b0aa909a509077ed83"}]},{"name":"go.mod","path":"test/go.mod","sha":"9822097eec68b89eaf7c980927da190e377983d7"},{"name":"go.sum","path":"test/go.sum","sha":"2fbf20bc3e7f555bcdefa9794c4a599e0e1b3823"},{"name":"persistent_ebs","children":[{"name":"persistent_ebs_volume_test.go","path":"test/persistent_ebs/persistent_ebs_volume_test.go","sha":"b96186ebb9ba418662e66d799e714486c513a3e1"}]},{"name":"route53","children":[{"name":"route53_helpers_test.go","path":"test/route53/route53_helpers_test.go","sha":"00c656316aa84876776cb43b79833c82dd5271e9"}]},{"name":"simple-server","children":[{"name":"simple_server_host_test.go","path":"test/simple-server/simple_server_host_test.go","sha":"3b2ac545c567de4fa2a9d95765aa5910cc6768b3"}]},{"name":"test_helpers.go","path":"test/test_helpers.go","sha":"e936e3804ae84f7af8b04b8e0c345903b148e4a4"},{"name":"upgrades","children":[{"name":"upgrade_test.go","path":"test/upgrades/upgrade_test.go","sha":"1282f60d628fa241bd0059a123bd081c847cd849"}]},{"name":"validation","children":[{"name":"validate_all_modules_and_examples_test.go","path":"test/validation/validate_all_modules_and_examples_test.go","sha":"33d73c385b64c4fc870033e99427e683c31dc45a"}]}]}]},"detailsContent":"<h1 class=\"preview__body--title\" id=\"bastion-host-examples\">Bastion Host Examples</h1><div class=\"preview__body--border\"></div><p>This folder shows an example of how to use the <a href=\"/repos/v0.15.6/module-server/modules/single-server\" class=\"preview__body--description--blue\">single-server module</a> to launch a\nsingle EC2 instance that is meant to serve as a bastion host. A bastion host is a security best practice where it is\nthe <em>only</em> server exposed to the public. You must connect to it (e.g. via SSH) before you can connect to any of your\nother servers, which are in private subnets. This way, you minimize the surface area you expose to attackers, and can\nfocus all your efforts on locking down just a single server.</p>\n<h2 class=\"preview__body--subtitle\" id=\"quick-start\">Quick start</h2>\n<p>To try these templates out you must have Terraform installed (minimum version: <code>0.6.11</code>):</p>\n<ol>\n<li>Open <code>variables.tf</code>, set the environment variables specified at the top of the file, and fill in any other variables that\ndon't have a default.</li>\n<li>Run <code>terraform get</code>.</li>\n<li>Run <code>terraform plan</code>.</li>\n<li>If the plan looks good, run <code>terraform apply</code>.</li>\n</ol>\n<h2 class=\"preview__body--subtitle\" id=\"why-a-bastion-host\">Why a Bastion Host?</h2>\n<p>Your team will need the ability to SSH directly to EC2 Instances. Should we then make these EC2 Instances publicly\naccessible to the Internet?</p>\n<p>No, because then we have multiple servers that represent potential attack vectors into your app. Instead, the best\npractice is to have a single server that's exposed to the public -- a "bastion host" -- on which we can focus all our\nefforts for locking down. It is a "bastion" or fortress on which we focus all our security resources versus diluting\nour efforts across multiple servers.</p>\n<p>As a result, we place the bastion host in the public subnet, and all other servers should be located in private subnets.\nOnce connected to the bastion host, you can then SSH to any other EC2 Instance.</p>\n<h2 class=\"preview__body--subtitle\" id=\"bastion-ami\">Bastion AMI</h2>\n<p>The bastion host can run any reasonably secure Linux distro. In this example, the default <code>ami</code> value is set to an\nUbuntu AMI.</p>\n<p>It's possible to undertake more aggressive measures for securing the Bastion Host. For example, you could implement\n<a href=\"https://www.digitalocean.com/community/tutorials/how-to-use-port-knocking-to-hide-your-ssh-daemon-from-attackers-on-ubuntu\" class=\"preview__body--description--blue\" target=\"_blank\">port knocking</a>,\nhowever there are <a href=\"http://bsdly.blogspot.com/2012/04/why-not-use-port-knocking.html\" class=\"preview__body--description--blue\" target=\"_blank\">downsides</a> to that approach.</p>\n<p>Or we could install <a href=\"https://www.snort.org/\" class=\"preview__body--description--blue\" target=\"_blank\">Intrustion Detection Software</a>, or prevent most SSH users from seeing\ncommon directories like <code>/etc/</code>. But each of these has downsides as well because with most features we introduce, we\nalso add management overhead.</p>\n<p>Therefore, we keep our default Bastion Host as simple as possible.</p>\n<h2 class=\"preview__body--subtitle\" id=\"ssh-access\">SSH access</h2>\n<p>Once you are logged into the Bastion Host, you are effectively "in the network" and you can then SSH from the Bastion\nHost to any other EC2 instance in the account, including those in the private subnets of the VPC.</p>\n<p>Since it takes two "hops" to SSH to your servers, the Bastion Host is often called an "SSH Jump Host" or "Jump Box." The\ntricky part with jump hosts is that, while you have your SSH keys on your local computer, the bastion host does not. To\navoid copying keys around (which is a huge security risk!), the best way to work with a jump host is to use\n<strong>SSH Agent</strong>.</p>\n<p><code>ssh-agent</code> runs on your local machine and is responsible for keeping all your private SSH keys in memory. When you SSH\nto another instance, if <code>ssh-agent</code> is running and you don't explicitly specify a local key file to use for\nauthentication, your <code>ssh-agent</code> will automatically try each of your keys one by one to log into the instance. Don't\nworry, your private keys are never transmitted; only used to participate in the SSH authentication process.</p>\n<p>A companion tool to <code>ssh-agent</code> is <code>ssh-add</code>. You can use <code>ssh-add</code> to add some or all of your local SSH Keys to local\nmemory (note: if you add too many keys, you may get an error like "ran out of authentication methods to try" before it\nfinds your key). Then, when you connect to the jump host with the <code>-A</code> option, your SSH keys will also be available on\nthe jump host.</p>\n<p>For example, let's say <code>bastion-v1.pem</code> was your Key Pair (AWS's term for SSH key) for the bastion host and\n<code>stg-ec2-instances-v1.pem</code> was your Key Pair for the EC2 instances in the Stage VPC. Here is how you could use SSH\nAgent to connect to an ec2 instance in the Stage VPC:</p>\n<pre>chmod <span class=\"hljs-number\">400</span> 'bastion-v1.pem' <span class=\"hljs-comment\"># You only need to do this once after downloading the Key Pair from AWS</span>\nchmod <span class=\"hljs-number\">400</span> 'stg-ec2-instances-v1.pem' <span class=\"hljs-comment\"># You only need to do this once after downloading the Key Pair from AWS</span>\nssh-add 'stg-ec2-instances-v1.pem'\nssh -A -i 'bastion-v1.pem' ubuntu@<BASTION-IP-ADDRESS>\n\n<span class=\"hljs-comment\"># Now you're on the bastion host</span>\nssh ec2-user@<EC2-INSTANCE-PRIVATE-IP-ADDRESS>\n\n<span class=\"hljs-comment\"># Now you're on the EC2 instance</span>\n</pre>\n<p>Note: If you're using <a href=\"/repos/terraform-aws-security/modules/ssh-iam\" class=\"preview__body--description--blue\">ssh-iam</a> from the\n<a href=\"/repos/terraform-aws-security\" class=\"preview__body--description--blue\">Security Package</a>, then you'll be able to SSH using your own\npublic key (instead of a Key Pair file) and your IAM username (instead of a shared account like ec2-user or ubuntu).\nThis simplifies the above commands to:</p>\n<pre>ssh -A <span class=\"hljs-tag\"><<span class=\"hljs-name\">YOUR-USERNAME</span>></span>@<span class=\"hljs-tag\"><<span class=\"hljs-name\">BASTION-IP-ADDRESS</span>></span>\nssh <span class=\"hljs-tag\"><<span class=\"hljs-name\">YOUR-USERNAME</span>></span>@<span class=\"hljs-tag\"><<span class=\"hljs-name\">EC2-INSTANCE-PRIVATE-IP-ADDRESS</span>></span>\n</pre>\n<h2 class=\"preview__body--subtitle\" id=\"known-errors\">Known errors</h2>\n<p>When you run <code>terraform apply</code> on these templates the first time, you may see the following error:</p>\n<pre>* aws_iam_instance_profile.<span class=\"hljs-keyword\">bastion: </span><span class=\"hljs-keyword\">diffs </span><span class=\"hljs-keyword\">didn't </span>match during apply. This is a <span class=\"hljs-keyword\">bug </span>with Terraform <span class=\"hljs-keyword\">and </span><span class=\"hljs-keyword\">should </span><span class=\"hljs-keyword\">be </span>reported as a GitHub Issue.\n</pre>\n<p>As the error implies, this is a Terraform bug, but fortunately, it's a harmless one related to the fact that AWS is\neventually consistent, and Terraform occasionally tries to use a recently-created resource that isn't yet available.\nJust re-run <code>terraform apply</code> and the error should go away.</p>\n<h2 class=\"preview__body--subtitle\" id=\"using-port-forwarding-with-your-bastion-host\">Using Port Forwarding with Your Bastion Host</h2>\n<h4 id=\"local-port-forwarding\">Local Port Forwarding</h4>\n<p>When you connect via SSH to your Bastion Host, you may opt to expose certain ports using <a href=\"http://unix.stackexchange.com/a/115906/129208\" class=\"preview__body--description--blue\" target=\"_blank\">SSH Local Port\nForwarding</a>. For example, you could SSH to the Bastion Host with\n<code>ssh -L 8500:vault-instance.com:8500 mylogin@bastion-host.com</code>, which will open a listener at <code>localhost:8500</code> which is\nrouted to the Bastion Host where it is then further routed to <code>vault-instance.com:8500</code> <em>from the Bastion Host</em>. As a\nresult, just by connecting to http://localhost:8500 from your local machine, you could view, for example, the Consul UI\nrunning on port 8500 on the private Vault instance.</p>\n<h4 id=\"socks-proxy\">SOCKS Proxy</h4>\n<p>What if you want to route all your web browsing through the Bastion Host? This is useful to appear to websites like you're\ncoming from a different IP address or different country. It's also useful for ensuring all your web traffic is encrypted\nsince all requests, even to non-HTTPS sites, are tunneled through the Bastion Host.</p>\n<p>To setup the Bastion Host as a SOCKS Proxy, simply run <code>ssh -D 5000 mylogin@bastion-host.com</code>. You now have a SOCKS Proxy\nrunning at <code>localhost:5000</code>. Now any app which is written to leverage a SOCKS Proxy can route all its requests through the\nSOCKS Proxy instead of your local network connection.</p>\n<p>For example, Firefox supports a SOCKS Proxy. See <a href=\"http://lifehacker.com/237227/geek-to-live--encrypt-your-web-browsing-session-with-an-ssh-socks-proxy\" class=\"preview__body--description--blue\" target=\"_blank\">one\nexample</a> of how\nto configure it.</p>\n","repoName":"module-server","repoRef":"v0.15.15","serviceDescriptor":{"serviceName":"Single EC2 Instance","serviceRepoName":"module-server","serviceRepoOrg":"gruntwork-io","cloudProviders":["aws"],"description":"Run a single EC2 instance for stateless or stateful apps. Supports IAM roles, EBS volumes, ENIs, and EIPs.","imageUrl":"single-service.png","licenseType":"subscriber","technologies":["Terraform","Bash"],"compliance":[],"tags":[""]},"serviceCategoryName":"Server orchestration","fileName":"README.md","filePath":"/examples/bastion-host","title":"Repo Browser: Single EC2 Instance","description":"Browse the repos in the Gruntwork Infrastructure as Code Library."}